«

Android系统触屏事件传递派发浅析

时间:2024-3-2 17:32     作者:韩俊     分类: Android


之前浅显的看过事件传递的过程,但是有一些细节还是不太清除,借这次机会,可以好好的整理一下之前没有想清楚的地方.(基于android 5.0源码),记录一下事件分发流程.

  1. 准备InputManagerService和WindowManagerService

SystemServer中new一个InputManagerSerivce实例, 并将其作为一个输入参数, new 一个WindowManagerService实例, 将他们都注册到ServiceManager中.

看看InputManagerService的构造方法

public InputManagerService(Context context) {
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
    Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, new LocalService());
}

调用了nativeInit()方法返回一个长整型,跟进nativeInit()方法

 static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
 jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
 if (messageQueue == NULL) {
     jniThrowRuntimeException(env, "MessageQueue is not initialized.");
     return 0;
 }
 NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
         messageQueue->getLooper());
 im->incStrong(0);
 return reinterpret_cast<jlong>(im);
 }

原来该整型保存的是NativeInputManager实例的指针.

  1. NativeInputManager实例化InputManager

看看NativeInputManager的构造方法

NativeInputManager::NativeInputManager(jobject contextObj,
      jobject serviceObj, const sp<Looper>& looper) :
      mLooper(looper), mInteractive(true) {
  // ...
  sp<EventHub> eventHub = new EventHub();
  mInputManager = new InputManager(eventHub, this, this);
  }

3 InputManager准备实际工作类

看看InputManager的构造方法

InputManager::InputManager(
      const sp<EventHubInterface>& eventHub,
      const sp<InputReaderPolicyInterface>& readerPolicy,
      const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
  mDispatcher = new InputDispatcher(dispatcherPolicy);
  mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
  initialize();
}

InputManager会构造两个实际工作的实例, InputDispather和InputReader两个实例.故名思意,InputReader是用来读取事件,InputDispatcher用来向上层框架和应用分发事件,当然InputDispatcher也会根据策略处理一些事件或者丢弃一些事件.这个类比较简单,就是以InputReader 和 InputDispatcher为参数分别创建两个线程, 线程函数中分别调用InputReader.loopOnce()方法和InputDispatcher.disptachOnce()方法.

4 InputReader 一直运行的工厂

InputReader主要工作是一直获取事件,处理加工事件.

4.1 EventHub 辛勤的小蜜蜂

EventHub主要功能是管理设备文件,对设备分类,读取事件.
EventHub中的getEvent()方法流程比较繁琐,用inotify接口来监控/dev/input目录,观察是否有新设备插入和设备移除, 并打开目录下的所有文件,封装成Device结构,并创建了管道用于唤醒睡眠线程, 使用epoll管理所有的文件描述符.主要的功能是从/dev/input目录下的设备文件读取事件,该方法会一直阻塞,直到有用户事件才会返回. 第一次调用该方法会扫描该目录下的所有文件,获取设备名,设备版本,物理路径,id和设备类型等信息.

4.2 InputReader构造函数

InputReader(const sp<EventHubInterface>& eventHub,
    const sp<InputReaderPolicyInterface>& policy, 
    const sp<InputListenerInterface>& listener); 

可以发现InputReader构造函数需要传入EventHub, InputReaderPolicyInterface, InputListenerInterface.
InputReader构造方法最后一个参数是mDispatcher, mReader中用mQueuedListener来保存.

InputReader主要用来读取和处理事件,具体的处理是由InputMapper及其子类的process方法来完成, 在loopOnce方法中会调用EventHub的getEvents方法不断读取原始事件,并对事件分类,设置不同的InputMapper, 最后调用mQueueListener->flush()方法.

void InputReader::loopOnce() {
size_t count = mEventHub->getEvents(timeoutMills, mEventBuffer, EVENT_BUFFER_SIZE);
processEventsLocked(mEventBuffer, count);
timeoutExpiredLocked(now);
mQueueListener->flush();
}

processEventLocked()方法中, 第一次调用loopOnce方法会调用 addDeviceLocked(), addDeviceLocked() 会调用createDeviceLocked()方法.

InputDevice* InputReader::createDeviceLocked(
    int32_t deviceId, 
    int32_t controllerNumber, 
    const InputDeviceIdentifier& identifier, 
    uint32_t classes) {
// ...
// Touchscreens and touchpad devices.
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
    device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        device->addMapper(new SingleTouchInputMapper(device));
   }
// ...
}

createDeviceLocked()方法会根据设备类型,添加不同的InputMapper. processEventLocked()方法中,不是第一次调用loopOnce时会调用 processEventsForDeviceLocked().
该方法直接调用每个InputDevice的process方法.

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

跟进InputDevice的process方法

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
    if (mDropUntilNextSync) {
        if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
            mDropUntilNextSync = false;
        } else {
        }
    } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
        ALOGI("Detected input event buffer overrun for device %s.", getName().string());
        mDropUntilNextSync = true;
        reset(rawEvent->when);
    } else {
        for (size_t i = 0; i < numMappers; i++) {
            InputMapper* mapper = mMappers[i];
            mapper->process(rawEvent);
        }
    }
}
}

可以发现InputDevice.process()方法最终是调用InputMapper的process方法.
class InputMapper 是用来处理加工原始事件, 表示可以处理某一相同类型的事件.
常见的InputMapper有SwitchInputMapper, KeyboardInputMapper, TrackballInputMapper, MultiTouchInputMapper和SingleTouchInputMapper.
InputReader处理完原始事件后构造了一个NofifyArgs, NotifyArgs也有很多子类,如NotifySwitchArgs, NotifyMotionArgs, NotifyKeyArgs等.
通过调用getListener()->notfiy()方法把它投入mQueueListener中. mQueueListener 是类QueuedInputListener的实例.

看看QueuedInputListener的几个方法
构造方法的innerListener参数也是就是InputReader构造方法的第三个参数,也就是InputDispathcer的实例.

QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
     mInnerListener(innerListener) {
}

在InputReader中调用getListener->ntotifyMotion()方法就是调用notifyMotion方法, 在InputReader中构造的NotifyMotionArgs结构是保存在栈上的,所以这里需要保存在堆上,然后入队.

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
 mArgsQueue.push(new NotifyMotionArgs(*args));
}

InputReader loopOnce最后调用的 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();
}

void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
     listener->notifyMotion(this);
 }

5忙碌的事件快递员

InputDispatcher主要作用是派送InputReader放在队列上的事件,当然,也会处理一些事件或者丢弃一些事件.
NotifyArgs结构体里的listener就是InputDispathcer, 此时函数调用进入了InputDispathcer.

5.1 notifyMotion方法

看看InputDispathcerde notfiyMotion方法

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
// here is debugging info I removed
if (!validateMotionEvent(args->action, args->pointerCount, args->pointerProperties)) {
    return;
}
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
bool needWake;
{ // acquire lock
    mLock.lock();
    if (shouldSendMotionToInputFilterLocked(args)) {
        mLock.unlock();
        MotionEvent event;
        event.initialize(args->deviceId, args->source, args->action, args->flags,
                args->edgeFlags, args->metaState, args->buttonState, 0, 0,
                args->xPrecision, args->yPrecision,
                args->downTime, args->eventTime,
                args->pointerCount, args->pointerProperties, args->pointerCoords);
        policyFlags |= POLICY_FLAG_FILTERED;
        if (!mPolicy->filterInputEvent(&event, policyFlags)) {
            return; // event was consumed by the filter
        }
        mLock.lock();
    }
    // Just enqueue a new motion event.
    MotionEntry* newEntry = new MotionEntry(args->eventTime,
            args->deviceId, args->source, policyFlags,
            args->action, args->flags, args->metaState, args->buttonState,
            args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
            args->displayId,
            args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
    needWake = enqueueInboundEventLocked(newEntry);
    mLock.unlock();
} // release lock
if (needWake) {
    mLooper->wake();
}
}   

删除了调试信息和空行后,就剩下这么多了.流程还是好理解, 首先验证NotifyMotionArgs的有效性, 如何验证没有跟进去看, 然后判断是否入队, 再是判断事件是否过滤, 通过了上面3个检查后, 提取NotifyMotionArgs的字段,构造MotionEntry实例, 入队,唤醒其他线程.
至此, 一个触屏事件从获取到加工全部完成,现在已经入队,那是不是准备分发给应用程序了呢? 其实还有会儿.

5.2 enqueueInboundEventLock 方法

跟进 enqueueInboundEventLocked 这个方法看看

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
traceInboundQueueLengthLocked();
switch (entry->type) {
case EventEntry::TYPE_KEY: {
    // I have removed. I just care about MotionEvent
    break;
}
case EventEntry::TYPE_MOTION: {
    // Optimize case where the current application is unresponsive and the user
    // decides to touch a window in a different application.
    // If the application takes too long to catch up then we drop all events preceding
    // the touch into the other window.
    MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
    if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
            && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
            && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
            && mInputTargetWaitApplicationHandle != NULL) {
        int32_t displayId = motionEntry->displayId;
        int32_t x = int32_t(motionEntry->pointerCoords[0].
                getAxisValue(AMOTION_EVENT_AXIS_X));
        int32_t y = int32_t(motionEntry->pointerCoords[0].
                getAxisValue(AMOTION_EVENT_AXIS_Y));
        sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
        if (touchedWindowHandle != NULL
                && touchedWindowHandle->inputApplicationHandle
                        != mInputTargetWaitApplicationHandle) {
            // User touched a different application than the one we are waiting on.
            // Flag the event, and start pruning the input queue.
            mNextUnblockedEvent = motionEntry;
            needWake = true;
        }
    }
    break;
}
}
return needWake;
}

看到了findTouchedWindowAtLocked(displayId, x, y) 这个方法后,感觉把事件派发给应用很近了. 这里的displayId 是在InputReader中设置的, 取值有ADISPLAY_ID_DEFAULT和ADISPLAY_ID_NONE.
InputDispatcherThread的线程函数是InputDispatcher类的dispatchOnce()方法.

5.3 dispatchOnce 方法

看看dispatchOnce

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

5.4 dispatchOnceInnerLocked方法

// Poke user activity for this event.
      if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
          pokeUserActivityLocked(mPendingEvent);
      }
      // Get ready to dispatch the event.
      resetANRTimeoutsLocked();

看下处理触屏事件的处理逻辑

    case EventEntry::TYPE_MOTION: {
    MotionEntry* typedEntry = static_cast<MotionEntry*> (mPendingEvent);`
    if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
         dropReason = DROP_REASON_APP_SWITCH;
      }
     if (dropReason == DROP_REASON_NOT_DROPPED
             && isStaleEventLocked(currentTime, typedEntry)) {
         dropReason = DROP_REASON_STALE;
     }
     if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
         dropReason = DROP_REASON_BLOCKED;
     }
      done = dispatchMotionLocked(currentTime, typedEntry,
            &dropReason, nextWakeupTime);
     break;
     }
  if (done) {
     if (dropReason != DROP_REASON_NOT_DROPPED) {
          dropInboundEventLocked(mPendingEvent, dropReason);
      }
     releasePendingEventLocked();
   *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
   }
   void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
  #if DEBUG_DISPATCH_CYCLE
 ALOGD("dispatchEventToCurrentInputTargets");
 #endif
  ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to t     rue
  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> connection = mConnectionsByFd.valueAt(connectionIndex);
         prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTar     get);
   } 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的inputChannel取得connection, 然后调用prepareDispatchCycleLocked方法. 关于inputChannel 和 Connection 后面再介绍. prepareDispatchCycleLocked方法最后调用enqueueDispatchEntriesLocked方法.

5.5 enqueueDispatchEntriesLocked方法

看enqueueDispatchEntriesLocked方法

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
    const sp<Connection>& 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);
}    
}   

这个方法首先调用enqueueDispatchEntryLock方法,enqueueDispatchEntryLock方法第四个参数传入的是一些标识请求模式的整型值,整型值具体代表什么意思,以后再分析,然后将EventEntry入队,最后调用startDispatchCycleLocked .

5.6 startDispatchCycleLocked

一路跟踪startDispatchCycleLocked

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
    const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ startDispatchCycle",
        connection->getInputChannelName());
 #endif

while (connection->status == Connection::STATUS_NORMAL
        && !connection->outboundQueue.isEmpty()) {
    DispatchEntry* dispatchEntry = connection->outboundQueue.head;
    dispatchEntry->deliveryTime = currentTime;

    // Publish the event.
    status_t status;
    EventEntry* eventEntry = dispatchEntry->eventEntry;
    switch (eventEntry->type) {
   case EventEntry::TYPE_MOTION: {
        MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
            // remove some
        }

        // Publish the motion event.
        status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
                motionEntry->deviceId, motionEntry->source,
                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
                xOffset, yOffset,
                motionEntry->xPrecision, motionEntry->yPrecision,
                motionEntry->downTime, motionEntry->eventTime,
                motionEntry->pointerCount, motionEntry->pointerProperties,
                usingCoords);
        break;
    }

    default:
        ALOG_ASSERT(false);
        return;
    }
    // Re-enqueue the event on the wait queue.
    connection->outboundQueue.dequeue(dispatchEntry);
    traceOutboundQueueLengthLocked(connection);
    connection->waitQueue.enqueueAtTail(dispatchEntry);
    traceWaitQueueLengthLocked(connection);
}
}

发现触屏事件最终是调用connection->inputPublisher.publishMotionEvent方法,这个方法参数有17个. 阅读最后几行代码,可以看到InputDispatcher中队列比较多.
到目前为止,InputDispatcher分发完成了.下一步应该就是通知应用来读取了.

分析应用读取事件之前,不得不看看connection的结构,以及它从哪里来.
connection是从mConnectionsByFd中检索出来的.在registerInputChannel方法中找到了

sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);

if (monitor) {
   mMonitoringChannels.push(inputChannel);
}

Connection是定义在InputDispatcher内部的类,看看定义

 class Connection : public RefBase {
    protected:
    virtual ~Connection();

    public:
    enum Status {
        // Everything is peachy.
        STATUS_NORMAL,
        // An unrecoverable communication error has occurred.
        STATUS_BROKEN,
        // The input channel has been unregistered.
        STATUS_ZOMBIE
    };

    Status status;
    sp<InputChannel> inputChannel; // never null
    sp<InputWindowHandle> inputWindowHandle; // may be null
    bool monitor;
    InputPublisher inputPublisher;
    InputState inputState;

注意有两个陌生的类 InputChannel 和 InputPublisher.

InputPubliser在InputTransport.h文件中的定义(去掉了参数)

class InputPublisher {
public:
explicit InputPublisher(const sp<InputChannel>& channel);
~InputPublisher();
inline sp<InputChannel> getChannel() { return mChannel; }
status_t publishKeyEvent()
status_t publishMotionEvent()
status_t receiveFinishedSignal();

private:
sp<InputChannel> mChannel;
};

InputChannel也是在InputTransport.h文件中的定义,只看看openInputChannelPair函数的声明

class InputChannel : public RefBase {
public:
   /* Creates a pair of input channels.
    *
    * Returns OK on success.
    */
 static status_t openInputChannelPair(const String8& name,
         sp<InputChannel>& outServerChannel, 
         sp<InputChannel>& outClientChannel);

};

感觉像是一对socket. 在WindowManagerService的addWindow方法中发现了同名的方法.

public int addWindow() {
// ...
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);     inputChannels[1].transferTo(outInputChannel);             mInputManager.registerInputChannel(win.mInputChannel, 
win.mInputWindowHandle);
// ...
 }

到了connection->inputPublisher.publishMotionEvent这里,InputDispatcher已经完成了事件的分发,等待窗口处理了.

下一篇文章将记录从应用程序角度来分析publishMotionEvent方法.

标签: android

热门推荐