input 事件_AOSP源码分析:Android Input事件的产生、读取和分发

大家好,今天为大家推荐来自MIUI的Cheeeelok同学的AOSP源码分析系列文章,本文依然从源码的角度带大家理解Android Input事件的产生、读取和分发。还没有看过作者上一篇文章 Android Input子系统:Input进程的创建,监听线程的启动 的同学,现在补上同样不迟,好了,话不多说,直接进入正文。

在上一篇博文中学习了Android Input系统事件监听模块,我们了解到InputManagerService启动后会启动InputReader开始监听来自EventHub的事件。今天就沿着前文的思路,看看EventHub将事件交给InputReader后会发生什么。

本文的内容可以由下图概括:

7c52c47f1c67fc806fe4e8697587e4cd.png

Input事件的读取者InputReader

在上一篇博文中已经讲到,对于InputRead,它在监听过程中会做以下事情:

  1. 启动后循环执行mReader->loopOnce()

  2. loopOnce()中会调用mEventHub->getEvents读取事件

  3. 读到了事件就会调用processEventsLocked处理事件

  4. 处理完成后调用getInputDevicesLocked获取输入设备信息

  5. 调用mPolicy->notifyInputDevicesChanged函数利用InputManagerService的代理通过Handler发送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知输入设备发生了变化

  6. 最后调用mQueuedListener->flush(),将事件队列中的所有事件交给在InputReader中注册过的InputDispatcher

95dd72b404b7a73c317efcd33ad1b78a.png

bool InputReaderThread::threadLoop() {
   mReader->loopOnce();    
   return true;
}

void InputReader::loopOnce() {
   ……

   size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

   { // acquire lock
       AutoMutex _l(mLock);
       mReaderIsAliveCondition.broadcast();        
       if (count) {
           processEventsLocked(mEventBuffer, count);
       }

   ……  if (oldGeneration != mGeneration) {
           inputDevicesChanged = true;
           getInputDevicesLocked(inputDevices);
       }
   } // release lock

   // Send out a message that the describes the changed input devices.
   if (inputDevicesChanged) {
       mPolicy->notifyInputDevicesChanged(inputDevices);
   }

   ……
   mQueuedListener->flush();
}

接下来我们就学习它具体的处理流程吧。

processEventsLocked对Input事件进行处理、归类

在processEventsLocked函数中,它主要做了以下事情:

  1. 循环获取RawEvent

  2. 如果RawEvent->type小于FIRST_SYNTHETIC_EVENT,说明这是个来自kernel的Input事件,则调用processEventsForDeviceLocked函数处理

  3. 否则此时的RawEvent->type就代表着Input设备的增、删、扫描事件,则调用对应的设备处理函数进行处理

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) {
           ……
           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 happen
                    break;
           }
       }
       count -= batchSize;
       rawEvent += batchSize;
   }
}
Input事件处理函数processEventsForDeviceLocked

在processEventsForDeviceLocked函数中,如果设备已注册,且来自它的事件不需要忽略,则调用device->process让该device处理Input事件。

void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) {
   ssize_t deviceIndex = mDevices.indexOfKey(deviceId);    
   if (deviceIndex < 0) {
       ALOGW("Discarding event for unknown deviceId %d.", deviceId);        
       return;
   }

   InputDevice* device = mDevices.valueAt(deviceIndex);    
   if (device->isIgnored()) {        
       //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
       return;
   }

   device->process(rawEvents, count);
}
Input事件归类处理InputMapper->process

在InputDevice中,存储着许多InputMapper,每种InputMapper对应一类Device,例如:Touch、Keyboard、Vibrator等等……而调用InputDevice的process函数,就是将Input事件传递给每一个InputMapper,匹配的InputMapper就会对Input事件进行处理,不匹配的则会忽略。

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
……    for (size_t i = 0; i < numMappers; i++) {
       InputMapper* mapper = mMappers[i];
       mapper->process(rawEvent);
   }
……
}

这里为了方便学习,我们以按键类事件为例继续探索,即KeyboardInputMapper。进入到它的process函数,可以看到,当RawEvent->type为EV_KEY时,说明是按键类Input事件,则调用processKey对它进行处理。

进入到processKey函数,它主要做了以下事情:

  1. 调用EventHub的mapKey函数根据deviceId、scanCode、usageCode得到keyCode、keyMetaState、policyFlags

  2. 对按键事件进行预处理(按下、按起的记录和逻辑判断)

  3. 将按键事件相关的信息封装到NotifyKeyArgs中(deviceId、when、policyFlags、down还是up、keyCode等等……)

  4. 最终调用getListener()->notifyKey,这里getListener得到的就是InputDispatcher

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
       int32_t usageCode) {    
   if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                             &keyCode, &keyMetaState, &policyFlags)) {
       keyCode = AKEYCODE_UNKNOWN;
       keyMetaState = mMetaState;
       policyFlags = 0;
   }    
   if (down) {
   ……
   } else {
   ……
   }

   ……    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
           down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
           AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
   getListener()->notifyKey(&args);
}

而这里的getListener得到的其实就是封装后的InputDispatcher,即QueuedInputListener:

InputReader.cpp
InputReader::InputReader(const sp& eventHub, const sp& policy, const sp& listener) :
       mContext(this), mEventHub(eventHub), mPolicy(policy),
       mGlobalMetaState(0), mGeneration(1),
       mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
       mConfigurationChangesToRefresh(0) {
   mQueuedListener = new QueuedInputListener(listener);
……
}

它的定义:

InputListener.cpp

QueuedInputListener::QueuedInputListener(const sp& innerListener) :
       mInnerListener(innerListener) {
}

所以调用getListener()->notifyKey(&args)调用的是QueuedInputListener的notifyKey函数,它里边有许多notifyXXX函数,做的事情都是将NotifyXXXArgs放入它的mArgsQueue队列中存储,等待处理。

调用getInputDevicesLocked获取输入设备信息

记得在processEventsLocked函数中,当rawEvent->type大于等于EventHubInterface::FIRST_SYNTHETIC_EVENT时会执行什么吗?接下来就让我们一起学习Input设备的增删:

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) {
       ……
       } 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 happen
               break;
           }
       }
       ……
   }
}

事实上这三个函数做的事情都很简单,我就不贴代码了,以addDeviceLocked为例,是非常常见的业务逻辑了:

  1. 判断设备是否已添加,若否则向下执行

  2. 根据deviceId从EventHub中获取identifier、classe、controllerNumber,调用createDeviceLocked将这些信息封装到InputDevice中

  3. 初始化InputDevice

  4. 将新创建的InputDevice添加到mDevice中存储

了解了这些后,看getInputDevicesLocked函数就会觉得非常简单了,它就是把mDevices中不需要被忽略的InputDevice取出来放入参数outInputDevices,也就是loopOnce中的inputDevices。

InputDevice添加完成后,调用InputManagerService的notifyInputDevicesChanged函数通知系统输入设备信息需要更新。

处理设备的增删,预处理、归类事件,将事件放入事件队列,通知系统更新设备信息后,当然就是要通知InputDispatcher取出事件队列中的事件进行处理了。

调用mQueuedListener->flush()通知InputDispatcher处理事件

调用mQueuedListener->flush()其实就是循环取出NotifyArgs列表中的NotifyArg并调用它的notify函数通知mInnerListener处理它。根据我们前面的分析可知,这里的mArgsQueue存储的就是前面存储的等待处理的NotifyXXXArgs。

InputListener.cppvoid 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();
}

在flush中,循环取出队列里的NotifyXXXArgs(有多种,例如NotifyKeyArgs、NotifyMotionArgs),调用它的notify函数通知mInnerListener,也就是InputReader创建时传入的listener。最终会调用mInnerListener的notifyXXX函数。

Input事件的分发者InputDispatcher

95cf57b7ebd06b9578519173f493ab39.png

调用InputDispatcher的notifyKey处理按键事件

进入到InputDispatcher的notifyKey函数,它做了以下事情:

  1. 验证NotifyArgs的有效性

  2. 对policyFlags和flag进行预处理

  3. 如果keycode为AKEYCODE_HOME则根据action设置’sys.domekey.down’属性(这属性貌似是指纹在用?)

  4. 对按键的down和up作一个处理以保证动作的连续性,做的事情大致上是:如果按键不是home或back,先以keycode为键,包含keycode、deviceId的KeyReplacement struct为值将按键信息添加到mReplacedKey里边,同时将metaState设为off,避免后面相同按键的重复判断。之后在up的时候,根据当前的keycode和deviceId取出之前cache的keycode并移除该KeyReplacement,同时恢复metaState,表示一次按键动作的完成。

  5. 将NotifyKeyArgs和keyCode、flag、metaState等封装到KeyEvent中

  6. 将KeyEvent经由InputManagerService交给PhoneWindowManager判断是否要在放入事件队列前拦截

  7. 判断是否要把事件发送到InputFilte中过滤

  8. 将NotifyArgs和flags、keyCode、repeatCount、metaState、policyFlags封装为KeyEntry,并加入事件处理队列inboundQueue

  9. 最后唤醒InputDispatcher线程的Looper,让它循环读取inboundQueue中的事件进行分发

InputDispatcher.cppvoid InputDispatcher::notifyKey(const NotifyKeyArgs* args) {    if (!validateKeyEvent(args->action)) {        return;
   }

   ……

   KeyEvent event;
   event.initialize(args->deviceId, args->source, args->action,
           flags, keyCode, args->scanCode, metaState, 0,
           args->downTime, args->eventTime);

       bool needWake;
   { // acquire lock
       mLock.lock();

   ……

       int32_t repeatCount = 0;
       KeyEntry* newEntry = new KeyEntry(args->eventTime,
               args->deviceId, args->source, policyFlags,
               args->action, flags, keyCode, args->scanCode,
               metaState, repeatCount, args->downTime);

       needWake = enqueueInboundEventLocked(newEntry);
       mLock.unlock();
   } // release lock

   if (needWake) {
       mLooper->wake();
   }
}

InputDispatcher的监听线程InputDispatcherThread

来到InputDispatcherThread的threadLoop函数,可以看到里面就是循环调用InputDispatcher的dispatchOnce()函数,它做了以下事情:

  1. 唤醒InputDispatcher线程继续分发操作

  2. 判断commandQueue是否为空,为空执行dispatchOnceInnerLocked

  3. 否则继续执行commandQueue里的命令

  4. 执行完commandQueue中的命令后休眠timeoutMillis时间

InputDispatcher.cpp

bool InputDispatcherThread::threadLoop() {
   mDispatcher->dispatchOnce();    
   return true;
}

void InputDispatcher::dispatchOnce() {
   nsecs_t nextWakeupTime = LONG_LONG_MAX;
   { // acquire lock
       AutoMutex _l(mLock);
       mDispatcherIsAliveCondition.broadcast();        // Run a dispatch loop if there are no pending commands.
       // The dispatch loop might enqueue commands to run afterwards.
       if (!haveCommandsLocked()) {
           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()) {
           nextWakeupTime = LONG_LONG_MIN;
       }
   } // release lock

   // 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);
}

来到dispatchOnceInnerLocked函数,它做了以下事情:

  1. 当设备处于非交互状态(即将休眠),为了确保设备的按键链正确,会将当前KeyRepeatState持有的lastKeyEntry释放并置空(resetKeyRepeatLocked函数)

  2. 对App切换进行优化,如果App切换时间小于nextWakeUpTime,就将appSwitchDueTime设为nextWakeUpTime,丢弃其他事件。

  3. 从事件队列中取出事件,调用pokeUserActivityLocked函数让PowerManagerService唤醒设备,避免让设备进入休眠

  4. 重设ANR计时

  5. 如果事件需要丢弃,则设置dropReason

至此准备工作就做完了,最后就把pendingEvent交给对应的dispatchXXXLocked函数分发,例如这里就是交给dispatchKeyLocked函数。

按键事件分发处理函数dispatchKeyLocked

进入到dispatchKeyLocked函数中,它做了以下事情:

  1. 处理按键重复

  2. 标记事件是否为长按事件

  3. 标记事件开始进行分发

  4. 判断是否需要拦截事件,拦截的话进行处理

  5. 调用findFocusedWindowTargetsLocked函数判断发生按键事件的Window并得到对应的inputTargets

  6. 调用addMonitoringTargetsLocked函数监控这些InputTarget的InputChannel

  7. 最后调用dispatchEventLocked分发按键事件

InputDispatcher与ANR的关联

findFocusedWindowTargetsLocked函数中有一点细节是需要关注的,就是里边的handleTargetsNotReadyLocked函数,它在focusedWindowHandle为空且focusedApplicationHandle不为空的时候,或checkWindowReadyForMoreInputLocked返回值为false(表示目前Window还没有准备好接收更多Input事件)时被调用。在这个方法里面涉及到onANRLocked的调用,它会触发ANR。里面做的事情大致是:

如果applicationHandle和windowHandle都为空,且inputTargetWaitCause(Input事件等待的原因)不是INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY(系统还没准备好),那么更新inputTargetWaitCause为INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,记录mInputTargetWaitStartTime(等待起始时间)为currentTime,超时时间设为无限大,mInputTargetWaitTimeoutExpired设为false,清空mInputTargetWaitApplicationHandle(等待窗口的ApplicationHandle队列)。

如果两者有一不为空且系统已经准备好,如果windowHandle不为空,timeout(超时时间)为windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT)的返回值;如果applicationHandle不为空,timeout(超时时间)为applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT)。然后记录mInputTargetWaitStartTime(等待起始时间)为currentTime,mInputTargetWaitTimeoutTime超时时间设为起始时间加timeout。然后如果windowHandle不为空则将mInputTargetWaitApplicationHandle设为windowHandle中保存的inputApplicationHandle;否则如果mInputTargetWaitApplicationHandle为空且inputApplicationHandle不为空,则将mInputTargetWaitApplicationHandle设为inputApplicationHandle。

如果currentTime大于mInputTargetWaitTimeoutTime,说明事件的等待超时了,就会执行onARNLocked函数,在里边进行ANR相关的处理。

分发处理InputTarget的dispatchEventLocked函数

在dispatchEventLocked函数中,再次调用pokeUserActivityLocked避免设备进入休眠状态,然后取出InputTargets里面的InputTarget,先调用getConnectionIndexLocked函数获得InputTarget对应的Connection,再调用prepareDispatchCycleLocked函数向Window分发按键事件,最后调用到enqueueDispatchEntriesLocked。

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
       EventEntry* eventEntry, const Vector& inputTargets) {
#if DEBUG_DISPATCH_CYCLE    ALOGD("dispatchEventToCurrentInputTargets");
#endif        ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
   pokeUserActivityLocked(eventEntry);        for (size_t i = 0; i < inputTargets.size(); i++) {                const InputTarget& inputTarget = inputTargets.itemAt(i);
       ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);                if (connectionIndex >= 0) {
           sp connection = mConnectionsByFd.valueAt(connectionIndex);
           prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
       } else {
#if DEBUG_FOCUS                ALOGD("Dropping event delivery to target with channel '%s' because it ""is no longer registered with the input dispatcher.",
                   inputTarget.inputChannel->getName().string());
#endif
       }
   }
}

在这里有一点要注意的是,为什么是判断多个InputTarget,因为对于KeyEvent来说,一个InputTarget可能就够了,但对于TouchEvent来说,就会出现同时触控多个InputTarget的情况。

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
   bool wasEmpty = connection->outboundQueue.isEmpty();    // 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.isEmpty()) {
       startDispatchCycleLocked(currentTime, connection);
   }
}

在enqueueDispatchEntriesLocked函数中,首先调用enqueueDispatchEntryLocked将事件的flag与给出的InputTarget的flag匹配,匹配成功的事件(只能匹配一个flag)再次封装,成为DispatchEntry,根据事件的type对DispatchEntry的resolvedAction赋值,添加到该窗口(InputTarget关联着某个窗口)的outboundQueue队列的队尾,最后留个log记录Connection和Window当前的信息。

将事件分发到对应窗口的outboundQueue队列中后,调用startDispatchCycleLocked循环处理outboundQueue队列中的事件,它做了以下事情:

  1. 取出队头的DispatchEntry

  2. 根据事件类型通过Connection的inputPublisher的publishXXXEvent函数将事件信息封装到InputMessage中,通过InputChannel发送InputMessage到对应窗口。

这里有一点细节,事件不仅仅会发到对应InputTarget中,还会异步通过另一个InputChannel将DispatchEntry发送到InputManagerService一个监控InputTarget中。该监控InputTarget什么都不会做,只是默默监控。第三方可以做一些自己的特殊事件监听(例如组合按键、手势等)。

完成上面的操作后,将DispatchEntry从outboundQueue中取出来放到waitQueue中,当publish出去的事件被处理完成(finished),InputManagerService就会从应用中得到一个回复,此时就会取出waitQueue中的事件。此外,还会计算事件的处理时间以判断是否要抛出ANR,相关知识会在后面解释。

至此,Input事件的产生、读取和分发流程就串联起来了,后面还将继续学习Input事件经InputDispatcher分发后是如何到达应用的。如果大家对AOSP源码分析系列感兴趣,欢迎继续关注!

欢迎关注

40d1e025722305c12b3ce91fc7bd6bdb.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值