请支持原创~~~·
系列博文:
Android input 原理分析_总序
Android input 原理分析(一) _ input 启动
Android input 原理分析(二) _ EventHub
Android input 原理分析(三) _ scanCode与keyCode映射
Android input 原理分析(四) _ input 分发
Andorid input 原理分析(五) _ input 命令
Android input 原理分析(六) _ input 上层分发流程
Android input 原理分析(七)_ input ANR
前面几篇分析了input的启动过程和EventHub 的监听流程,这一篇将详细分析Input Reader thread 的处理过程。
0. InputReader->loopOnce()
frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::loopOnce() {...size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lock... if (count) {processEventsLocked(mEventBuffer, count);}...} // release lock...mQueuedListener->flush();}
从之前的 原理分析(二) 得知无论是DEVICE_ADDED 或是DEVICE_REMOVED,还是普通的按键,最终都会将所有的信息都存储到第二个参数mEventBuffer 中,并通过getEvents 返回确切的event 个数。
loopOnce 的主要目的:
- 获取从getEvents 获取的events 和对应的count;
- 通过processEventsLocked 进行详细处理;
- flush 告诉InputDispatcher 进行分发等处理;
1. processEventsLocked
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {for (const RawEvent* rawEvent = rawEvents; count;) {int32_t type = rawEvent->type;size_t batchSize = 1;if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {int32_t deviceId = rawEvent->deviceId;while (batchSize < count) {if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||rawEvent[batchSize].deviceId != deviceId) {break;}batchSize += 1;}processEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {switch (rawEvent->type) {case EventHubInterface::DEVICE_ADDED:addDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::DEVICE_REMOVED:removeDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::FINISHED_DEVICE_SCAN:handleConfigurationChangedLocked(rawEvent->when);break;default:ALOG_ASSERT(false); // can't happenbreak;}}count -= batchSize;rawEvent += batchSize;}
}
对getEvents 返回的RawEvent 分别解析,消息大概分为四类:
- DEVICE_ADDED 设备有增加时会通过inotify 把event 抛出来
- DEVICE_REMOVED 设备有删除时抛出
- FINISHED_DEVICE_SCAN 扫描完成时抛出
- 普通的input event
1.1 addDeviceLocked
void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {...InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);device->configure(when, &mConfig, 0);device->reset(when);...mDevices.emplace(eventHubId, device);...
}
核心是通过identifier 确认InputDevice 已经添加到InputReader 的mDevice 中,如果没有添加则create 一个InputDevice。
InputDevice 中记录了设备的详细信息,并记录了不同设备属性对应的InputMapper。最终不同设备对于event 的处理策略都对应到InputMapper的process() 中。
–>
继续分析createDeviceLocked()
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(int32_t eventHubId, const InputDeviceIdentifier& identifier) {auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {return devicePair.second->getDescriptor().size() && identifier.descriptor.size() &&devicePair.second->getDescriptor() == identifier.descriptor;});std::shared_ptr<InputDevice> device;if (deviceIt != mDevices.end()) {device = deviceIt->second;} else {int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked();device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),identifier);}device->addEventHubDevice(eventHubId);return device;
}
在创建InputDevice 时会将mContext 传入InputDevice,mContext 用以InputDevice 通过InputReader 访问全局input 设备的状态和参数,例如通过mContext 可以访问NativeInputManager、EventHub、InputReader甚至是InputDispatcher的回调;
–>
继续分析addEventHubDevice(),frameworks\native\services\inputflinger\reader\InputDevice.cpp
void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {if (mDevices.find(eventHubId) != mDevices.end()) {return;}std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));uint32_t classes = contextPtr->getDeviceClasses();std::vector<std::unique_ptr<InputMapper>> mappers;// Check if we should skip populationif (!populateMappers) {mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});return;}// Switch-like devices.if (classes & INPUT_DEVICE_CLASS_SWITCH) {mappers.push_back(std::make_unique<SwitchInputMapper>(*contextPtr));}// Scroll wheel-like devices.if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(*contextPtr));}// Vibrator-like devices.if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));}// Keyboard-like devices.uint32_t keyboardSource = 0;int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {keyboardSource |= AINPUT_SOURCE_KEYBOARD;}if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;}if (classes & INPUT_DEVICE_CLASS_DPAD) {keyboardSource |= AINPUT_SOURCE_DPAD;}if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {keyboardSource |= AINPUT_SOURCE_GAMEPAD;}if (keyboardSource != 0) {mappers.push_back(std::make_unique<KeyboardInputMapper>(*contextPtr, keyboardSource, keyboardType));}// Cursor-like devices.if (classes & INPUT_DEVICE_CLASS_CURSOR) {mappers.push_back(std::make_unique<CursorInputMapper>(*contextPtr));}// Touchscreens and touchpad devices.if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));}// Joystick-like devices.if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {mappers.push_back(std::make_unique<JoystickInputMapper>(*contextPtr));}// External stylus-like devices.if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {mappers.push_back(std::make_unique<ExternalStylusInputMapper>(*contextPtr));}// insert the context into the devices setmDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
}
- InputDevieContext 内有成员变量InputDevice 和 eventHub id,这个类专门提出来用以InputDevice 与EventHub 交互,通过id 用来访问EventHub的函数,但只限于访问当前input device。
- 根据device classes 创建对应的mapper,确定InputDevice 的具体策略mapper。
- 将创建好的eventHubId 与mapper 对应关系存入到mDevices 中
根据不同的classes 创建mapper 对应关系如下:
classes type |
mapper |
INPUT_DEVICE_CLASS_SWITCH |
SwitchInputMapper |
INPUT_DEVICE_CLASS_ROTARY_ENCODER |
RotaryEncoderInputMapper |
INPUT_DEVICE_CLASS_VIBRATOR |
VibratorInputMapper |
INPUT_DEVICE_CLASS_KEYBOARD INPUT_DEVICE_CLASS_ALPHAKEY INPUT_DEVICE_CLASS_DPAD INPUT_DEVICE_CLASS_GAMEPAD |
KeyboardInputMapper |
INPUT_DEVICE_CLASS_CURSOR |
CursorInputMapper |
INPUT_DEVICE_CLASS_TOUCH_MT |
MultiTouchInputMapper |
INPUT_DEVICE_CLASS_TOUCH |
SingleTouchInputMapper |
INPUT_DEVICE_CLASS_JOYSTICK |
JoystickInputMapper |
INPUT_DEVICE_CLASS_EXTERNAL_STYLUS |
ExternalStylusInputMapper |
1.2 removeDeviceLocked
void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) {auto deviceIt = mDevices.find(eventHubId);... std::shared_ptr<InputDevice> device = std::move(deviceIt->second);mDevices.erase(deviceIt);bumpGenerationLocked();...device->removeEventHubDevice(eventHubId);...
}
–>
void InputDevice::removeEventHubDevice(int32_t eventHubId) {mDevices.erase(eventHubId);
}
removeDeviceLocked 函数与addDeviceLocked对应,将符合要求的device 分别从InputReader 中的mDevices 和InputDevice 中的mDevice 移除
1.3 handleConfigurationChangedLocked
void InputReader::handleConfigurationChangedLocked(nsecs_t when) {// Reset global meta state because it depends on the list of all configured devices.updateGlobalMetaStateLocked();// Enqueue configuration changed.NotifyConfigurationChangedArgs args(mContext.getNextId(), when);mQueuedListener->notifyConfigurationChanged(&args);
}
创建了一个NotifyConfigurationChangedArgs,来看下mQueuedListener->notifyConfigurationChanged:
void QueuedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {traceEvent(__func__, args->id);mArgsQueue.push_back(new NotifyConfigurationChangedArgs(*args));
}
1.4 processEventsForDeviceLocked
这个是按键分发的起点,无论是keyboard还是motion。
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,size_t count) {auto deviceIt = mDevices.find(eventHubId);if (deviceIt == mDevices.end()) {ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);return;}std::shared_ptr<InputDevice>& device = deviceIt->second;if (device->isIgnored()) {// ALOGD("Discarding event for ignored deviceId %d.", deviceId);return;}device->process(rawEvents, count);
}
- 查询InputReader 中是否存在device,该device 是通过EventHub DEVICE_ADDED 消息上来的;
- 调用InputDevice 的process() 进行分发处理;
–>
继续分析InputDevice 的process()
frameworks\native\services\inputflinger\reader\InputDevice.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {// Process all of the events in order for each mapper.// We cannot simply ask each mapper to process them in bulk because mappers may// have side-effects that must be interleaved. For example, joystick movement events and// gamepad button presses are handled by different mappers but they should be dispatched// in the order received.for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {if (mDropUntilNextSync) {if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {mDropUntilNextSync = false;} } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());mDropUntilNextSync = true;reset(rawEvent->when);} else {for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {mapper.process(rawEvent);});}--count;}
}
详细的rawEvent->type 参考:https://www.kernel.org/doc/Documentation/input/event-codes.txt
最后会通过一个for 循环分别调用InputDevice 中mappers 的process 函数,因为mapper 有多种,这里用KeyboardInputMapper 举例:
–>
继续分析KeyboardInputMapper 的process()
frameworks\native\services\inputflinger\reader\mapper\KeyboardInputMapper.cpp
void KeyboardInputMapper::process(const RawEvent* rawEvent) {switch (rawEvent->type) {case EV_KEY: {int32_t scanCode = rawEvent->code;int32_t usageCode = mCurrentHidUsage;mCurrentHidUsage = 0;//排除鼠标按键的处理if (isKeyboardOrGamepadKey(scanCode)) {processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);}break;}case EV_MSC: {if (rawEvent->code == MSC_SCAN) {mCurrentHidUsage = rawEvent->value;}break;}case EV_SYN: {if (rawEvent->code == SYN_REPORT) {mCurrentHidUsage = 0;}}}
}
最终会根据scanCode 和usageCode 进行按键的最终处理,注意,这里rawEvent->value 代表是down 还是up,如果不为0表示down。
–>
继续分析processKey()
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) {int32_t keyCode;int32_t keyMetaState;uint32_t policyFlags;if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState,&policyFlags)) {keyCode = AKEYCODE_UNKNOWN;keyMetaState = mMetaState;policyFlags = 0;}if (down) {// Rotate key codes according to orientation if needed.if (mParameters.orientationAware) {keyCode = rotateKeyCode(keyCode, getOrientation());}// Add key down.ssize_t keyDownIndex = findKeyDown(scanCode);if (keyDownIndex >= 0) { //此时为down,如果能在mKeyDowns 里面找到,说明else 已经进入过,repeat// key repeat, be sure to use same keycode as before in case of rotationkeyCode = mKeyDowns[keyDownIndex].keyCode;} else { //此时为down,mKeyDowns 里面没有,说明按键刚按下// key downif ((policyFlags & POLICY_FLAG_VIRTUAL) &&getContext()->shouldDropVirtualKey(when, keyCode, scanCode)) {return;}if (policyFlags & POLICY_FLAG_GESTURE) {getDeviceContext().cancelTouch(when);}KeyDown keyDown;keyDown.keyCode = keyCode;keyDown.scanCode = scanCode;mKeyDowns.push_back(keyDown);}mDownTime = when;} else {// Remove key down.ssize_t keyDownIndex = findKeyDown(scanCode);if (keyDownIndex >= 0) { //此时为up,如果mKeyDowns 里面有key,说明刚松开,需要erase,以便下一次down// key up, be sure to use same keycode as before in case of rotationkeyCode = mKeyDowns[keyDownIndex].keyCode;mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);} else { //此时为up,如果mKeyDowns 里没有key,说明此按键没有被down 过// key was not actually downALOGI("Dropping key up from device %s because the key was not down. ""keyCode=%d, scanCode=%d",getDeviceName().c_str(), keyCode, scanCode);return;}}if (updateMetaStateIfNeeded(keyCode, down)) {// If global meta state changed send it along with the key.// If it has not changed then we'll use what keymap gave us,// since key replacement logic might temporarily reset a few// meta bits for given key.keyMetaState = mMetaState;}nsecs_t downTime = mDownTime;...NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, //native 的keydown/keyup 会与KeyEvent中对应AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); //创建NotifyKeyArgs,以便分发这个keygetListener()->notifyKey(&args); //这里调用的是QueuedInputListener 中的notifyKey()
}
mapKey() 通过scanCode 映射出keyCode,详细可以参考《原理分析(三)》
至此,keyboard 的event 基本分析完成,现在QueuedInputListener 中存放了一堆的event,需要分发出去,所以回到InputReader::loopOnce()
2. mQueuedListener->flush()
void QueuedInputListener::flush() {size_t count = mArgsQueue.size();for (size_t i = 0; i < count; i++) {NotifyArgs* args = mArgsQueue[i];args->notify(mInnerListener);delete args;}mArgsQueue.clear();
}
这里是多态的对象队列,这里还是以NotifyKeyArgs 为例
2.1 NotifyKeyArgs::notify()
frameworks\native\services\inputflinger\InputListener.cpp
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {listener->notifyKey(this);
}
参数从QueuedInputListener 中传入,而QueuedInputListener 中 mInnerListener 在构造时由InputReader 传入,追溯InputReader 构造,是在InputManager 构造的时候:
frameworks\native\services\inputflinger\InputManager.cpp
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {mDispatcher = createInputDispatcher(dispatcherPolicy);mClassifier = new InputClassifier(mDispatcher);mReader = createInputReader(readerPolicy, mClassifier);
}
所以,NotifyKeyArgs::notify 中的notifyKey() 就可以继续追溯InputClassifier中:
frameworks\native\services\inputflinger\InputClassifier.cpp
void InputClassifier::notifyKey(const NotifyKeyArgs* args) {// pass throughmListener->notifyKey(args);
}
从InputManager 构造中得知,InputClassifier 中的mListener 就是InputDispatcher 了,所以,InputReader 的event 处理,最终调用到了InputDispatcher::notifyKey()
3. InputDispatcher::notifyKey()
frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {if (!validateKeyEvent(args->action)) { //接收ACTION_DOWN 和ACTION_UPreturn;}...KeyEvent event;event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,args->action, flags, keyCode, args->scanCode, metaState, repeatCount,args->downTime, args->eventTime);android::base::Timer t;mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); //正式分发key前,先interceptif (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { //处理时间如果大于50ms 输出警告ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",std::to_string(t.duration().count()).c_str());}bool needWake;{ // acquire lockmLock.lock();if (shouldSendKeyToInputFilterLocked(args)) { //确认是否进入input filter 处理mLock.unlock();policyFlags |= POLICY_FLAG_FILTERED;if (!mPolicy->filterInputEvent(&event, policyFlags)) { //进入input filter处理,如果有consumer设置了filter,会直接返回falsereturn; // event was consumed by the filter}mLock.lock();}KeyEntry* newEntry =new KeyEntry(args->id, args->eventTime, args->deviceId, args->source,args->displayId, policyFlags, args->action, flags, keyCode,args->scanCode, metaState, repeatCount, args->downTime);needWake = enqueueInboundEventLocked(newEntry); //唤醒dispatcher thread 前准备工作mLock.unlock();} // release lockif (needWake) { //如果上面enqueue 返回ok,则唤醒dispatcher threadmLooper->wake();}
}
InputReader 中的notify 队列分别处理,当时input key 时会进入dispatcher 中的NotifyKey,这里核心处理有:
- mPolicy->interceptKeyBeforeQueueing(),提前将key 发送到JAVA 中的PhoneWindowManager 处理一些UI 交互问题;
- mPolicy->filterInputEvent(),如果有consumer enable 了filter,表示这个key 会被拦截掉,不会进行dispatch
- enqueueInboundEventLocked(),将event 以KeyEntry 形式存放在mInboundQueue,以便InputDispatcher 唤醒后取出。
- mLooper->wake(),唤醒InputDispatcher thread。
3.1 mPolicy->interceptKeyBeforeQueueing()
mPolicy 是在InputDispatcher 构造的时候传入,从InputManager 构造中得知,这里mPolicy 是NativeInputManager 实例。
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,uint32_t& policyFlags) {ATRACE_CALL();...if ((policyFlags & POLICY_FLAG_TRUSTED)) {...if (keyEventObj) {wmActions = env->CallIntMethod(mServiceObj,gServiceClassInfo.interceptKeyBeforeQueueing,keyEventObj, policyFlags);if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {wmActions = 0;}...} else {...}...} else {...}
}
这里从JNI 调用JAVA,mServiceObj 是在nativeInit 传入,为IMS 的java 端实例。
通过interceptKeyBeforeQueueing 接口调用IMS ,将native 解析好好KeyEvent 传回JAVA 处理。
–>
继续分析IMS 中interceptKeyBeforeQueueing()
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);}
现在回头来看下《原理分析(一)》 中第2点,SystemServer 启动时:
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {...wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);...t.traceBegin("StartInputManager");inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());inputManager.start();t.traceEnd();...}
这里注意两点:
- WSM 构造时传入的PhoneWindowManager实例;
- inputManager 会将WSM 中的InputManagerCallback 注册进来;
来看下这个InputManagerCallback:
frameworks\base\services\core\java\com\android\server\wm\InputManagerCallback.java
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {}
–>
继续分析mWindowManagerCallbacks.interceptKeyBeforeQueueing()
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);}
这里mService 为WMS,mPolicy 就是WSM 在构造时传入的PhoneWindowManager
–>
继续分析PhoneWindowManager.interceptKeyBeforeQueueing()
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {...// Handle special keys.switch (keyCode) {case KeyEvent.KEYCODE_BACK: {if (down) {interceptBackKeyDown();} else {...}break;}case KeyEvent.KEYCODE_VOLUME_DOWN:case KeyEvent.KEYCODE_VOLUME_UP:case KeyEvent.KEYCODE_VOLUME_MUTE: {...}case KeyEvent.KEYCODE_ENDCALL: {...}case KeyEvent.KEYCODE_POWER: {cancelPendingAccessibilityShortcutAction();result &= ~ACTION_PASS_TO_USER;isWakeKey = false; // wake-up will be handled separatelyif (down) {interceptPowerKeyDown(event, interactive);} else {interceptPowerKeyUp(event, interactive, canceled);}break;}...case KeyEvent.KEYCODE_SLEEP: {...}...}// Intercept the Accessibility keychord for TV (DPAD_DOWN + Back) before the keyevent is// processed through interceptKeyEventBeforeDispatch since Talkback may consume this event// before it has a chance to reach that method.if (mHasFeatureLeanback) {switch (keyCode) {case KeyEvent.KEYCODE_DPAD_DOWN:case KeyEvent.KEYCODE_BACK: {boolean handled = interceptAccessibilityGestureTv(keyCode, down);if (handled) {result &= ~ACTION_PASS_TO_USER;}break;}}}...return result;}
这里处理的东西比较多,包括BACK、POWER、VOLUME_* 等等。暂时不做分析了。
从InputDispatcher 代码得知,这里的需要尽快不要超过50ms,在分发key 之前希望有些UI 逻辑尽快处理掉。
3.2 mPolicy->filterInputEvent()
在处理完 interceptKeyBeforeQueueing 后,还是需要回到InputDispatcher 中,这里继续分析filterInputEvent()
InputDispatcher 中在调用该函数之前,需要通过 shouldSendKeyToInputFilterLocked() 确认是否已经set 过,而SetInputFilterEnabled 是通过JAVA 设置下来的属性,如果为true,则表示enable 了这个filter,那么所有的key 都会全部传送给IMS。
bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {ATRACE_CALL();jobject inputEventObj;JNIEnv* env = jniEnv();switch (inputEvent->getType()) {case AINPUT_EVENT_TYPE_KEY:inputEventObj = android_view_KeyEvent_fromNative(env,static_cast<const KeyEvent*>(inputEvent));break;case AINPUT_EVENT_TYPE_MOTION:inputEventObj = android_view_MotionEvent_obtainAsCopy(env,static_cast<const MotionEvent*>(inputEvent));break;default:return true; // dispatch the event normally}...// The callee is responsible for recycling the event.jboolean pass = env->CallBooleanMethod(mServiceObj, gServiceClassInfo.filterInputEvent,inputEventObj, policyFlags);...return pass;
}
这里JNI 调用JAVA 接口,来看下IMS 中实现:
final boolean filterInputEvent(InputEvent event, int policyFlags) {synchronized (mInputFilterLock) {if (mInputFilter != null) {try {mInputFilter.filterInputEvent(event, policyFlags);} catch (RemoteException e) {/* ignore */}return false;}}event.recycle();return true;}
这里mInputFilter 是通过setInputFilter 设置下来,即如果有consumer 设置了该filter,这里会直接放回false,表示有customer 处理了这个key。
3.3 enqueueInboundEventLocked()
如果没有filter input,会调用enqueueInboundEventLocked(),这个函数是input 分发的关键,所有从InputReader notify 上来的event,都需要通过这个函数存放到mInbondQueue,InputDispatcher接着会从这个queue 中取出event并继续分发。详细看最后的时序图。
最终,根据函数的返回结果,确定是否要wake dipatcher thread。
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {bool needWake = mInboundQueue.empty();mInboundQueue.push_back(entry);traceInboundQueueLengthLocked();switch (entry->type) {case EventEntry::Type::KEY: {// Optimize app switch latency.// If the application takes too long to catch up then we drop all events preceding// the app switch key.const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry);if (isAppSwitchKeyEvent(keyEntry)) {if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {mAppSwitchSawKeyDown = true;} else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {if (mAppSwitchSawKeyDown) {mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;mAppSwitchSawKeyDown = false;needWake = true;}}}break;}case EventEntry::Type::MOTION: {if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {mNextUnblockedEvent = entry;needWake = true;}break;}case EventEntry::Type::FOCUS: {LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");break;}case EventEntry::Type::CONFIGURATION_CHANGED:case EventEntry::Type::DEVICE_RESET: {// nothing to dobreak;}}return needWake;
}
从这个函数我们可以看出,在两种情况下,它的返回值为true:
- 一是当加入该键盘事件到mInboundQueue之前,mInboundQueue为空,这表示InputDispatcherThread线程正在睡眠等待InputReaderThread线程的唤醒,因此,它返回true表示要唤醒InputDispatccherThread线程;
- 二是加入该键盘事件到mInboundQueue之前,mInboundQueue不为空,但是此时用户按下的是Home键,按下Home键表示要切换App,我们知道,在切换App时,新的App会把它的键盘消息接收通道注册到InputDispatcher中去,并且会等待InputReader的唤醒,因此,在这种情况下,也需要返回true,表示要唤醒InputDispatccherThread线程。
如果不是这两种情况,那么就说明InputDispatccherThread线程现在正在处理前面的键盘事件,不需要唤醒它。
3.4 mLooper->wake()
if (needWake) {mLooper->wake();}
mLooper 在InputDispatcher 构造的时候创建,3.3 节会确认是否需要唤醒InputThread 继续分发工作。
4. InputDispatcher::dispatchOnce()
void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;{...// Run a dispatch loop if there are no pending commands.// The dispatch loop might enqueue commands to run afterwards.if (!haveCommandsLocked()) { //EventEntry 第一次进来,没有添加到command中,后面会添加进来等待下面运行dispatchOnceInnerLocked(&nextWakeupTime);}// Run all pending commands if there are any.// If any commands were run then force the next poll to wake up immediately.if (runCommandsLockedInterruptible()) { //通过上面执行会将KeyEntry 添加到command 中nextWakeupTime = LONG_LONG_MIN;} ...}// Wait for callback or timeout or wake. (make sure we round up, not down)nsecs_t currentTime = now();int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);mLooper->pollOnce(timeoutMillis);
}
dispatchOnce 主要分四部分:
- mCommandQueue 中存放了CommandEntry,存放一些pending commands,最初mCommandQueue 中是没有entry 的,所以会调用dispatchOnceInnerLocked,这个函数有可能会有entry 添加到mCommandQueue 中;
- 如果mCommand 中有pending commands,runCommandsLockedInterruptible函数执行会返回true,nextWakeupTime 则会被置最小值,这个最小值让looper 立即唤醒;
- mLooper->pollOnce 会进入下一次的loop,但是timeoutMillis 如果是最小值会被立即唤醒;
- 如果因为timeoutMillis 为最小值而唤醒looper,说明pending commands 已经被执行,下面再次调用dispatchOnceInnerLocked 确认event 是否被intercept,是否进行深入分发到App;
先来看下dispatchOnceInnerLocked:
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { //注意这里的参数,ANR 需要nsecs_t currentTime = now();...// Ready to start a new event.// If we don't already have a pending event, go grab one.if (!mPendingEvent) { //开始mPendingEvent 为空,在有按键的时候会从mInboundQueue 取出,创建是在notifyKey中if (mInboundQueue.empty()) {...} else {// Inbound queue has at least one entry.mPendingEvent = mInboundQueue.front();mInboundQueue.pop_front();traceInboundQueueLengthLocked();}...}...switch (mPendingEvent->type) {...case EventEntry::Type::KEY: {KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); //在notifyKey 中创建好的,这里转换下...done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);break;}}...}
这一篇主要分析input 分发的流程,所以抽取分发的流程代码,其他的状态控制、input ANR 等,后面再补充。
这里主要是将notifyKey 中存放在mInboundQueue 中的KeyEntry 取出来,传入到dispatchKeyLocked 进行分发。
4.1 dispatchKeyLocked()
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {...// Give the policy a chance to intercept the key.if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { //如果在dispatch 之前没有处理intercept,则开始处理std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(&InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); //创建CommandEntry,以便interceptsp<InputWindowHandle> focusedWindowHandle =getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));if (focusedWindowHandle != nullptr) {commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());}commandEntry->keyEntry = entry;postCommandLocked(std::move(commandEntry)); //存入command queueentry->refCount += 1;return false; // 返回,立即处理这个intercept} else {entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;}} else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {...}if (*dropReason != DropReason::NOT_DROPPED) { //确认经过intercept 是否中断该次dispatchsetInjectionResult(entry,*dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED: INPUT_EVENT_INJECTION_FAILED);mReporter->reportDroppedKey(entry->id);return true;}// Identify targets.std::vector<InputTarget> inputTargets;int32_t injectionResult =findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);//查找目标窗口if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {return false;}...// Dispatch the key.dispatchEventLocked(currentTime, entry, inputTargets); //继续分发按键return true;
}
这里有几点注意:
- 当dispatch 之前dispatchInProgress 为false,所以会先确认是否为repeat key
- 在dispatch 之前,会给policy 一个intercept 的机会,会在queue 中添加一个command 去做doInterceptKeyBeforeDispatchingLockedInterruptible,并且return 回looper。looper 之后会运行runCommandsLockedInterruptible,确认该event 是否被intercept,如果没有被阻断,那么,InputDispatcher 的looper 会再次运行,继续下一次流程,此时entry->interceptKeyResult 已经处理完成;
- 通过findFocusedWindowTargetsLocked 确认inputTarget,即分发的目标;
- 通过dispatchEventLocked 进行最终的按键分发处理
4.1.1 doInterceptKeyBeforeDispatchingLockedInterruptible
如果在dispatch 之前会进入intercept,用以确认是否中断此次event key 的dispatch
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) {KeyEntry* entry = commandEntry->keyEntry;KeyEvent event = createKeyEvent(*entry);mLock.unlock();android::base::Timer t;sp<IBinder> token = commandEntry->inputChannel != nullptr? commandEntry->inputChannel->getConnectionToken(): nullptr;nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",std::to_string(t.duration().count()).c_str());}mLock.lock();if (delay < 0) {entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;} else if (!delay) {entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;} else {entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;entry->interceptKeyWakeupTime = now() + delay;}entry->release();
}
注意两点:
- 通过mPolicy 进行这次intercept
- 通过返回值决定是否中断此次dispatch;
如果mPolicy->interceptKeyBeforeDispatching 的返回值为-1,则中断此次dispatch。这里的interceptKeyBeforeDispatching 同之前的beforeQueuing,暂不做详细分解。
4.1.2 findFocusedWindowTargetsLocked
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,const EventEntry& entry,std::vector<InputTarget>& inputTargets,nsecs_t* nextWakeupTime) {std::string reason;int32_t displayId = getTargetDisplayId(entry);sp<InputWindowHandle> focusedWindowHandle =getValueByKey(mFocusedWindowHandlesByDisplay, displayId);sp<InputApplicationHandle> focusedApplicationHandle =getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);...// we have a valid, non-null focused windowresetNoFocusedWindowTimeoutLocked();// Check permissions.if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {return INPUT_EVENT_INJECTION_PERMISSION_DENIED;}...// Success! Output targets.addWindowTargetLocked(focusedWindowHandle,InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,BitSet32(0), inputTargets);// Done.return INPUT_EVENT_INJECTION_SUCCEEDED;
}
主要确认focus 的window,如果没有出现ANR,会通过window handle 确认input channel或者是axis 的point,并存放到inputTarget 中。
不过这里需要关心的是focusedWindowHandle,它是怎么来的,另外窗口增删的时候如何保持最新的呢?这里就牵扯到跟WindowManagerService交互的问题了,focusedWindowHandle 的值是在InputDispatcher::setInputWindows中设置的,详细看函数updateWindowHandlesForDisplayLocked()。
4.1.3 dispatchEventLocked
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,const std::vector<InputTarget>& inputTargets) {...pokeUserActivityLocked(*eventEntry);for (const InputTarget& inputTarget : inputTargets) {sp<Connection> connection =getConnectionLocked(inputTarget.inputChannel->getConnectionToken());if (connection != nullptr) {prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);}...}
}
–>
重点分析prepareDispatchCycleLocked,会通过该函数进行分发:
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection,EventEntry* eventEntry,const InputTarget& inputTarget) {...// Not splitting. Enqueue dispatch entries for the event as is.enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
–>
继续分析enqueueDispatchEntriesLocked
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& connection,EventEntry* eventEntry,const InputTarget& inputTarget) {if (ATRACE_ENABLED()) {std::string message =StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=0x%" PRIx32 ")",connection->getInputChannelName().c_str(), eventEntry->id);ATRACE_NAME(message.c_str());}bool wasEmpty = connection->outboundQueue.empty();// Enqueue dispatch entries for the requested modes.enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_OUTSIDE);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_IS);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);// If the outbound queue was previously empty, start the dispatch cycle going.if (wasEmpty && !connection->outboundQueue.empty()) {startDispatchCycleLocked(currentTime, connection);}
}
这里主要是两个部分:
- enqueueDispatchEntryLocked
- startDispatchCycleLocked
第 3.3 节中讲到 input 分发的关键点,将InputReader 中出来的KeyEntry 存放到mInboundQueue。这里equeueDispatchEntryLocked 是input 分发的另一个关键地方。将mInboundQueue 中的KeyEntry 取出后并转换成另一种形式DispatchEntry,最终会存放到这里的connection->outboundQueue 中。
在equeueDispatchEntryLocked 中,对于motion 根据不同dispatchMode 指定不同的 Event_Action,对于key 来说只会是AKEY_EVENT_ACTION_DOWN。
–>
继续来分析startDispatchCycleLocked
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection) {...while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {...switch (eventEntry->type) {case EventEntry::Type::KEY: {...status =connection->inputPublisher.publishKeyEvent(...);break;}case EventEntry::Type::MOTION: {...status = connection->inputPublisher.publishMotionEvent(...);reportTouchEventForStatistics(*motionEntry);break;}...}...// Re-enqueue the event on the wait queue.connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),connection->outboundQueue.end(),dispatchEntry));traceOutboundQueueLength(connection);connection->waitQueue.push_back(dispatchEntry);...}
}
- 将deliver time 存入dispatchEntry->deliveryTime 中;
- 将5s 的延迟时间存入到dispatchEntry->timeoutTime 中;
- 通过connection->inputPublisher 调用publishKeyEvent或publishMotionEvent 、publishFocusEvent发送此次event;
- 将dispatchEntry 从connection->outboundQueue 移到connection->waitQueue 中;
- 此次timeout 计入mAnrTracker 中;
–>
frameworks\native\libs\input\InputTransport.cpp
status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,int32_t source, int32_t displayId,std::array<uint8_t, 32> hmac, int32_t action,int32_t flags, int32_t keyCode, int32_t scanCode,int32_t metaState, int32_t repeatCount, nsecs_t downTime,nsecs_t eventTime) {...InputMessage msg;msg.header.type = InputMessage::Type::KEY;msg.body.key.seq = seq;msg.body.key.eventId = eventId;...return mChannel->sendMessage(&msg);
}
publishMotionEvent 同样是调用mChannel->sendMessage
–>
继续来分析sendMessage
status_t InputChannel::sendMessage(const InputMessage* msg) {const size_t msgLength = msg->size();InputMessage cleanMsg;msg->getSanitizedCopy(&cleanMsg);ssize_t nWrite;do {nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);} while (nWrite == -1 && errno == EINTR);...return OK;
}
其实这里socket 通信。
这个Socket是怎么来的呢?或者说两端通信的一对Socket是怎么来的呢?详细见第 5 节。
5. InputChannel 由来
上面说到socket 通信,其实还是要牵扯到WindowManagerService,在APP端向WMS请求添加窗口的时候,会伴随着Input通道的创建,窗口的添加一定会调用ViewRootImpl的setView函数。
本节主要分三个部分:
- setView 创建Input Channel pair;
- 将server 端InputChannel 注册到 Input dispatcher;
- 将client 端InputChannel 注册到WindowInputEventReceiver;
- InputChannel pair 通过socket 通信;
5.1 setView 创建Input Channel pair
frameworks\base\core\java\android\view\ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {synchronized (this) {...// Schedule the first layout -before- adding to the window// manager, to make sure we do the relayout before receiving// any other events from the system.requestLayout();InputChannel inputChannel = null;if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {inputChannel = new InputChannel(); //创建InputChannel}mForceDecorViewVisibility = (mWindowAttributes.privateFlags& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;try {mOrigWindowType = mWindowAttributes.type;mAttachInfo.mRecomputeGlobalAttributes = true;collectViewAttributes();adjustLayoutParamsForCompatibility(mWindowAttributes);res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes, //添加窗口,并请求开辟socket input 通信通道getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mDisplayCutout, inputChannel,mTempInsets, mTempControls);setFrame(mTmpFrame);}...if (inputChannel != null) {if (mInputQueueCallback != null) {mInputQueue = new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}mInputEventReceiver = new WindowInputEventReceiver(inputChannel, //监听input 通道Looper.myLooper());}...}}
注意:
在IWindowSession.aidl定义中 InputChannel是out 类型,也就是说需要服务端进行填充,那么接着看服务端WMS如何填充的呢?
另外,在最后会通过这个out 的InputChannel 和Looper.myLooper() 创建一个WindowInputEventReceiver,用以最终的通信,最后是如何通信的呢?详细看 5.3 节。
先来看下addToDisplayAsUser
step 1. addToDisplayAsUser
framewors\base\services\core\java\com\android\server\wm\Session.java
@Overridepublic int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, Rect outFrame,Rect outContentInsets, Rect outStableInsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,outInsetsState, outActiveControls, userId);}
这里已经将参数标记为outInputChannel。
step 2. addWindow
framewors\base\services\core\java\com\android\server\wm\WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,Rect outContentInsets, Rect outStableInsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,int requestUserId) {......final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if (openInputChannels) {win.openInputChannel(outInputChannel);}...}
step3. openInputChannel
framewors\base\services\core\java\com\android\server\wm\WindowState.java
void openInputChannel(InputChannel outInputChannel) {...String name = getName();InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); //创建通信通道mInputChannel = inputChannels[0]; //本地用mClientChannel = inputChannels[1]; //App 用mWmService.mInputManager.registerInputChannel(mInputChannel);...if (outInputChannel != null) {mClientChannel.transferTo(outInputChannel);mClientChannel.dispose();mClientChannel = null;}...
}
- 这里通过InputChannel 创建了一个inputChannels 数组,就是channel pair。
- 另外,mInputChannel 会保留在WMS 本地,并将其注册到 Input Manager 中作为server端;
- mClientChannel 将通过outInputChannel 传回到App 端,即上面ViewRootImpl 中setView 里创建的InputChannel;
step4. openInputChannelPair
frameworks\base\core\java\android\view\InputChannel.java
public static InputChannel[] openInputChannelPair(String name) {...return nativeOpenInputChannelPair(name);
}
–>
framewoks\native\libs\input\InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {int sockets[2];if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {status_t result = -errno;ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",name.c_str(), errno);outServerChannel.clear();outClientChannel.clear();return result;}int bufferSize = SOCKET_BUFFER_SIZE;setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));sp<IBinder> token = new BBinder();std::string serverChannelName = name + " (server)";android::base::unique_fd serverFd(sockets[0]);outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);std::string clientChannelName = name + " (client)";android::base::unique_fd clientFd(sockets[1]);outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);return OK;
}
这里socketpair的创建与访问其实是还是借助文件描述符,WMS需要借助Binder通信向APP端回传文件描述符fd,这部分只是可以参考Binder知识,主要是在内核层面实现两个进程fd的转换,窗口添加成功后,socketpair被创建,被传递到了APP端,但是信道并未完全建立,因为还需要一个主动的监听,毕竟消息到来是需要通知的,先看一下信道模型
5.2 registerInputChannel
接着5.1 节中 step 3,通过openInputChannelPair 创建好Input Channel pair 之后,会将server 端的InputChannel 注册到Input Dispatcher 中。
framewors\base\services\core\java\com\android\server\wm\WindowState.java
void openInputChannel(InputChannel outInputChannel) {...InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);mInputChannel = inputChannels[0];mClientChannel = inputChannels[1];mWmService.mInputManager.registerInputChannel(mInputChannel);...}
—->
frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
public void registerInputChannel(InputChannel inputChannel) {if (inputChannel == null) {throw new IllegalArgumentException("inputChannel must not be null.");}nativeRegisterInputChannel(mPtr, inputChannel);}
—->
frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,jlong ptr, jobject inputChannelObj) {NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,inputChannelObj);...status_t status = im->registerInputChannel(env, inputChannel);...
}
这里会调用NativeInputManager regiterInputChannel,最终调用到InputDispatcher。
—->
frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {{ // acquire lockstd::scoped_lock _l(mLock);...sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);int fd = inputChannel->getFd();...mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);} // release lock// Wake the looper because some connections have changed.mLooper->wake();return OK;
}
通过inputChannel 创建一个Connection,并将socket fd 添加到Looper 线程的epoll 数组中,等待client 发送异常消息等,详细看handleReceiveCallback,这里暂不做过多解析。
这里也就回到了4.1.3 节中,通过InputDispatcher最终会通过InputChannel::sendMessage,然后向socket fd 中发送消息。
5.3 创建WindowInputEventReceiver
回到 5.1 节,在setView 的时候会通过addToDisplayAsUser 创建InputChannel pair,并注册server 端的InputChannel 到InputDispatcher中。但在setView 的最后将out Inputchannel 注册到WindowInputEventReceiver 中。
ViewRootImpl.java setView函数中:
if (inputChannel != null) {if (mInputQueueCallback != null) {mInputQueue = new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}mInputEventReceiver = new WindowInputEventReceiver(inputChannel,Looper.myLooper());}
注意这里WindowInputEventReceiver 构造的参数有两个:
- InputChannel,也就是out InputChannel,或称client InputChannel;
- Looper.myLooper(),也就是主线程;
step1. WindowInputEventReceiver 类
final class WindowInputEventReceiver extends InputEventReceiver {public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {super(inputChannel, looper);}@Overridepublic void onInputEvent(InputEvent event) {...}}
step2. InputEventReceiver 构造
frameworks\base\core\java\android\view\InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {if (inputChannel == null) {throw new IllegalArgumentException("inputChannel must not be null");}if (looper == null) {throw new IllegalArgumentException("looper must not be null");}mInputChannel = inputChannel;mMessageQueue = looper.getQueue();mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue);mCloseGuard.open("dispose");}
step3. nativeInit 将client InputChannel 加到Looper 的epoll 中
来看下nativeInit:
frameworks\base\core\jni\android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,inputChannelObj);...sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);...sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);status_t status = receiver->initialize();...return reinterpret_cast<jlong>(receiver.get());
}
这里创建了NativeInputEventReceiver,并调用了initialize()
—->
frameworks\base\core\jni\android_view_InputEventReceiver.cpp
void NativeInputEventReceiver::setFdEvents(int events) {if (mFdEvents != events) {mFdEvents = events;int fd = mInputConsumer.getChannel()->getFd();if (events) {mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);} else {mMessageQueue->getLooper()->removeFd(fd);}}
}
APP端的监听消息的手段是:将socket添加到Looper线程的epoll数组中去,一有消息到来Looper线程就会被唤醒,并获取事件内容,从代码上来看,通信信道的打开是伴随WindowInputEventReceiver的创建来完成的。
注意,这里的addFd 中this 参数,就是下面要解析的 handleEvent()。
这里简单列一下client 端的时序图:
信息到来,Looper根据fd找到对应的监听器:NativeInputEventReceiver,并调用handleEvent处理对应事件
frameworks\base\core\jni\android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {...if (events & ALOOPER_EVENT_INPUT) {JNIEnv* env = AndroidRuntime::getJNIEnv();status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");return status == OK || status == NO_MEMORY ? 1 : 0;}...
之后会进一步读取事件,并封装成Java层对象,传递给Java层,进行相应的回调处理:
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {...ScopedLocalRef<jobject> receiverObj(env, nullptr);bool skipCallbacks = false;for (;;) {uint32_t seq;InputEvent* inputEvent;status_t status = mInputConsumer.consume(&mInputEventFactory,consumeBatches, frameTime, &seq, &inputEvent);if (status != OK && status != WOULD_BLOCK) {ALOGE("channel '%s' ~ Failed to consume input event. status=%d",getInputChannelName().c_str(), status);return status;}...if (!skipCallbacks) {if (!receiverObj.get()) {receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));if (!receiverObj.get()) {ALOGW("channel '%s' ~ Receiver object was finalized ""without being disposed.", getInputChannelName().c_str());return DEAD_OBJECT;}}jobject inputEventObj;switch (inputEvent->getType()) {case AINPUT_EVENT_TYPE_KEY:if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());}inputEventObj = android_view_KeyEvent_fromNative(env,static_cast<KeyEvent*>(inputEvent));break;case AINPUT_EVENT_TYPE_MOTION: {if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Received motion event.", getInputChannelName().c_str());}MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {*outConsumedBatch = true;}inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);break;}case AINPUT_EVENT_TYPE_FOCUS: {...}default:assert(false); // InputConsumer should prevent this from ever happeninginputEventObj = nullptr;}if (inputEventObj) {...env->CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);if (env->ExceptionCheck()) {ALOGE("Exception dispatching input event.");skipCallbacks = true;}env->DeleteLocalRef(inputEventObj);} else {...}}...}
}
最终调用到InputEventReceiver.dispatchInputEvent() 调到JAVA 端
private void dispatchInputEvent(int seq, InputEvent event) {mSeqMap.put(event.getSequenceNumber(), seq);onInputEvent(event);}
这就回到了step1 中的onInputEvent。
至此,Input 分发的过程基本完成,event 发送到JAVA 后处理这里暂时不做过多分析,总结如下:
- InputReader在input event 触发时候会被唤醒,并通过getEvent 获取详细信息;
- InputReader 会根据不同的type(key / motion),进行分发通过回调notify 给InputDispatcher;
- InputDispatcher 收到notify 后,会将input event 以KeyEntry 形式存放在mInboundQueue 中,并唤醒InputDispatcher thread 进行分发流程;
- InputDispatcher thread 被唤醒后会将mInboundQueue 中的KeyEntry 取出,并创建connection,将取出的KeyEntry 存放到connection 中的oubboundQueue 中;
- Event 会记录到各个连接App 的WaitQueue中;
- App 接收到Input event,同时记录到PendingInputEventQueue 中,然后对事件进行分发处理;
- App 处理完后,回调IMS 将负责监听的WaitQueue 中对应的Input 移除;
下面记录Input 分发过程简单的时序图:
参考:
https://blog.csdn.net/wd229047557/article/details/100765797
https://blog.csdn.net/whale_kyle/article/details/96435055
https://blog.csdn.net/angelsmiling/article/details/103248039
https://blog.csdn.net/hongzg1982/article/details/54812359