Input事件流程
目录 |
一、开机时input机制创建
开机时会创建 system_server,然后在其中创建service,下面这里是 WindowManagerService的创建。
这里没有显式的使用 new创建,而是调用了类中静态函数 WindowManagerService.main,同时我们也看到 WindowManagerService的构建函数为private类型:
private WindowManagerService(Context context, PowerManagerService pm, DisplayManagerService displayManager, InputManagerService inputManager, Handler uiHandler, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) ...
其实这是非常常见的单实例类,这里不过多描述,继续看:
SystemServer.java
class ServerThread extends Thread { ... @Override public void run() { ... // Critical services... try { Slog.i(TAG, "Entropy Service"); ServiceManager.addService("entropy", new EntropyService()); ... Slog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, power, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL, !firstBoot); //HERE ServiceManager.addService(Context.WINDOW_SERVICE, wm); ...
进入该 main函数:
WindowManagerService.java
public static WindowManagerService main(Context context, PowerManagerService pm, boolean haveInputMethods, boolean allowBootMsgs) { WMThread thr = new WMThread(context, pm, haveInputMethods, allowBootMsgs); //HERE thr.start(); //HERE synchronized (thr) { while (thr.mService == null) { try { thr.wait(); } catch (InterruptedException e) { } } return thr.mService; } }
创建了WMThread并start,呵呵,看到start应该条件反射地想到run了:
static class WMThread extends Thread { ... public void run() { Looper.prepare(); WindowManagerService s = new WindowManagerService(mContext, mPM, mHaveInputMethods, mAllowBootMessages); //HERE ... synchronized (this) { mService = s; notifyAll(); } Looper.loop(); } }
果然,是在这里创建了 WindowManagerService,继续跟进:
private WindowManagerService(Context context, PowerManagerService pm, boolean haveInputMethods, boolean showBootMsgs) { ... mInputManager = new InputManager(context, this); //HERE PolicyThread thr = new PolicyThread(mPolicy, this, context, pm); ... }
嗯,终于看到与 "Input"相关的东东了:InputManager,看名字应该是 Input事件的管理类。
InputManager.java
public InputManager(Context context, WindowManagerService windowManagerService) { this.mContext = context; this.mWindowManagerService = windowManagerService; this.mCallbacks = new Callbacks(); Looper looper = windowManagerService.mH.getLooper(); Slog.i(TAG, "Initializing input manager"); nativeInit(mContext, mCallbacks, looper.getQueue()); //HERE // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); }
构建函数很简单,无非是一些成员变量的赋值,除此之外就是两个函数:nativeInit和Watchdog.getInstance().addMonitor(this)。第一个函数我们要深入分析才知道干了些啥;第个二则是将此实例加入Watchdog的监视列表mMonitors.
com_android_server_InputManager.cpp
{ "nativeInit", "(Landroid/content/Context;" "Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V", (void*) android_server_InputManager_nativeInit }, ...
static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz, jobject contextObj, jobject callbacksObj, jobject messageQueueObj) { if (gNativeInputManager == NULL) { sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper); //HERE } else { LOGE("Input manager already initialized."); jniThrowRuntimeException(env, "Input manager already initialized."); } }
可以看到开始转入了 native环境,并在这里创建了 NativeInputManager,同时把 mContext, mCallbacks 和 looper.getQueue()作为参数传入其中。
NativeInputManager::NativeInputManager(jobject contextObj, jobject callbacksObj, const sp<Looper>& looper) : mLooper(looper) { JNIEnv* env = jniEnv(); ... sp<EventHub> eventHub = new EventHub(); //HERE mInputManager = new InputManager(eventHub, this, this); //HERE }
哦,原来 native层也有 InputManager
InputManager.cpp
InputManager::InputManager( const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); //HERE initialize(); }
void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); //HERE mDispatcherThread = new InputDispatcherThread(mDispatcher); //HERE }
可以看到这里又创建了 InputDispatcher、InputReader的实例,同时还创建了两个线程:InputReaderThread和 InputDispatcherThread.
——这几个对象的关系是什么?我们继续分析
二、input事件的扫描
首先来看 InputReaderThread:
InputReader.h
/* Reads raw events from the event hub and processes them, endlessly. */ class InputReaderThread : public Thread { public: InputReaderThread(const sp<InputReaderInterface>& reader); virtual ~InputReaderThread(); private: sp<InputReaderInterface> mReader; virtual bool threadLoop(); //HERE };
InputReader.cpp
bool InputReaderThread::threadLoop() { mReader->loopOnce(); //HERE return true; }
void InputReader::loopOnce() { int32_t timeoutMillis; { // acquire lock AutoMutex _l(mLock); uint32_t changes = mConfigurationChangesToRefresh; if (changes) { mConfigurationChangesToRefresh = 0; refreshConfigurationLocked(changes); } timeoutMillis = -1; if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); } } // release lock size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); //HERE { // acquire lock AutoMutex _l(mLock); if (count) { processEventsLocked(mEventBuffer, count); } if (!count || timeoutMillis == 0) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); mNextTimeout = LLONG_MAX; timeoutExpiredLocked(now); } } // release lock mQueuedListener->flush(); }
我们看这一行:mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
没错,这就是最为关键的一行!
mEventHub是什么东东?如果读者留心的话,会发现在上面已经出现过了,在 NativeInputManager的构建函数中。至于这个具体是干什么用的,这里只要看其中的三个函数就能了解个大概:
EventHub.cpp
EventHub::EventHub(void) : mBuiltInKeyboardId(-1), mNextDeviceId(1), mOpeningDevices(0), mClosingDevices(0), mNeedToSendFinishedDeviceScan(false), mNeedToReopenDevices(false), mNeedToScanDevices(true), mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mNumCpus = sysconf(_SC_NPROCESSORS_ONLN); mEpollFd = epoll_create(EPOLL_SIZE_HINT); //epoll就不用解释了吧,linux标准的东西 LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); mINotifyFd = inotify_init(); //inotify也是linux的东西,就是它能够监视某一文件,当其被创建、修改、读写时会有反映在生成的文件 mINotifyFd上 int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); //这里是监视DEVICE_PATH(/dev/input)的删除、创建操作 LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d", DEVICE_PATH, errno); struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.u32 = EPOLL_ID_INOTIFY; //然后我们通过epoll对 mINotifyFd进行I/O侦听便可完成对 /dev/input/下文件的个数变化,如增加输入设备时可以及时加入监听列表中 result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); int wakeFds[2]; result = pipe(wakeFds); LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1]; result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", errno); result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", errno); eventItem.data.u32 = EPOLL_ID_WAKE; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); //这里也是一样,增加了侦听mWakeReadPipeFd,暂且不看吧 LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", errno); } ... size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { ... // Grab the next input event. bool deviceChanged = false; while (mPendingEventIndex < mPendingEventCount) { //被侦听的文件有事件发生,可能是notifyId、pipefd或input设备文件 const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; //取出 if (eventItem.data.u32 == EPOLL_ID_INOTIFY) { //为notifyid,表示input设备文件被移除或添加 if (eventItem.events & EPOLLIN) { mPendingINotify = true; //设置标志,后续重新扫描/dev/input/下文件 } else { LOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); } continue; } if (eventItem.data.u32 == EPOLL_ID_WAKE) { //被唤醒,上面侦听 mWakeReadPipeFd的返回,先不看 if (eventItem.events & EPOLLIN) { LOGV("awoken after wake()"); awoken = true; char buffer[16]; ssize_t nRead; do { nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); } else { LOGW("Received unexpected epoll event 0x%08x for wake read pipe.", eventItem.events); } continue; } ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32); if (deviceIndex < 0) { LOGW("Received unexpected epoll event 0x%08x for unknown device id %d.", eventItem.events, eventItem.data.u32); continue; } Device* device = mDevices.valueAt(deviceIndex); //真正的input设备,开始取数据 if (eventItem.events & EPOLLIN) { int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity); //读出数据到readBuffer中 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { // Device was removed before INotify noticed. LOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n", device->fd, readSize, bufferSize, capacity, errno); deviceChanged = true; closeDeviceLocked(device); } else if (readSize < 0) { if (errno != EAGAIN && errno != EINTR) { LOGW("could not get event (errno=%d)", errno); } } else if ((readSize % sizeof(struct input_event)) != 0) { LOGE("could not get event (wrong size: %d)", readSize); } else { int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; size_t count = size_t(readSize) / sizeof(struct input_event); for (size_t i = 0; i < count; i++) { const struct input_event& iev = readBuffer[i]; LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d", device->path.string(), (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); #ifdef HAVE_POSIX_CLOCKS // Use the time specified in the event instead of the current time // so that downstream code can get more accurate estimates of // event dispatch latency from the time the event is enqueued onto // the evdev client buffer. // // The event's timestamp fortuitously uses the same monotonic clock // time base as the rest of Android. The kernel event device driver // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts(). // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a // system call that also queries ktime_get_ts(). event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL + nsecs_t(iev.time.tv_usec) * 1000LL; LOGV("event time %lld, now %lld", event->when, now); #else event->when = now; #endif event->deviceId = deviceId; event->type = iev.type; event->scanCode = iev.code; event->value = iev.value; event->keyCode = AKEYCODE_UNKNOWN; event->flags = 0; if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) { status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code, &event->keyCode, &event->flags); LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", iev.code, event->keyCode, event->flags, err); } event += 1; } capacity -= count; if (capacity == 0) { // The result buffer is full. Reset the pending event index // so we will try to read the device again on the next iteration. mPendingEventIndex -= 1; break; } } } else { LOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, device->identifier.name.string()); } } ... int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); //等待新事件 acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock if (pollResult == 0) { // Timed out. mPendingEventCount = 0; break; } if (pollResult < 0) { // An error occurred. mPendingEventCount = 0; // Sleep after errors to avoid locking up the system. // Hopefully the error is transient. if (errno != EINTR) { LOGW("poll failed (errno=%d)\n", errno); usleep(100000); } } else { // Some events occurred. mPendingEventCount = size_t(pollResult); // On an SMP system, it is possible for the framework to read input events // faster than the kernel input device driver can produce a complete packet. // Because poll() wakes up as soon as the first input event becomes available, // the framework will often end up reading one event at a time until the // packet is complete. Instead of one call to read() returning 71 events, // it could take 71 calls to read() each returning 1 event. // // Sleep for a short period of time after waking up from the poll() to give // the kernel time to finish writing the entire packet of input events. if (mNumCpus > 1) { usleep(250); } } } // All done, return the number of events we read. return event - buffer; } status_t EventHub::openDeviceLocked(const char *devicePath) { char buffer[80]; LOGV("Opening device: %s", devicePath); int fd = open(devicePath, O_RDWR); ... // Register with epoll. struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; //EPOLLIN :表示对应的文件描述符可以读; eventItem.data.u32 = deviceId; if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { //将 devicePath加入监听 ... mDevices.add(deviceId, device); device->next = mOpeningDevices; mOpeningDevices = device; return 0; }
而devicePath则正是/dev/input/目录下的文件,openDeviceLocked的被调用关系如下:
取其中一个函数看一下:
status_t EventHub::readNotifyLocked() { int res; char devname[PATH_MAX]; char *filename; char event_buf[512]; int event_size; int event_pos = 0; struct inotify_event *event; LOGV("EventHub::readNotify nfd: %d\n", mINotifyFd); res = read(mINotifyFd, event_buf, sizeof(event_buf)); //取出 mINotifyFd中的数据 if(res < (int)sizeof(*event)) { if(errno == EINTR) return 0; LOGW("could not get event, %s\n", strerror(errno)); return -1; } //printf("got %d bytes of event information\n", res); strcpy(devname, DEVICE_PATH); filename = devname + strlen(devname); *filename++ = '/'; while(res >= (int)sizeof(*event)) { event = (struct inotify_event *)(event_buf + event_pos); //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); if(event->len) { strcpy(filename, event->name); //获取文件列表文件名 if(event->mask & IN_CREATE) { openDeviceLocked(devname); //加入epoll监视列表 } else { LOGI("Removing device '%s' due to inotify event\n", devname); closeDeviceByPathLocked(devname); } } event_size = sizeof(*event) + event->len; res -= event_size; event_pos += event_size; } return 0; }
三、input事件的分发
从 mEventHub中取出数据后,就要想办法快速实时地传送给framework/app,这样才能达到人机交互的目的,现在我们就来看这一部分。
InputReader.cpp
void InputReader::loopOnce() { ... size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); if (count) { processEventsLocked(mEventBuffer, count); //HERE } ... } // release lock mQueuedListener->flush(); }
取出的事件以数组的形式、按顺序存在 mEventBuffer当中,count保存了所取出的事件的个数。
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); //HERE } else { switch (rawEvent->type) { case EventHubInterface::DEVICE_ADDED: //input设备有增加 addDeviceLocked(rawEvent->when, rawEvent->deviceId); break; case EventHubInterface::DEVICE_REMOVED: //input设备有减少 removeDeviceLocked(rawEvent->when, rawEvent->deviceId); break; case EventHubInterface::FINISHED_DEVICE_SCAN: //input设备扫描完成 handleConfigurationChangedLocked(rawEvent->when); break; default: LOG_ASSERT(false); // can't happen break; } } count -= batchSize; rawEvent += batchSize; } }
上面这三个case,与 inotify监听相对应,我们这里就常规的input事件分发继续分析:
void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) { ssize_t deviceIndex = mDevices.indexOfKey(deviceId); if (deviceIndex < 0) { LOGW("Discarding event for unknown deviceId %d.", deviceId); return; } InputDevice* device = mDevices.valueAt(deviceIndex); if (device->isIgnored()) { //LOGD("Discarding event for ignored deviceId %d.", deviceId); return; } device->process(rawEvents, count); //HERE }
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->scanCode == SYN_REPORT) { mDropUntilNextSync = false; } else { } } else if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_DROPPED) { mDropUntilNextSync = true; reset(rawEvent->when); } else { for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; mapper->process(rawEvent); //HERE, 将所有类型的mapper跑一遍 //ps.只有与rawEvent类型匹配的mapper才会真正作有意义的处理 //因为在所有的process中均会对rawEvent进行类型判断,只有匹配才作进一步处理 } } } }
上面所使用的mMapper[]数组在 iNotify监控到/dev/input/下文件有增加,进而 addDeviceLocked时会有以下操作:
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, const String8& name, uint32_t classes) { InputDevice* device = new InputDevice(&mContext, deviceId, name, classes); // External devices. if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { device->setExternal(true); } // Switch-like devices. if (classes & INPUT_DEVICE_CLASS_SWITCH) { device->addMapper(new SwitchInputMapper(device)); //开关类输入设备 } // 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) { device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType)); //键盘类设备 } // Cursor-like devices. if (classes & INPUT_DEVICE_CLASS_CURSOR) { device->addMapper(new CursorInputMapper(device)); //??尚不大清楚是何种输入设备 } // 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)); //单点触摸屏 } // Joystick-like devices. if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { device->addMapper(new JoystickInputMapper(device)); //手柄 } return device; }
(一)触屏事件
以下为触屏事件对应的mapper,我们以多点触摸屏为例:
InputReader.cpp
void MultiTouchInputMapper::process(const RawEvent* rawEvent) { TouchInputMapper::process(rawEvent); mMultiTouchMotionAccumulator.process(rawEvent); }
void TouchInputMapper::process(const RawEvent* rawEvent) { mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { sync(rawEvent->when); } }
从上面看来,只是将rawEvent->when所传入到sync当中,那其他信息呢?
在 MultiTouchInputMapper::process中,我们看到还调用了MultiTouchMotionAccumulator::process,并传入了整个 rawEvent,"accumulator"翻译成中文是"蓄电池、累加器"的意思,那么我们现在猜想,这一步中的sync并未将当前的rawEvent进行同步分发,而是只是将此事件放在 mMultiTouchMotionAccumulator中暂时累积起来而已。
是不是这样呢? 我们再继续分析~
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { if (rawEvent->type == EV_ABS) { //此次上报数据类型为 EV_ABS, bool newSlot = false; if (mUsingSlotsProtocol) { if (rawEvent->scanCode == ABS_MT_SLOT) { mCurrentSlot = rawEvent->value; newSlot = true; } } else if (mCurrentSlot < 0) { mCurrentSlot = 0; } if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { //mSlotCount,这个值表示当前触摸屏支持多点触摸的最大点数, //由 InputDevice::addDeviceLocked -> InputDevice::configure //-> TouchInputMapper::configure -> MultiTouchInputMapper::configureRawPointerAxes //-> MultiTouchMotionAccumulator::configure设置,即由驱动确定 //mCurrentSlot表示当前实际的触摸点数 ... } else { Slot* slot = &mSlots[mCurrentSlot]; //使slot指向&mSlots[mCurrentSlot],mCureentSlot表示当前有几个手指同时点触 //支持多点触控的触摸屏所报上来的数据为一个椭圆, //以下为此椭圆的各项参数 //压力等 switch (rawEvent->scanCode) { case ABS_MT_POSITION_X: slot->mInUse = true; slot->mAbsMTPositionX = rawEvent->value; break; case ABS_MT_POSITION_Y: slot->mInUse = true; slot->mAbsMTPositionY = rawEvent->value; break; case ABS_MT_TOUCH_MAJOR: ... } } } else if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_MT_REPORT) { //上报此类型数据表示一次完整的touch事件已经上报完成 // MultiTouch Sync: The driver has returned all data for *one* of the pointers. mCurrentSlot += 1; //mCurrentSlot加1,指向下一个slot(每一个slot代表一个完整的触点信息) } }
基本明白了,原来一个触点所包含的坐标、压力等信息是分别上报上来的,InputReader线程接收后通过"accumulator"进行综合,每次上报事件"(rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_MT_REPORT) "代表一个触点信息的完结。
继续看 TouchInputMapper::process函数:
void TouchInputMapper::process(const RawEvent* rawEvent) { mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { sync(rawEvent->when); } }
这里又调用了三个process,这三个process分别对应三种类型的触摸设备事件:CursorButton、CursorScroll、TouchButton,设计理念与mapper一样,也是在各自的process中判断type,匹配才进行处理,加到"accumulator"中,如:
void CursorScrollAccumulator::process(const RawEvent* rawEvent) { if (rawEvent->type == EV_REL) { switch (rawEvent->scanCode) { case REL_WHEEL: mRelWheel = rawEvent->value; break; case REL_HWHEEL: mRelHWheel = rawEvent->value; break; } } }
再看sync函数,sync意为同步,那这里应该是真正将数据上报的地方了:
void TouchInputMapper::sync(nsecs_t when) { // Sync button state. // 将 mCursorButtonAccumulator.process 和 mTouchButtonAccumulator.process // 中处理得到的最新状态获取并同步到 mCurrentButtonState上来 mCurrentButtonState = mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState(); // Sync scroll state. // 将通过 mCursorScrollAccumulator.process处理得到的最新状态同步到 // mCurrentRawVScroll和 mCurrentRawHScroll当中 mCurrentRawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); mCurrentRawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); mCursorScrollAccumulator.finishSync(); // Sync touch state. // 同样, 将通过 mMultiTouchMotionAccumulator.process处理得到的多点触控数据 // 同步(同步至成员变量 mCurrentRawPointerData中) bool havePointerIds = true; mCurrentRawPointerData.clear(); syncTouch(when, &havePointerIds); #if DEBUG_RAW_EVENTS if (!havePointerIds) { LOGD("syncTouch: pointerCount %d -> %d, no pointer ids", mLastRawPointerData.pointerCount, mCurrentRawPointerData.pointerCount); } else { LOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " "hovering ids 0x%08x -> 0x%08x", mLastRawPointerData.pointerCount, mCurrentRawPointerData.pointerCount, mLastRawPointerData.touchingIdBits.value, mCurrentRawPointerData.touchingIdBits.value, mLastRawPointerData.hoveringIdBits.value, mCurrentRawPointerData.hoveringIdBits.value); } #endif // Reset state that we will compute below. mCurrentFingerIdBits.clear(); mCurrentStylusIdBits.clear(); mCurrentMouseIdBits.clear(); mCurrentCookedPointerData.clear(); if (mDeviceMode == DEVICE_MODE_DISABLED) { //disable mode, 丢弃 // Drop all input if the device is disabled. mCurrentRawPointerData.clear(); mCurrentButtonState = 0; } else { // Preprocess pointer data. if (!havePointerIds) { assignPointerIds(); } // Handle policy on initial down or hover events. uint32_t policyFlags = 0; //针对多点触摸事件, mLastRawPointerData无数据&&mCurrentRawPointerData中有数据, 意味着现在是首次按下 bool initialDown = mLastRawPointerData.pointerCount == 0 && mCurrentRawPointerData.pointerCount != 0; //针对触摸按键事件, mLastButtonState状态为空&&mCurrentButtonState状态不为空则认为按键动作为DOWN bool buttonsPressed = mCurrentButtonState & ~mLastButtonState; if (initialDown || buttonsPressed) { // If this is a touch screen, hide the pointer on an initial down. if (mDeviceMode == DEVICE_MODE_DIRECT) { getContext()->fadePointer(); } // Initial downs on external touch devices should wake the device. // We don't do this for internal touch screens to prevent them from waking // up in your pocket. // TODO: Use the input device configuration to control this behavior more finely. if (getDevice()->isExternal()) { policyFlags |= POLICY_FLAG_WAKE_DROPPED; } } // Synthesize key down from raw buttons if needed. // 根据 policyFlags, mLastButtonState和mCurrentButtonState等这些事件, 软件生成相应的 // 按键事件, 然后通过 notyfyKey上报 synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, policyFlags, mLastButtonState, mCurrentButtonState); // Consume raw off-screen touches before cooking pointer data. // If touches are consumed, subsequent code will not receive any pointer data. // 对 mCurrentRawPointerData进行判断, 如果是虚拟按键则生成对应按键并上报 if (consumeRawTouches(when, policyFlags)) { // 返回 TRUE则表示为虚拟按键事件, 已经处理完毕, 清除 mCurrentRawPointerData mCurrentRawPointerData.clear(); } // Cook pointer data. This call populates the mCurrentCookedPointerData structure // with cooked pointer data that has the same ids and indices as the raw data. // The following code can use either the raw or cooked data, as needed. // 呵呵, 这个函数名字取得很形象 // 前面说过, 之前的数据是保存在 mCurrentRawPointerData当中, 数据是未处理的, 是raw // 而这里则是进行加工, cook, 比如pressure换算, x&y坐标翻转等, 结果存在 mCurrentCookedPointerData中 // 即 mCurrentRawPointerData -> mCurrentCookedPointerData cookPointerData(); // Dispatch the touches either directly or by translation through a pointer on screen. // 开始分发 mCurrentCookedPointerData // 如果设备模式为 DEVICE_MODE_POINTER, 则要先作一下 mapping, 即映射(是否类似于电脑触摸板???) if (mDeviceMode == DEVICE_MODE_POINTER) { // 转换设备类型标志位 for (BitSet32 idBits(mCurrentRawPointerData.touchingIdBits); !idBits.isEmpty(); ) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id); if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { mCurrentStylusIdBits.markBit(id); } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { mCurrentFingerIdBits.markBit(id); } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) { mCurrentMouseIdBits.markBit(id); } } for (BitSet32 idBits(mCurrentRawPointerData.hoveringIdBits); !idBits.isEmpty(); ) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id); if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { mCurrentStylusIdBits.markBit(id); } } // Stylus takes precedence over all tools, then mouse, then finger. // 这里说了, Stylus优先级最高, 其次为mouse , 再为finger PointerUsage pointerUsage = mPointerUsage; if (!mCurrentStylusIdBits.isEmpty()) { mCurrentMouseIdBits.clear(); mCurrentFingerIdBits.clear(); pointerUsage = POINTER_USAGE_STYLUS; } else if (!mCurrentMouseIdBits.isEmpty()) { mCurrentFingerIdBits.clear(); pointerUsage = POINTER_USAGE_MOUSE; } else if (!mCurrentFingerIdBits.isEmpty() || isPointerDown(mCurrentButtonState)) { pointerUsage = POINTER_USAGE_GESTURES; } // 终于开始dispatch了 dispatchPointerUsage(when, policyFlags, pointerUsage); } else { // 设备模式为触摸屏 if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches && mPointerController != NULL) { mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); mPointerController->setButtonState(mCurrentButtonState); mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords, mCurrentCookedPointerData.idToIndex, mCurrentCookedPointerData.touchingIdBits); } // 开始分发 dispatchHoverExit(when, policyFlags); dispatchTouches(when, policyFlags); dispatchHoverEnterAndMove(when, policyFlags); } // Synthesize key up from raw buttons if needed. - 作者已经说得很明白了 synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, policyFlags, mLastButtonState, mCurrentButtonState); } // Copy current touch to last touch in preparation for the next cycle. mLastRawPointerData.copyFrom(mCurrentRawPointerData); mLastCookedPointerData.copyFrom(mCurrentCookedPointerData); mLastButtonState = mCurrentButtonState; mLastFingerIdBits = mCurrentFingerIdBits; mLastStylusIdBits = mCurrentStylusIdBits; mLastMouseIdBits = mCurrentMouseIdBits; // Clear some transient state. mCurrentRawVScroll = 0; mCurrentRawHScroll = 0; }
上面在处理数据时有对数据进行判断,当判断触点处于虚拟按键区域时,会生成对应的key事件,我们这里再来看一下它是怎么做的:
bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { // Check for release of a virtual key. // 虚拟按键当前为 DOWN状态 if (mCurrentVirtualKey.down) { // 当前手指未按下任何区域 if (mCurrentRawPointerData.touchingIdBits.isEmpty()) { // Pointer went up while virtual key was down. mCurrentVirtualKey.down = false; if (!mCurrentVirtualKey.ignored) { #if DEBUG_VIRTUAL_KEYS LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); #endif //制造一个 UP事件并上报 dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); } return true; } if (mCurrentRawPointerData.touchingIdBits.count() == 1) { uint32_t id = mCurrentRawPointerData.touchingIdBits.firstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id); const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); //手指当前所触控区域仍然为 mCurrentVirtualKey所存储按键 if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { // Pointer is still within the space of the virtual key. return true; } } // Pointer left virtual key area or another pointer also went down. // Send key cancellation but do not consume the touch yet. // This is useful when the user swipes through from the virtual key area // into the main display surface. // 手指当前按下区域已经不是之前触控按键所在区域 mCurrentVirtualKey.down = false; if (!mCurrentVirtualKey.ignored) { #if DEBUG_VIRTUAL_KEYS LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); #endif //制造一个 UP事件并上报 dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY | AKEY_EVENT_FLAG_CANCELED); } } //判断是否符合DOWN事件特征 if (mLastRawPointerData.touchingIdBits.isEmpty() && !mCurrentRawPointerData.touchingIdBits.isEmpty()) { // Pointer just went down. Check for virtual key press or off-screen touches. uint32_t id = mCurrentRawPointerData.touchingIdBits.firstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id); if (!isPointInsideSurface(pointer.x, pointer.y)) { // If exactly one pointer went down, check for virtual key hit. // Otherwise we will drop the entire stroke. if (mCurrentRawPointerData.touchingIdBits.count() == 1) { // 根据pointer坐标确定keyCode等 const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); if (virtualKey) { mCurrentVirtualKey.down = true; mCurrentVirtualKey.downTime = when; mCurrentVirtualKey.keyCode = virtualKey->keyCode; mCurrentVirtualKey.scanCode = virtualKey->scanCode; mCurrentVirtualKey.ignored = mContext->shouldDropVirtualKey( when, getDevice(), virtualKey->keyCode, virtualKey->scanCode); if (!mCurrentVirtualKey.ignored) { #if DEBUG_VIRTUAL_KEYS LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); #endif // 创造 DOWN事件并上报 dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); } } } return true; } } // Disable all virtual key touches that happen within a short time interval of the // most recent touch within the screen area. The idea is to filter out stray // virtual key presses when interacting with the touch screen. // // Problems we're trying to solve: // // 1. While scrolling a list or dragging the window shade, the user swipes down into a // virtual key area that is implemented by a separate touch panel and accidentally // triggers a virtual key. // // 2. While typing in the on screen keyboard, the user taps slightly outside the screen // area and accidentally triggers a virtual key. This often happens when virtual keys // are layed out below the screen near to where the on screen keyboard's space bar // is displayed. // 在mConfig.virtualKeyQuietTime这个时间内禁止所有虚拟按键 if (mConfig.virtualKeyQuietTime > 0 && !mCurrentRawPointerData.touchingIdBits.isEmpty()) { mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); } return false; }
通过sync函数,我们发现,touch所有事件的分发,根据 device mode的不同,都是通过以下两种方式完成的:
DEVICE_MODE_POINTER:
dispatchPointerUsage(when, policyFlags, pointerUsage);
DEVICE_MODE_DIRECT:
dispatchHoverExit(when, policyFlags); dispatchTouches(when, policyFlags); dispatchHoverEnterAndMove(when, policyFlags);
而上面这四个函数最终也都是通过调用 dispatchMotion,只是使用的参数(第四个参数, 即action)不一样罢了:
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime)
这里贴出 dispatchTouches函数看一下:
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { BitSet32 currentIdBits = mCurrentCookedPointerData.touchingIdBits; BitSet32 lastIdBits = mLastCookedPointerData.touchingIdBits; int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentButtonState; if (currentIdBits == lastIdBits) { if (!currentIdBits.isEmpty()) { // No pointer id changes so this is a move event. // The listener takes care of batching moves so we don't have to deal with that here. dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mCurrentCookedPointerData.pointerProperties, mCurrentCookedPointerData.pointerCoords, mCurrentCookedPointerData.idToIndex, currentIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime); } } else { // There may be pointers going up and pointers going down and pointers moving // all at the same time. BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value); BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value); BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value); BitSet32 dispatchedIdBits(lastIdBits.value); // Update last coordinates of pointers that have moved so that we observe the new // pointer positions at the same time as other pointers that have just gone up. bool moveNeeded = updateMovedPointers( mCurrentCookedPointerData.pointerProperties, mCurrentCookedPointerData.pointerCoords, mCurrentCookedPointerData.idToIndex, mLastCookedPointerData.pointerProperties, mLastCookedPointerData.pointerCoords, mLastCookedPointerData.idToIndex, moveIdBits); if (buttonState != mLastButtonState) { moveNeeded = true; } // Dispatch pointer up events. while (!upIdBits.isEmpty()) { uint32_t upId = upIdBits.clearFirstMarkedBit(); dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, metaState, buttonState, 0, mLastCookedPointerData.pointerProperties, mLastCookedPointerData.pointerCoords, mLastCookedPointerData.idToIndex, dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); dispatchedIdBits.clearBit(upId); } // Dispatch move events if any of the remaining pointers moved from their old locations. // Although applications receive new locations as part of individual pointer up // events, they do not generally handle them except when presented in a move event. if (moveNeeded) { LOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, 0, mCurrentCookedPointerData.pointerProperties, mCurrentCookedPointerData.pointerCoords, mCurrentCookedPointerData.idToIndex, dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime); } // Dispatch pointer down events using the new pointer locations. while (!downIdBits.isEmpty()) { uint32_t downId = downIdBits.clearFirstMarkedBit(); dispatchedIdBits.markBit(downId); if (dispatchedIdBits.count() == 1) { // First pointer is going down. Set down time. mDownTime = when; } dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0, mCurrentCookedPointerData.pointerProperties, mCurrentCookedPointerData.pointerCoords, mCurrentCookedPointerData.idToIndex, dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); } } }
可以发现其action用到了 AMOTION_EVENT_ACTION_MOVE 和 AMOTION_EVENT_ACTION_POINTER_UP,所有的action包含以下几种:
enum { AMOTION_EVENT_ACTION_MASK = 0xff, AMOTION_EVENT_ACTION_POINTER_INDEX_MASK = 0xff00, AMOTION_EVENT_ACTION_DOWN = 0, AMOTION_EVENT_ACTION_UP = 1, AMOTION_EVENT_ACTION_MOVE = 2, AMOTION_EVENT_ACTION_CANCEL = 3, AMOTION_EVENT_ACTION_OUTSIDE = 4, AMOTION_EVENT_ACTION_POINTER_DOWN = 5, AMOTION_EVENT_ACTION_POINTER_UP = 6, AMOTION_EVENT_ACTION_HOVER_MOVE = 7, AMOTION_EVENT_ACTION_SCROLL = 8, AMOTION_EVENT_ACTION_HOVER_ENTER = 9, AMOTION_EVENT_ACTION_HOVER_EXIT = 10, };
进入 dispatchMotion:
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) { PointerCoords pointerCoords[MAX_POINTERS]; PointerProperties pointerProperties[MAX_POINTERS]; uint32_t pointerCount = 0; while (!idBits.isEmpty()) { uint32_t id = idBits.clearFirstMarkedBit(); uint32_t index = idToIndex[id]; pointerProperties[pointerCount].copyFrom(properties[index]); pointerCoords[pointerCount].copyFrom(coords[index]); if (changedId >= 0 && id == uint32_t(changedId)) { action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } pointerCount += 1; } LOG_ASSERT(pointerCount != 0); if (changedId >= 0 && pointerCount == 1) { // Replace initial down and final up action. // We can compare the action without masking off the changed pointer index // because we know the index is 0. if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { action = AMOTION_EVENT_ACTION_DOWN; } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { action = AMOTION_EVENT_ACTION_UP; } else { // Can't happen. LOG_ASSERT(false); } } NotifyMotionArgs args(when, getDeviceId(), source, policyFlags, action, flags, metaState, buttonState, edgeFlags, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime); getListener()->notifyMotion(&args); }
发现最终是调用 getListener()->notifyMotion(&args),继续看代码(确实有点累了):
getListener()获取的是 mQueuedListener:
InputListenerInterface* InputReader::ContextImpl::getListener() { return mReader->mQueuedListener.get(); }
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) { mArgsQueue.push(new NotifyMotionArgs(*args)); }
哦,原来只是用参数构建 NotifyMotionArgs对象加入队列。
看到这里,相信很多人都会有疑问,事件只是经过一系列处理、转换后存到了mArgsQueue中,还是没有上报上去啊!?上报的动作到底在哪里呢?
我们再回过头来看一下 InputReader::loopOnce这个函数:
void InputReader::loopOnce() { int32_t timeoutMillis; { // acquire lock AutoMutex _l(mLock); uint32_t changes = mConfigurationChangesToRefresh; if (changes) { mConfigurationChangesToRefresh = 0; refreshConfigurationLocked(changes); } timeoutMillis = -1; if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); } } // release lock size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); if (count) { processEventsLocked(mEventBuffer, count); } if (!count || timeoutMillis == 0) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); #if DEBUG_RAW_EVENTS LOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); #endif mNextTimeout = LLONG_MAX; timeoutExpiredLocked(now); } } // release lock // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked // on another thread similarly waiting to acquire the InputReader lock thereby // resulting in a deadlock. This situation is actually quite plausible because the // listener is actually the input dispatcher, which calls into the window manager, // which occasionally calls into the input reader. mQueuedListener->flush(); }
我们这一节的分析都集中在 mEventHub->getEvents 和 processEventsLocked当中,但在最后它还调用了一个函数 flush(),一看到这个函数大家应该马上会想到写文件时的flush吧!——即所有真正的磁盘写动作都是在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(); }
继续看一下 NotifyArgs这个类:
/* Superclass of all input event argument objects */ struct NotifyArgs { virtual ~NotifyArgs() { } virtual void notify(const sp<InputListenerInterface>& listener) const = 0; };
可以发现这是一个无法实例化的父类,因为其 notify函数是纯虚函数,我们再看一下它有多少子类:
struct NotifyConfigurationChangedArgs : public NotifyArgs struct NotifyKeyArgs : public NotifyArgs struct NotifyMotionArgs : public NotifyArgs struct NotifySwitchArgs : public NotifyArgs struct NotifyDeviceResetArgs : public NotifyArgs
在上面所示的 dispatchMotion函数中可以看到,触屏事件分发时所用的 NotifyArgs子类为 NotifyMotionArgs:
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const { listener->notifyMotion(this); }
这个listener是何方神圣?
首先看到是这里传入的:
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(); }
而这个成员变量 mInnerListener又是在构建函数中传入的:
QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) : mInnerListener(innerListener) { }
而 QueuedInputListener又是在 InputReader实例化的:
InputReader::InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) : mContext(this), mEventHub(eventHub), mPolicy(policy), mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); { // acquire lock ... }
listener这个参数还是传进来的,必须再找到 InputReader构建的地方:
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(); }
一切都明白了!原来这个listener就是 InputDispatcher的对象 mDispatcher!
我们来看一下 interceptMotionBeforeQueueing这个函数,直接从名字上看,应该是说在将motion事件加入队列前进行拦截
void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { // Policy: // - Ignore untrusted events and pass them along. // - No special filtering for injected events required at this time. // - Filter normal events based on screen state. // - For normal events brighten (but do not wake) the screen if currently dim. if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) { if (isScreenOn()) { policyFlags |= POLICY_FLAG_PASS_TO_USER; if (!isScreenBright()) { policyFlags |= POLICY_FLAG_BRIGHT_HERE; } } else { JNIEnv* env = jniEnv(); // 由register_android_server_InputManager可知,这里实际调用的是 // com/android/server/wm/InputManager类中的 interceptMotionBeforeQueueingWhenScreenOff函数 jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.interceptMotionBeforeQueueingWhenScreenOff, policyFlags); if (checkAndClearExceptionFromCallback(env, "interceptMotionBeforeQueueingWhenScreenOff")) { wmActions = 0; } policyFlags |= POLICY_FLAG_WOKE_HERE | POLICY_FLAG_BRIGHT_HERE; // 根据 interceptMotionBeforeQueueingWhenScreenOff返回的wmAction进行处理 handleInterceptActions(wmActions, when, /*byref*/ policyFlags); } } else { policyFlags |= POLICY_FLAG_PASS_TO_USER; } }
那这个 gCallbacksClassInfo.interceptMotionBeforeQueueingWhenScreenOff到底最终调用的是哪个函数呢?
InputManager.java
@SuppressWarnings("unused") public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) { return mWindowManagerService.mInputMonitor.interceptMotionBeforeQueueingWhenScreenOff( policyFlags); }
继续:
InputMonitor.java
/* Provides an opportunity for the window manager policy to intercept early * motion event processing when the screen is off since these events are normally * dropped. */ public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) { return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags); }
找到了:
PhoneWindowManager.java
/** {@inheritDoc} */ @Override public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) { int result = 0; final boolean isWakeMotion = (policyFlags & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; if (isWakeMotion) { // 如果当前界面为锁屏界面 if (mKeyguardMediator.isShowing()) { // If the keyguard is showing, let it decide what to do with the wake motion. // 让锁屏进行一些处理 mKeyguardMediator.onWakeMotionWhenKeyguardShowingTq(); } else { // Otherwise, wake the device ourselves. // 设置标志 ACTION_POKE_USER_ACTIVITY result |= ACTION_POKE_USER_ACTIVITY; } } return result; }
void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags) { if (wmActions & WM_ACTION_GO_TO_SLEEP) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("handleInterceptActions: Going to sleep."); #endif //调用 PowerManagerService::goToSleep android_server_PowerManagerService_goToSleep(when); } if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("handleInterceptActions: Poking user activity."); #endif //调用 PowerManagerService::userActivity android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT); } if (wmActions & WM_ACTION_PASS_TO_USER) { policyFlags |= POLICY_FLAG_PASS_TO_USER; } else { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("handleInterceptActions: Not passing key to user."); #endif } }
四、笔记
updateInputWindowsLw -> setInputWindows -> nativeSetInputWindows -> setInputWindows -> mInputManager->getDispatcher()->setInputWindows(windowHandles) -> 更新 mFocusedWindowHandle status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) =>更新 mMonitoringChannels //InputDispatcher.cpp bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. ... // Handle case where the policy asked us to try again later last time. if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) { if (currentTime < entry->interceptKeyWakeupTime) { if (entry->interceptKeyWakeupTime < *nextWakeupTime) { *nextWakeupTime = entry->interceptKeyWakeupTime; } return false; // wait until next wakeup } entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; entry->interceptKeyWakeupTime = 0; } // 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) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); //interceptKeyBeforeDispatching if (mFocusedWindowHandle != NULL) { commandEntry->inputWindowHandle = mFocusedWindowHandle; } commandEntry->keyEntry = entry; entry->refCount += 1; return false; // wait for the command to run } else { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { if (*dropReason == DROP_REASON_NOT_DROPPED) { *dropReason = DROP_REASON_POLICY; } } // Clean up if dropping the event. if (*dropReason != DROP_REASON_NOT_DROPPED) { setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); return true; } // Identify targets. Vector<InputTarget> inputTargets; int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); //将 mFocusedWindowHandle 加入 inputTargets if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; } setInjectionResultLocked(entry, injectionResult); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { return true; } addMonitoringTargetsLocked(inputTargets); //将 mMonitoringChannels 加入 inputTargets // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true; }