Android input 原理分析(四 _ input 分发

请支持原创~~~·

系列博文:

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. 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

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注