现在,InputReader线程已经开始运行,在InputReaderThread::threadLoop开始读取EventHub送上来的事件。mReader指向一个InputReader对象,调用InputReader::loopOnce函数。
/frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
先忽略其他细节,InputReader是在getEvents中捞取事件的。mEventBuffer是一个RawEvent结构体数组,mEventHub指向一个EventHub对象。
/frameworks/native/services/inputflinger/InputReader.cpp
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector<InputDeviceInfo> inputDevices;
{ // acquire lock
AutoMutex _l(mLock);
oldGeneration = mGeneration;
timeoutMillis = -1;
uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
timeoutMillis = 0;
refreshConfigurationLocked(changes);
} else if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
} // release lock
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
processEventsLocked(mEventBuffer, count);
}
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
mNextTimeout = LLONG_MAX;
timeoutExpiredLocked(now);
}
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
} // release lock
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener->flush();
}
EventHub在什么时候创建的呢?在NativeInputManager的构造函数中。看看EventHub的构造函数。首先创建了epoll文件描述符mEpollFd。 inotify是一个内核用于通知用户空间程序文件系统变化的机制。inotify_init用于创建一个inotify的fd,inotify_add_watch中DEVICE_PATH为“/dev/input”,inotify_add_watch作用是监控/dev/input目录下文件的变化。之后,将mEpollFd加入到epoll监控队列中,并将EPOLL_ID_INOTIFY保存到eventItem的union成员epoll_data中。然后,创建一个管道,将读端加入到epoll监控队列中,并将EPOLL_ID_WAKE保存到eventItem的union成员epoll_data中。
/frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
mINotifyFd = inotify_init();
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
DEVICE_PATH, errno);
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
int wakeFds[2];
result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
int major, minor;
getLinuxRelease(&major, &minor);
// EPOLLWAKEUP was introduced in kernel 3.5
mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}
回到EventHub::getEvents函数。readBuffer是内核input_event数组,event指向RawEvent数组下个可用的地址,capacity表示input_event数组的剩余容量,awoken表示是否通过写入管道唤醒阻塞的epoll_wait。随后进入一个死循环。
/frameworks/native/services/inputflinger/EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;
size_t capacity = bufferSize;
bool awoken = false;
...
这部分是重启设备处理流程。当Input的一些设置被改变时,mNeedToReopenDevices会变true,表示要重启设备。调用closeAllDevicesLocked关闭所有打开的设备,将mNeedToScanDevices设为true,表示下次进入getEvents函数时要扫描打开设备。之后,使用break退出主循环,等待loopOnce再一次调用getEvents函数进行扫描设备的操作。
/frameworks/native/services/inputflinger/EventHub.cpp
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// Reopen input devices if needed.
if (mNeedToReopenDevices) {
mNeedToReopenDevices = false;
ALOGI("Reopening all input devices due to a configuration change.");
closeAllDevicesLocked();
mNeedToScanDevices = true;
break; // return to the caller before we actually rescan
}
...
mDevices类型为KeyedVector< int32_t, Device*>,第一个模板参数为Device的id。第二个模板参数是对应的Device结构体指针,KeyedVector内部使用SortedVector实现,里面元素按key值的大小排列。closeAllDevicesLocked就是逐一对mDevices里的Device执行closeDeviceLocked函数进行关闭。
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::closeAllDevicesLocked() {
while (mDevices.size() > 0) {
closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
}
}
mBuiltInKeyboardId在EventHub构造函数中被初始化为NO_BUILT_IN_KEYBOARD(-2)。在打开设备时,若设备的类别为键盘,游戏手柄及虚拟键盘时,mBuiltInKeyboardId会被设为该设备的id。当要关闭的设备类型是上述类型时,将 mBuiltInKeyboardId重新置成NO_BUILT_IN_KEYBOARD。isVirtual函数判断Device的fd是否小于0,若大于或等于0,将这个fd从mEpollFd监控队列中移除。releaseControllerNumberLocked将Device的controllerNumber置0,并清除掉mControllerNumbers相应的标志位。然后将参数对应的Device从mDevices中移除,并将对应的fd关闭。
mOpeningDevices对应一个打开的Device组成的链表的头节点。从这个头节点开始遍历,找到要关闭的Device,将其从链表中删除。若找不到,将这个要关闭的Device插入到到mClosingDevices链表的头部,mClosingDevices仍指向这个链表的头部。mClosingDevices对应一个关闭的Device组成的链表的头节点。
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::closeDeviceLocked(Device* device) {
ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
device->path.string(), device->identifier.name.string(), device->id,
device->fd, device->classes);
if (device->id == mBuiltInKeyboardId) {
ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
device->path.string(), mBuiltInKeyboardId);
mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
}
if (!device->isVirtual()) {
if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) {
ALOGW("Could not remove device fd from epoll instance. errno=%d", errno);
}
}
releaseControllerNumberLocked(device);
mDevices.removeItem(device->id);
device->close();
// Unlink for opening devices list if it is present.
Device* pred = NULL;
bool found = false;
for (Device* entry = mOpeningDevices; entry != NULL; ) {
if (entry == device) {
found = true;
break;
}
pred = entry;
entry = entry->next;
}
if (found) {
// Unlink the device from the opening devices list then delete it.
// We don't need to tell the client that the device was closed because
// it does not even know it was opened in the first place.
ALOGI("Device %s was immediately closed after opening.", device->path.string());
if (pred) {
pred->next = device->next;
} else {
mOpeningDevices = device->next;
}
delete device;
} else {
// Link into closing devices list.
// The device will be deleted later after we have informed the client.
device->next = mClosingDevices;
mClosingDevices = device;
}
}
这里要提一下Device结构体。
/frameworks/native/services/inputflinger/EventHub.h
struct Device {
Device* next;//Device是以链表的形式组织的
int fd; // 文件描述符
const int32_t id;//唯一id
const String8 path;//设备路径
const InputDeviceIdentifier identifier;//厂商信息
uint32_t classes;//类别
//掩码数组
uint8_t keyBitmask[(KEY_MAX + 1) / 8];
uint8_t absBitmask[(ABS_MAX + 1) / 8];
uint8_t relBitmask[(REL_MAX + 1) / 8];
uint8_t swBitmask[(SW_MAX + 1) / 8];
uint8_t ledBitmask[(LED_MAX + 1) / 8];
uint8_t ffBitmask[(FF_MAX + 1) / 8];
uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
//配置信息
String8 configurationFile;
PropertyMap* configuration;
//键盘映射表
VirtualKeyMap* virtualKeyMap;
KeyMap keyMap;
sp overlayKeyMap;
sp combinedKeyMap;
//力反馈相关
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
int32_t controllerNumber;
int32_t timestampOverrideSec;
int32_t timestampOverrideUsec;
...
遍历mClosingDevices链表,每次得到一个Device,填充好一个RawEvent,标记为DEVICE_REMOVED类型,event指针加1表示使用掉了一个RawEvent,然后delete掉这个Device,将mNeedToSendFinishedDeviceScan标记为true,表示待会要发送扫描完成的事件。capacity减1,若等于0表示nput_event数组已无剩余空间,退出遍历。
/frameworks/native/services/inputflinger/EventHub.cpp
while (mClosingDevices) {
Device* device = mClosingDevices;
ALOGV("Reporting device closed: id=%d, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
event->type = DEVICE_REMOVED;
event += 1;
delete device;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
InputReader第一次调用getEvents时,上面步骤都是不执行的。mNeedToScanDevices在构造函数中被初始化为true,所以会从这里开始。
关键代码是调用scanDevicesLocked。最后也将mNeedToSendFinishedDeviceScan 设为true,待会会发送设备扫描完成事件。
/frameworks/native/services/inputflinger/EventHub.cpp
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
scanDirLocked扫描/dev/input下的文件,并以这些文件的完整路径名为入参调用openDeviceLocked函数。
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::scanDevicesLocked() {
status_t res = scanDirLocked(DEVICE_PATH);//DEVICE_PATH为"/dev/input"
if(res < 0) {
ALOGE("scan dir failed for %s\n", DEVICE_PATH);
}
if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
createVirtualKeyboardLocked();
}
}
openDeviceLocked函数实现比较繁琐。选择重点的来讲。第一部分,使用从fd获得的信息填充identifier的成员,包括name,bus,product,vendor,version,location和uniqueId。assignDescriptorLocked用来设置identifier的descriptor和nonce成员。
/frameworks/native/services/inputflinger/EventHub.cpp
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
ALOGV("Opening device: %s", devicePath);
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
if(fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
}
InputDeviceIdentifier identifier;
// Get device name.
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
//fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.name.setTo(buffer);
}
// Check to see if the device is on our excluded list
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const String8& item = mExcludedDevices.itemAt(i);
if (identifier.name == item) {
ALOGI("ignoring event id %s driver %s\n", devicePath, item.string());
close(fd);
return -1;
}
}
// Get device driver version.
int driverVersion;
if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
// Get device identifier.
struct input_id inputId;
if(ioctl(fd, EVIOCGID, &inputId)) {
ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
identifier.bus = inputId.bustype;
identifier.product = inputId.product;
identifier.vendor = inputId.vendor;
identifier.version = inputId.version;
// Get device physical location.
if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
//fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.location.setTo(buffer);
}
// Get device unique id.
if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
//fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.uniqueId.setTo(buffer);
}
// Fill in the descriptor.
assignDescriptorLocked(identifier);
// Make file descriptor non-blocking for use with poll().
if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
ALOGE("Error %d making device file descriptor non-blocking.", errno);
close(fd);
return -1;
}
...
}
看看assignDescriptorLocked函数。uniqueId用来描述identifier的唯一性,当uniqueId为空时,就使用nonce来确保唯一性。descriptor是用identifier各个成员的值按一定格式组合起来再使用SHA1加密得到的值,generateDescriptor函数的作用就是产生这个SHA1值赋给descriptor。如果uniqueId为空,检查mDevices中各个Device对应的descriptor是否等于当前得到的descriptor,若相等,将nonce加1,重新使用generateDescriptor生成新的descriptor,直到这个新的descriptor是唯一的。
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) {
// Compute a device descriptor that uniquely identifies the device.
// The descriptor is assumed to be a stable identifier. Its value should not
// change between reboots, reconnections, firmware updates or new releases
// of Android. In practice we sometimes get devices that cannot be uniquely
// identified. In this case we enforce uniqueness between connected devices.
// Ideally, we also want the descriptor to be short and relatively opaque.
identifier.nonce = 0;
String8 rawDescriptor = generateDescriptor(identifier);
if (identifier.uniqueId.isEmpty()) {
// If it didn't have a unique id check for conflicts and enforce
// uniqueness if necessary.
while(getDeviceByDescriptorLocked(identifier.descriptor) != NULL) {
identifier.nonce++;
rawDescriptor = generateDescriptor(identifier);
}
}
ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(),
identifier.descriptor.string());
}
/frameworks/native/services/inputflinger/EventHub.cpp
static String8 generateDescriptor(InputDeviceIdentifier& identifier) {
String8 rawDescriptor;
rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor,
identifier.product);
// TODO add handling for USB devices to not uniqueify kbs that show up twice
if (!identifier.uniqueId.isEmpty()) {
rawDescriptor.append("uniqueId:");
rawDescriptor.append(identifier.uniqueId);
} else if (identifier.nonce != 0) {
rawDescriptor.appendFormat("nonce:%04x", identifier.nonce);
}
if (identifier.vendor == 0 && identifier.product == 0) {
// If we don't know the vendor and product id, then the device is probably
// built-in so we need to rely on other information to uniquely identify
// the input device. Usually we try to avoid relying on the device name or
// location but for built-in input device, they are unlikely to ever change.
if (!identifier.name.isEmpty()) {
rawDescriptor.append("name:");
rawDescriptor.append(identifier.name);
} else if (!identifier.location.isEmpty()) {
rawDescriptor.append("location:");
rawDescriptor.append(identifier.location);
}
}
identifier.descriptor = sha1(rawDescriptor);
return rawDescriptor;
}
回到openDeviceLocked函数中。在EventHub构造函数中,mNextDeviceId初始化为1。这样,新创建的Device的id会从1起逐渐递增。之后是一大串的Log。
/frameworks/native/services/inputflinger/EventHub.cpp
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
ALOGV("add device %d: %s\n", deviceId, devicePath);
ALOGV(" bus: %04x\n"
" vendor %04x\n"
" product %04x\n"
" version %04x\n",
identifier.bus, identifier.vendor, identifier.product, identifier.version);
ALOGV(" name: \"%s\"\n", identifier.name.string());
ALOGV(" location: \"%s\"\n", identifier.location.string());
ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string());
ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string());
ALOGV(" driver: v%d.%d.%d\n",
driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
loadConfigurationLocked用于加载按键文件,详见我另一篇博客:《Android加载按键文件流程》。之后的流程统一按处理按键事件展开。
/frameworks/native/services/inputflinger/EventHub.cpp
...
// Load the configuration file for the device.
loadConfigurationLocked(device);
...
EV_KEY表示按键类型的事件。能够上报这类事件的设备有键盘,鼠标,手柄,手写板等一切拥有按钮的设备(包括手机上的实体按键)。在Device结构体中,对应的事件位掩码keyBitmask描述了可以产生的事件的集合。按键事件的全集包括字符按键,方向键,控制键,鼠标键,游戏按键等。
/frameworks/native/services/inputflinger/EventHub.cpp
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
...
// See if this is a keyboard. Ignore everything in the button range except for
// joystick and gamepad buttons which are handled like keyboards for the most part.
bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
sizeof_bit_array(KEY_MAX + 1));
bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
sizeof_bit_array(BTN_MOUSE))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
sizeof_bit_array(BTN_DIGI));
if (haveKeyboardKeys || haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
如果Device类型为keyboard或joystick,加载解析.kl和.kcm文件。类型为keyboard的情况还会把mBuiltInKeyboardId设置为该Device的id。根据传上来的按键集合,可以将keyboard类型分为几类:
INPUT_DEVICE_CLASS_ALPHAKEY表示字符按键设备,INPUT_DEVICE_CLASS_DPAD表示拥有方向键的设备,INPUT_DEVICE_CLASS_GAMEPAD表示游戏手柄设备。
/frameworks/native/services/inputflinger/EventHub.cpp
// Load the key map.
// We need to do this for joysticks too because the key layout may specify axes.
status_t keyMapStatus = NAME_NOT_FOUND;
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
// Load the keymap for the device.
keyMapStatus = loadKeyMapLocked(device);
}
// Configure the keyboard, gamepad or virtual keyboard.
if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
// Register the keyboard as a built-in keyboard if it is eligible.
if (!keyMapStatus
&& mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD
&& isEligibleBuiltInKeyboard(device->identifier,
device->configuration, &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
if (hasKeycodeLocked(device, AKEYCODE_Q)) {
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}
// See if this device has a DPAD.
if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
// See if this device has a gamepad.
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
break;
}
将Device对应的fd添加到epoll监控队列中,监听事件为EPOLLIN。在3.5版本以上的Linux中,增设监听事件EPOLLWAKEUP。
/frameworks/native/services/inputflinger/EventHub.cpp
// Register with epoll.
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
if (mUsingEpollWakeup) {
eventItem.events |= EPOLLWAKEUP;
}
eventItem.data.u32 = deviceId;
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
delete device;
return -1;
}
最后,往mDevices添加id-Device*键值对,往mOpeningDevices表示的链表头部插入该Device,mOpeningDevices继续指向该链表的头部。
/frameworks/native/services/inputflinger/EventHub.cpp
...
addDeviceLocked(device);
return 0;
}
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::addDeviceLocked(Device* device) {
mDevices.add(device->id, device);
device->next = mOpeningDevices;
mOpeningDevices = device;
}
值得注意的是,在scanDevicesLocked函数中,还将创建VirtualKeyboard,此处不再详述。至此,/dev/input/下的所有Device信息都已经被初始化,mOpeningDevices链表已经就绪。现在返回到getEvents函数中。
遍历mOpeningDevices链表,每个Device对应生成一个RawEvent。RawEvent的时间戳设为当前时间,deviceId设为mBuiltInKeyboardId或Device的id,事件类型记为DEVICE_ADDED。mNeedToSendFinishedDeviceScan设为true,表示要发送设备扫描完成事件,最后将capacity减1,因为产生了一个input_event事件,input_event数组剩余容量减1。
/frameworks/native/services/inputflinger/EventHub.cpp
...
while (mOpeningDevices != NULL) {
Device* device = mOpeningDevices;
ALOGV("Reporting device opened: id=%d, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
每次重启,卸载,开启设备时,都要将mNeedToSendFinishedDeviceScan设为true,这会对应一个RawEvent和input_event.
/frameworks/native/services/inputflinger/EventHub.cpp
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
event->when = now;
event->type = FINISHED_DEVICE_SCAN;
event += 1;
if (--capacity == 0) {
break;
}
}
InputReader在线程循环中不断调用loopOnce,也就意味着getEvents函数会被循环调用。先看看第一次调用的情况。前面提到,第一次进入getEvents函数,入口点在scanDevicesLocked函数以用来扫描打开的设备。在EventsHub的构造函数中,
mPendingEventIndex和mPendingEventCount都被初始化为0,所以第一次进入getEvents函数时并不会进入以下循环:
/frameworks/native/services/inputflinger/EventHub.cpp
...
while (mPendingEventIndex < mPendingEventCount) {
...
设备的添加导致产生RawEvent事件,使得event指针不等于RawEvents数组首地址。所以接下来会退出getEvents函数的主循环,直接返回产生的RawEvent数量。
/frameworks/native/services/inputflinger/EventHub.cpp
if (event != buffer || awoken) {
break;
}
...
return event - buffer;
第二次进入getEvents时,由于mNeedToScanDevices已被置为false,所以不会再去扫描设备。此时event的值与buffer相同,这次会进入到epoll_wait中。epoll监听了以下fd:Inotify的fd,第一次扫描的设备fd,唤醒管道读端的fd。epoll_wait在等待timeoutMillis的时间内,返回的pollResult为发生的目标事件数量,同时赋给mPendingEventCount,发生的事件保存在mPendingEventItems中。下一步,重新进入getEvent函数主循环。
/frameworks/native/services/inputflinger/EventHub.cpp
...
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
...
else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
}
...
mPendingEventCount已经为正,终于可以进入while (mPendingEventIndex < mPendingEventCount)循环了。
遍历mPendingEventItems中的epoll_event,如果是Inotify的EPOLLIN事件,将mPendingINotify设为true。之后contine这个循环。
/frameworks/native/services/inputflinger/EventHub.cpp
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
if (eventItem.events & EPOLLIN) {
mPendingINotify = true;
} else {
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue;
}
如果是管道读端的EPOLLIN事件,把awoken设置成true,读走管道读端的数据后,continue这个循环。
/frameworks/native/services/inputflinger/EventHub.cpp
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
char buffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
}
continue;
}
根据eventItem找到发生EPOLLIN事件的Device后,从该Device对应的fd读取数据到readBuffer中,readBuffer是一个input_event结构体数组。如果数取的数据为空或者出错返回ENODEV,说明对应的Device已经被移除,deviceChanged设为true,调用closeDeviceLocked处理该设备移除事件。如果正常返回,count得到在Device上发生的input_event数量。遍历readBuffer上的每个input_event,接下来用input_event的成员去初始化RawEvent,readBuffer剩余容量减1。若剩余容量为0,则退出当前循环。
/frameworks/native/services/inputflinger/EventHub.cpp
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
...
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// Device was removed before INotify noticed.
ALOGW("could not get event, removed? (fd: %d size: %" PRId32
" bufferSize: %zu capacity: %zu errno: %d)\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(device);
} else if (readSize < 0) {
if (errno != EAGAIN && errno != EINTR) {
ALOGW("could not get event (errno=%d)", errno);
}
} else if ((readSize % sizeof(struct input_event)) != 0) {
ALOGE("could not get event (wrong size: %d)", readSize);
} else {
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
device->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
// Some input devices may have a better concept of the time
// when an input event was actually generated than the kernel
// which simply timestamps all events on entry to evdev.
// This is a custom Android extension of the input protocol
// mainly intended for use with uinput based device drivers.
if (iev.type == EV_MSC) {
if (iev.code == MSC_ANDROID_TIME_SEC) {
device->timestampOverrideSec = iev.value;
continue;
} else if (iev.code == MSC_ANDROID_TIME_USEC) {
device->timestampOverrideUsec = iev.value;
continue;
}
}
...
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
mPendingEventIndex -= 1;
break;
}
}
} else if (eventItem.events & EPOLLHUP) {
ALOGI("Removing device %s due to epoll hang-up event.",
device->identifier.name.string());
deviceChanged = true;
closeDeviceLocked(device);
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.",
eventItem.events, device->identifier.name.string());
}
}
处理完mPendingEventItems的事件后,mPendingEventIndex的值与mPendingEventCount的值相等。如果之前处理的mPendingEventItem中的事件有发生在Inotify的(mPendingINotify为true),说明发生了设备增删事件,此时会调用readNotifyLocked处理设备增删事件,将deviceChanged设为true,mPendingINotify 设为false。
/frameworks/native/services/inputflinger/EventHub.cpp
// readNotify() will modify the list of devices so this must be done after
// processing all other events to ensure that we read all remaining events
// before closing the devices.
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
mPendingINotify = false;
readNotifyLocked();
deviceChanged = true;
}
EventHub::readNotifyLocked函数就是遍历所有的Inotify事件,根据inotify_event的mask值判断设备是新增还是卸载进行处理。如果是设备新增事件,调用openDeviceLocked进行处理。如果是设备卸载事件,调用closeDeviceByPathLocked进行处理,closeDeviceByPathLocked内部调用了closeDeviceLocked。
/frameworks/native/services/inputflinger/EventHub.cpp
status_t EventHub::readNotifyLocked() {
int res;
char devname[PATH_MAX];
char *filename;
char event_buf[512];
int event_size;
int event_pos = 0;
struct inotify_event *event;
ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
res = read(mINotifyFd, event_buf, sizeof(event_buf));
if(res < (int)sizeof(*event)) {
if(errno == EINTR)
return 0;
ALOGW("could not get event, %s\n", strerror(errno));
return -1;
}
//printf("got %d bytes of event information\n", res);
strcpy(devname, DEVICE_PATH);
filename = devname + strlen(devname);
*filename++ = '/';
while(res >= (int)sizeof(*event)) {
event = (struct inotify_event *)(event_buf + event_pos);
//printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
if(event->len) {
strcpy(filename, event->name);
if(event->mask & IN_CREATE) {
openDeviceLocked(devname);
} else {
ALOGI("Removing device '%s' due to inotify event\n", devname);
closeDeviceByPathLocked(devname);
}
}
event_size = sizeof(*event) + event->len;
res -= event_size;
event_pos += event_size;
}
return 0;
}
deviceChanged被设置为true的地方有三处:1.读取Device到readBuffer中的内容为空或者产生ENODEV的错误时;2.Device事件为EPOLLHUP(被挂起)时;3.发生Inotify事件,处理完mClosingDevices,mOpeningDevices,mDevices等数据结构的变化及Device的信息初始化操作后。
continue表示重新进入getEvents函数主循环,根据设备变化事件生成相应的RawEvent。
/frameworks/native/services/inputflinger/EventHub.cpp
...
if (deviceChanged) {
continue;
}
第二次进入getEvents函数,可能由于没有生成RawEvent,导致event的值和buffer的值相等,所以不会进入break阶段。生成RawEvent的地方有:1.进入getEvents函数时mNeedToReopenDevices为true,表示需要重启设备;2.第一次进入getEvents函数,需要扫描/dev/input/下面的设备;3.mNeedToSendFinishedDeviceScan为true,表示生成扫描完成事件,发生在重启设备,首次扫描设备,设备新增和卸载阶段。也就是说,第二次进入getEvents函数时,只要不发生上述事件,event的值和buffer的值就会相等。将mPendingEventIndex置0,是为了能使下次进入getEvents函数能进入while (mPendingEventIndex < mPendingEventCount)循环,之后流程会走到epoll_wait阶段。
epoll_wait监控了Inotify的fd,管道读端的fd和设备的fd。epoll_wait可能会发生阻塞,因为第四个参数值可能为-1,而设备那边真的风平浪静没有任何动作发生。这样,epoll_wait函数便不能返回。Android设置了EventHub::wake往管道写端写入一个‘w’以唤醒epoll_wait,使epoll_wait函数得以返回,以重新进入getEvents函数主循环,重新进入主循环后会在”if (event != buffer || awoken)”处返回。
发生break退出getEvents函数主循环的地方有以下几处:1.input_event结构体数组readBuffer被塞满;2.重启设备;3.生成了RawEvent事件;4.管道唤醒操作;5.epoll_wait返回值为0。
epoll_wait之所以设置在”if (event != buffer || awoken)”之后是因为生成了RawEvent就不用再监听了,直接返回。没有生成就继续监听,以期待进入主循环生成RawEvent。
getEvents函数最终返回生成的RawEvent数量。
/frameworks/native/services/inputflinger/EventHub.cpp
if (event != buffer || awoken) {
break;
}
...
mPendingEventIndex = 0;
mLock.unlock(); // release lock before poll, must be before release_wake_lock
release_wake_lock(WAKE_LOCK_ID);
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
if (pollResult == 0) {
// Timed out.
mPendingEventCount = 0;
break;
}
if (pollResult < 0) {
// An error occurred.
mPendingEventCount = 0;
// Sleep after errors to avoid locking up the system.
// Hopefully the error is transient.
if (errno != EINTR) {
ALOGW("poll failed (errno=%d)\n", errno);
usleep(100000);
}
} else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
}
}
// All done, return the number of events we read.
return event - buffer;
}
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::wake() {
ALOGV("wake() called");
ssize_t nWrite;
do {
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);
if (nWrite != 1 && errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
首次进入getEvents函数的流程是扫描/dev/input下的设备,并添加到epoll监控队列中。第二次进入getEvents函数时,启用epoll_wait进行监听,之后进入主循环。首次循环中,根据epoll_wait返回的结果,在循环中生成对应的RawEvent,若有RawEvent,getEvents函数会返回等待第三次进入。若没有RawEvent,会进入二次循环,此时epoll_wait可能又带来了新的事件让程序去生成RawEvent。。。(这里忽略了一些个别情况)。