从 InputManagerService: 创建与启动 可知,Input 系统的主要功能,主要集中在 native 层,并且Input 系统的 native 层又包含 InputReader, InputClassifer, InputDispatcher 三个子模块。本文来分析 InputReader 从创建到启动的基本流程,为后续分析 InputReader 的每一个功能打好基础。
InputReader 的创建
从 InputManagerService: 创建与启动 可知, InputReader 的创建过程如下
1 2 3 4 5 6 7 |
// InputReaderFactory.cpp sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) { return new InputReader(std::make_unique<EventHub>(), policy, listener); } |
InputReader 依赖 EventHub,因此首先要看下 EventHub 的创建过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
EventHub::EventHub(void) : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), mNeedToSendFinishedDeviceScan(false), mNeedToReopenDevices(false), mNeedToScanDevices(true), // mNeedToScanDevices 初始化为 true,表示需要扫描输入设备 mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { ensureProcessCanBlockSuspend(); // 1. 创建 epoll mEpollFd = epoll_create1(EPOLL_CLOEXEC); // 2. 初始化 inotify mINotifyFd = inotify_init(); // 监听 /dev/input/ 目录项的创建与删除,其实就是监听输入设备的创建与删除 mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); // ... // 3. epoll 监听 inotify 事件 // 可读事件,表明有输入设备的创建与删除 struct epoll_event eventItem = {}; eventItem.events = EPOLLIN | EPOLLWAKEUP; eventItem.data.fd = mINotifyFd; int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); // 4. 创建管道 int wakeFds[2]; result = pipe(wakeFds); mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1]; // 设置管道两端为非阻塞 result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); // 5. epoll 监听管道读端的事件 // 可读事件,表明需要唤醒 InputReader 线程,触发条件一般为配置更新 eventItem.data.fd = mWakeReadPipeFd; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); } |
EventHub 创建过程如下
- 创建 epoll 实例。
- 初始化 inotify 实例,并用 epoll 监听它的事件。当输入设备添加/删除时,epoll 就会收到 inotify 的可读事件,因此 EventHub 和 InputReader 就可以动态地处理输入设备的添加/删除。
- 创建管道。
- epoll 监听管道的读端的事件。当配置更新时,会向管道的写端写入数据,epoll 就会收到管道的可读事件,如果此时 InputReader 线程处于休眠状态,那么 InputReader 将被唤醒来处于配置更新。
epoll, inotify, pipe,它们的作用和使用方式,请读者自行查阅 Unix/Linux 资料。
现在让我们继续看下 InputReader 的创建过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) : mContext(this), // mContext 代表 InputReader 的环境 mEventHub(eventHub), mPolicy(policy), mGlobalMetaState(0), mLedMetaState(AMETA_NUM_LOCK_ON), mGeneration(1), mNextInputDeviceId(END_RESERVED_ID), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { // InputReader 会把加工后的事件添加到 QueuedInputListener 队列中,之后一起分发给 InputClassifier mQueuedListener = new QueuedInputListener(listener); { // acquire lock std::scoped_lock _l(mLock); // 刷新配置 // 其实就是更新 InputReader::mConfig refreshConfigurationLocked(0); // 更新 InputReader::mGlobalMetaState // 与键盘输入设备的meta按键相关 updateGlobalMetaStateLocked(); } // release lock } |
InputReader 的构造函数很简单,就是成员变量的初始化。其中需要重点看下 refreshConfigurationLocked(0) 是如何刷新 InputReader 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 注意,此时参数 changes 为 0 void InputReader::refreshConfigurationLocked(uint32_t changes) { // 通过 InputReaderPolicyInterface 获取配置,保存到 InputReader::mConfig 中 mPolicy->getReaderConfiguration(&mConfig); // EventHub 保存排除的设备 mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); if (!changes) return; // ... } |
原来 InputReader::mConfig 代表的就是 InputReader 的配置,并且是通过 InputReaderPolicyInterface mPolicy 获取配置的。
从 InputManagerService: 创建与启动 可知,InputReaderPolicyInterface 接口的实现者是 NativeInputManager ,而 NativeInputManager 是 Input 系统的上层与底层沟通的桥梁,因此 InputReader 必定是通过 NativeInputManager 向上层获取配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) { ATRACE_CALL(); JNIEnv* env = jniEnv(); // 1. 通过JNI,向上层 InputManagerService 获取配置,并保存到 outConfig 中 jint virtualKeyQuietTime = env->CallIntMethod(mServiceObj, gServiceClassInfo.getVirtualKeyQuietTimeMillis); if (!checkAndClearExceptionFromCallback(env, "getVirtualKeyQuietTimeMillis")) { outConfig->virtualKeyQuietTime = milliseconds_to_nanoseconds(virtualKeyQuietTime); } outConfig->excludedDeviceNames.clear(); jobjectArray excludedDeviceNames = jobjectArray(env->CallStaticObjectMethod( gServiceClassInfo.clazz, gServiceClassInfo.getExcludedDeviceNames)); if (!checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && excludedDeviceNames) { jsize length = env->GetArrayLength(excludedDeviceNames); for (jsize i = 0; i < length; i++) { std::string deviceName = getStringElementFromJavaArray(env, excludedDeviceNames, i); outConfig->excludedDeviceNames.push_back(deviceName); } env->DeleteLocalRef(excludedDeviceNames); } // Associations between input ports and display ports // The java method packs the information in the following manner: // Original data: [{'inputPort1': '1'}, {'inputPort2': '2'}] // Received data: ['inputPort1', '1', 'inputPort2', '2'] // So we unpack accordingly here. outConfig->portAssociations.clear(); jobjectArray portAssociations = jobjectArray(env->CallObjectMethod(mServiceObj, gServiceClassInfo.getInputPortAssociations)); if (!checkAndClearExceptionFromCallback(env, "getInputPortAssociations") && portAssociations) { jsize length = env->GetArrayLength(portAssociations); for (jsize i = 0; i < length / 2; i++) { std::string inputPort = getStringElementFromJavaArray(env, portAssociations, 2 * i); std::string displayPortStr = getStringElementFromJavaArray(env, portAssociations, 2 * i + 1); uint8_t displayPort; // Should already have been validated earlier, but do it here for safety. bool success = ParseUint(displayPortStr, &displayPort); if (!success) { ALOGE("Could not parse entry in port configuration file, received: %s", displayPortStr.c_str()); continue; } outConfig->portAssociations.insert({inputPort, displayPort}); } env->DeleteLocalRef(portAssociations); } outConfig->uniqueIdAssociations.clear(); jobjectArray uniqueIdAssociations = jobjectArray( env->CallObjectMethod(mServiceObj, gServiceClassInfo.getInputUniqueIdAssociations)); if (!checkAndClearExceptionFromCallback(env, "getInputUniqueIdAssociations") && uniqueIdAssociations) { jsize length = env->GetArrayLength(uniqueIdAssociations); for (jsize i = 0; i < length / 2; i++) { std::string inputDeviceUniqueId = getStringElementFromJavaArray(env, uniqueIdAssociations, 2 * i); std::string displayUniqueId = getStringElementFromJavaArray(env, uniqueIdAssociations, 2 * i + 1); outConfig->uniqueIdAssociations.insert({inputDeviceUniqueId, displayUniqueId}); } env->DeleteLocalRef(uniqueIdAssociations); } jint hoverTapTimeout = env->CallIntMethod(mServiceObj, gServiceClassInfo.getHoverTapTimeout); if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) { jint doubleTapTimeout = env->CallIntMethod(mServiceObj, gServiceClassInfo.getDoubleTapTimeout); if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) { jint longPressTimeout = env->CallIntMethod(mServiceObj, gServiceClassInfo.getLongPressTimeout); if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) { outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(hoverTapTimeout); // We must ensure that the tap-drag interval is significantly shorter than // the long-press timeout because the tap is held down for the entire duration // of the double-tap timeout. jint tapDragInterval = max(min(longPressTimeout - 100, doubleTapTimeout), hoverTapTimeout); outConfig->pointerGestureTapDragInterval = milliseconds_to_nanoseconds(tapDragInterval); } } } jint hoverTapSlop = env->CallIntMethod(mServiceObj, gServiceClassInfo.getHoverTapSlop); if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) { outConfig->pointerGestureTapSlop = hoverTapSlop; } // 2. 从 NativeInputManager::mLocked 更新配置,保存到 outConfig 中 // NativeInputManager::mLocked 的数据是上层经由 InputManagerService 传入的 { // acquire lock AutoMutex _l(mLock); outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed * POINTER_SPEED_EXPONENT); outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled; outConfig->showTouches = mLocked.showTouches; outConfig->pointerCapture = mLocked.pointerCapture; outConfig->setDisplayViewports(mLocked.viewports); outConfig->defaultPointerDisplayId = mLocked.pointerDisplayId; outConfig->disabledDevices = mLocked.disabledInputDevices; } // release lock } |
从整体看,获取 InputReader 配置的方式有两种
- 通过 JNI 向上层的 InputManagerService 获取配置。
- 从 NativeInputManager::mLocked 获取配置。
从 InputManagerService: 创建与启动 可知,NativeInputManager::mLocked 是在 NativeInputManager 的构造函数中进行初始化的,但是它并不是不变的,而是上层经由 InputManagerService 进行操控的。
例如,mLocked.showTouches 对应开发者模式下的 Show taps 功能,InputManagerService 会监听这个开关的状态,相应地改变 mLocked.showTouches,并且会通知 InputReader 配置改变了,InputReader 在处理配置改变的过程时,会重新获取 mLocked.showTouches 这个配置。
有 一部分 的配置是可以通过 adb shell dumpsys input 命令进行查看的
1 2 3 4 5 6 7 8 9 |
Input Manager State: Interactive: true System UI Lights Out: false Pointer Speed: 0 Pointer Gestures Enabled: true Show Touches: false Pointer Capture: Disabled, seq=0 |
而另外一部分配置,由于会对输入设备进行配置,因此可以从 dump 出的输入设备的信息中查看。
InputReader 的运行
从 InputManagerService: 创建与启动 可知,InputReader 通过线程,循环调用 InputReader::loopOnce() 执行任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; bool inputDevicesChanged = false; std::vector<InputDeviceInfo> inputDevices; { // acquire lock std::scoped_lock _l(mLock); oldGeneration = mGeneration; timeoutMillis = -1; // 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 // 2. 从 EventHub 获取事件 size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock std::scoped_lock _l(mLock); mReaderIsAliveCondition.notify_all(); // 3. 处理事件 if (count) { processEventsLocked(mEventBuffer, count); } if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (now >= mNextTimeout) { mNextTimeout = LLONG_MAX; timeoutExpiredLocked(now); } } // 4. 处理输入设备改变 // 4.1 输入设备改变,重新获取输入设备信息 if (oldGeneration != mGeneration) { inputDevicesChanged = true; inputDevices = getInputDevicesLocked(); } } // release lock // 4.2 通知监听者,输入设备改变了 if (inputDevicesChanged) { mPolicy->notifyInputDevicesChanged(inputDevices); } // 5. 刷新队列中缓存的事件 // 其实就是把事件分发给 InputClassifier mQueuedListener->flush(); } |
InputReader 所做的事情如下
- 如果配置改变了,那么就更新配置。
- 从 EventHub 获取事件,并处理获取到的事件。在处理事件的过程中,InputReader 会对事件进行加工,然后保存到 QueuedInputListener 缓存队列中。
- 如果设备发生改变,那么重新获取新的设备信息,并通知监听者。
- QueuedInputListener 刷新缓存的事件,其实就是把 InputReader 加工后的事件分发给 InputClassifer。
EventHub 提供事件
InputReader 的本质就是处理从 EventHub 获取的事件,然后分发给下一环。因为我们必须了解 EventHub::getEvents() 是如何为 InputReader 提供事件的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
// EventHub.cpp size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { ALOG_ASSERT(bufferSize >= 1); std::scoped_lock _l(mLock); struct input_event readBuffer[bufferSize]; RawEvent* event = buffer; size_t capacity = bufferSize; bool awoken = false; for (;;) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); // Reopen input devices if needed. if (mNeedToReopenDevices) { // ... } // Report any devices that had last been added/removed. for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) { // ... } // 扫描输入设备 if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked(); mNeedToSendFinishedDeviceScan = true; } // 为扫描后打开的每一个输入设备,填充一个类型为 DEVICE_ADDED 的事件 while (!mOpeningDevices.empty()) { std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin()); mOpeningDevices.pop_back(); event->when = now; event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; event->type = DEVICE_ADDED; event += 1; // Try to find a matching video device by comparing device names for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end(); it++) { // ... } // 每次填充完事件,就把设备 Device 保存到 mDevices 中 auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device)); if (!inserted) { ALOGW("Device id %d exists, replaced.", device->id); } // 表明你需要发送设备扫描完成事件 mNeedToSendFinishedDeviceScan = true; if (--capacity == 0) { break; } } // 填充设备扫描完成事件 if (mNeedToSendFinishedDeviceScan) { mNeedToSendFinishedDeviceScan = false; event->when = now; event->type = FINISHED_DEVICE_SCAN; event += 1; if (--capacity == 0) { break; } } // Grab the next input event. bool deviceChanged = false; // 处理 epoll 事件 while (mPendingEventIndex < mPendingEventCount) { // 处理 inotify 事件,表明输入设备新增或者删除 const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; if (eventItem.data.fd == mINotifyFd) { if (eventItem.events & EPOLLIN) { mPendingINotify = true; } else { ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); } continue; } // 处理管道事件,这是用来唤醒 InputReader 线程 if (eventItem.data.fd == mWakeReadPipeFd) { if (eventItem.events & EPOLLIN) { ALOGV("awoken after wake()"); awoken = true; char wakeReadBuffer[16]; ssize_t nRead; do { nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer)); } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer)); } else { ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.", eventItem.events); } continue; } // 接下来是处理设备的输入事件 Device* device = getDeviceByFdLocked(eventItem.data.fd); if (device == nullptr) { continue; } if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) { // ... } 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]; event->when = processEventTimestamp(iev); event->readTime = systemTime(SYSTEM_TIME_MONOTONIC); 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.c_str()); deviceChanged = true; closeDeviceLocked(*device); } else { ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, device->identifier.name.c_str()); } } // 处理设备改变 // mPendingEventIndex >= mPendingEventCount 表示处理完所有的输入事件后,再处理设备的改变 if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) { mPendingINotify = false; readNotifyLocked(); deviceChanged = true; } // 设备发生改变,那么跳过当前循环,在下一个循环的开头处理设备改变 if (deviceChanged) { continue; } // 如果有事件,或者被唤醒,那么终止循环,接下来 InputReader 会处理事件或者更新配置 if (event != buffer || awoken) { break; } mPendingEventIndex = 0; mLock.unlock(); // release lock before poll // 此时没有事件,并且也没有被唤醒,那么超时等待 epoll 事件 int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); mLock.lock(); // reacquire lock after poll if (pollResult == 0) { // 处理超时... } if (pollResult < 0) { // 处理错误... } else { // 保存待处理事件的数量 mPendingEventCount = size_t(pollResult); } } // 返回事件的数量 return event - buffer; } |
EventHub::getEvent() 提供事件的过程很长,但是现在我们不必去了解所有的细节,我们要有从整体看局部的眼光。EventHub 其实只生成了两类事件
- 设备的添加/删除事件。这种事件不是通过操作设备而产生的,系统称之为合成事件。
- 输入事件。这种事件是通过操作设备产生的,例如手指在触摸屏上滑动,系统称之为元输入事件。
看来我们得分两部分来分析这两类事件的生成以及处理过程,因此下一篇文章,我们分析合成事件的生成以及处理过程。
本文转自 https://juejin.cn/post/7164221690203865119,如有侵权,请联系删除。