Input子系统架构包括内核层与框架层详解.doc
第1章 Android Input子系统架构1.1 Input服务的启动在Android的开机过程中,系统中的服务很多都是由SystemServer中启动的。SystemServer的代码中有这么一句话。Framework/base/services/java/com/android/server/SystemServer.javaSlog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, power, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,!firstBoot);在这里new了一个WindowManagerService的类,我们找到这个类的构造函数。Framework/base/services/java/com/android/server/wm/WindowManagerServer.javaprivate WindowManagerService(Context context, PowerManagerService pm, boolean haveInputMethods, boolean showBootMsgs) mInputManager = new InputManager(context, this);mInputManager.start();在WindowManagerService的构造函数中又new了一个InputManager类。InputManager类是整个android的input的上层代码最重要的类,就是通过这个类繁衍出了整个复杂的Android的input子系统。作用就好像Zygote的孕育着Android的各个服务,而InputManager就是负责将整个android的Input子系统。Framework/base/services/java/com/android/server/wm/WindowManagerServer.javapublic InputManager(Context context, WindowManagerService windowManagerService) Looper looper = windowManagerService.mH.getLooper();nativeInit(mContext, mCallbacks, looper.getQueue();在InputManger的构造函数中,调用了nativeInit这个方式,看到native开头或者结尾的函数,一般都是JNI。在InputManager的JNI可以找到这个函数的实现。Framework/base/services/jni/com_android_server_InputManager.javastatic JNINativeMethod gInputManagerMethods = "nativeInit", "(Landroid/content/Context;""Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V", (void*) android_server_InputManager_nativeInit ,简单介绍下JNI的代码风格,第一个引号括起来的函数就是我们java代码的函数原型,中间的引号中的就是代表java原型函数的参数。而最后的那个函数就是在对应的函数。一般都是c+代码。Framework/base/services/jni/com_android_server_InputManager.javastatic void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz, jobject contextObj, jobject callbacksObj, jobject messageQueueObj) gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper);在JNI的代码中,又构造了一个重要的NativeInputManager类,这是个C+的本地类。已经不在是之前了那个java的InputManager类。接下来看看NativeInputManager的构造函数。Framework/base/services/jni/com_android_server_InputManager.javaNativeInputManager:NativeInputManager(jobject contextObj, jobject callbacksObj, const sp<Looper>& looper) :mLooper(looper) sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this);这里new了两个类,EventHub和InputManager类。EventHub就是Input子系统的HAL层了,负责将linux的所有的input设备打开并负责轮询读取他们的上报的数据,后面会详细介绍,这里先简单介绍一下。InputManager类主要是负责管理input Event,有InputReader从EventHub读取事件,然后交给InputDispatcher进行分发。Framework/base/services/input/InputManager.cppInputManager:InputManager( const sp<InputReaderInterface>& reader, const sp<InputDispatcherInterface>& dispatcher) : mReader(reader), mDispatcher(dispatcher) initialize();void InputManager:initialize() mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher);在InputManager中的initialize的初始化了两个线程。一个是inputReaderThread,负责从EventHub中读取事件,另外一个是InputDispatcherThread线程,主要负责分发读取的事件去处理。Framework/base/services/java/com/android/server/wm/WindowManagerServer.javaprivate WindowManagerService(Context context, PowerManagerService pm, boolean haveInputMethods, boolean showBootMsgs) mInputManager.start();在开始的时候,new了一个InputManager,然后在继续调用其start方法。Framework/base/services/java/com/android/server/wm/WindowManagerServer.javapublic void start() Slog.i(TAG, "Starting input manager"); nativeStart(); registerPointerSpeedSettingObserver(); registerShowTouchesSettingObserver(); updatePointerSpeedFromSettings(); updateShowTouchesFromSettings(); NativeStart()跑到JNI的代码中去了,跟上面的方式一样。Framework/base/services/jni/com_android_server_InputManager.javastatic JNINativeMethod gInputManagerMethods = "nativeStart", "()V", (void*) android_server_InputManager_nativeStart ,static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) status_t result = gNativeInputManager->getInputManager()->start();在java代码中用了nativeStart(),然后JNI中又调用了NativeInputManager的start方法。在Native的InputManager中找到start的实现。Framework/base/services/input/InputManager.cppstatus_t InputManager:start() status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);这个方法就是在前面InputManager中的构造函数initialize中的两个线程运行起来。先看InputDispatcher线程运行的情况,然后就是InputReader线程。Framework/base/services/input/InputDispatcher.cppbool InputDispatcherThread:threadLoop() mDispatcher->dispatchOnce(); return true;InputDispatcher线程调用了Dispatcher的dispatchOnce的方法。同样的InputReader线程也会调用Reader的ReaderOnce的方法。Framework/base/services/input/InputDispatcher.cppvoid InputDispatcher:dispatchOnce() dispatchOnceInnerLocked(&nextWakeupTime); int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); mLooper->pollOnce(timeoutMillis);dispatchOnceInnerLocked是处理input输入消息,mLooper->pollOnce(timeoutMillis)是等待下次输入消息的事件。先看下消息在dispatchOnceInnerLocked函数中是如何处理的。Framework/base/services/input/InputDispatcher.cppvoid InputDispatcher:dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) case EventEntry:TYPE_KEYdone = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);case EventEntry:TYPE_MOTION: done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);这个函数比较长,input事件在android的上层通过两个队列来保存,分别是InboundQueue和outboundQueue。当有input事件产生时候,会判断InboundQueue是否为空,如果事件不为空的话,就从队列中取出这个input事件,然后根据input事件的类型来分发事件给不同的处理函数,比较常见的是KEY和Motion事件。不管是Key事件也好还是Motion事件都会调用dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);这个函数来继续处理。Framework/base/services/input/InputDispatcher.cppvoid InputDispatcher:dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, EventEntry* eventEntry, bool resumeWithAppendedMotionSample) prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, resumeWithAppendedMotionSample);在这个函数中会继续调用prepareDispatchCycleLocked方法来继续处理。而在prepareDispatchCycleLocked中又会继续调用startDispatchCycleLocked(currentTime, connection)来进一步处理。Framework/base/services/input/InputDispatcher.cppvoid InputDispatcher:startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) switch (eventEntry->type) case EventEntry:TYPE_KEY: status = connection->inputPublisher.publishKeyEventcase EventEntry:TYPE_MOTION: status = connection->inputPublisher.publishMotionEventstatus = connection->inputPublisher.sendDispatchSignal();这个函数主要是根据input事件的类型来分发给不同的函数去处理,如果是KEY类型的事件就调用inputPublisher类的publishKeyEvent,如果是MOTION类的事件就会调用inputPublisher类的publishMotionEvent方法。并在最后发一个sendDispatchSignal。Framework/base/libs/ui/InputTransport.cppstatus_t InputPublisher:publishInputEvent(int ashmemFd = mChannel->getAshmemFd();int result = ashmem_pin_region(ashmemFd, 0, 0);mSemaphoreInitialized = true; mSharedMessage->consumed = false; mSharedMessage->type = type; mSharedMessage->deviceId = deviceId;mSharedMessage->source = source;利用publisher中的publishInputEvent将input event写入共享内存。这边产生了事件,另外一边必然会有个地方回去消费这个事件。注意到上面的代码中,最后发送了一个sendDispatchSignal。Framework/base/libs/ui/InputTransport.cppstatus_t InputPublisher:sendDispatchSignal() return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);这个函数直接调用了inputChannel的sendSignal方法。继续找到inputChannel的sendSignal实现。Framework/base/libs/ui/InputTransport.cppstatus_t InputChannel:sendSignal(char signal) do nWrite = :write(mSendPipeFd, & signal, 1); while (nWrite = -1 && errno = EINTR);而在注册InputChannel的时候就曾经注册了当Looper接收到了信号的一个回调函数。Framework/base/services/input/InputDispatcher.cppstatus_t InputDispatcher:registerInputChannel(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);在handleReceiveCallback中,作为回调函数然后调用InputConsumer的consume函数来消费从inputReader中读取过来的InputEvent。Framework/base/core/jni/android_view_InputQueue.cppint NativeInputQueue:handleReceiveCallback(int receiveFd, int events, void* data) status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);回过头来看之前的InputReader线程,在inputManager的start方法被调用了,Input的线程也就开始运行了。Framework/base/services/input/InputReader.cppbool InputReaderThread:threadLoop() mReader->loopOnce(); return true;在InputReader的loopOnce中会调用EventHub的getevents方法。这个方法会和linux内核的input子系统打交道。Framework/base/services/input/InputReader.cppvoid InputReader:loopOnce() size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);if (count) processEventsLocked(mEventBuffer, count); 这个函数主要通过EventHub的getEvents来获取input事件。Framework/base/services/input/EventHub.cppsize_t EventHub:getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) struct input_event readBufferbufferSize;for (;) if (mNeedToScanDevices) mNeedToScanDevices = false; scanDevicesLocked(); mNeedToSendFinishedDeviceScan = true; 在eventHub初始化的时候mNeedToScanDevices的值是ture的,所以会直接进入到scanDevicesLocked。而在内核里面所有的input device在注册的时候都会在linux的文件系统下的/dev/input 下面,所以按照一般的HAL的思想,如果要去操作这个设备,首先还是要打开这个设备节点的。Framework/base/services/input/EventHub.cppvoid EventHub:scanDevicesLocked() status_t res = scanDirLocked(DEVICE_PATH); if(res < 0) LOGE("scan dir failed for %sn", DEVICE_PATH); status_t EventHub:scanDirLocked(const char *dirname)openDeviceLocked(devname);代码中的while循环会对DEVICE_PATH(/dev/input)下的所有的设备节点调用openDeviceLocked方法。Framework/base/services/input/EventHub.cppstatus_t EventHub:openDeviceLocked(const char *devicePath) int fd = open(devicePath, O_RDWR);InputDeviceIdentifier identifier;if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1if(ioctl(fd, EVIOCGVERSION, &driverVersion)if(ioctl(fd, EVIOCGID, &inputId)identifier.bus = inputId.bustype;identifier.product = inputId.product;identifier.vendor = inputId.vendor;identifier.version = inputId.version;Device* device = new Device(fd, deviceId, String8(devicePath), identifier);struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem); eventItem.events = EPOLLIN;eventItem.data.u32 = deviceId;mDevices.add(deviceId, device); device->next = mOpeningDevices; mOpeningDevices = device;return 0;首先通过open系统调用得到设备节点的文件描述符,然后新构造一个叫InputDeviceIdentifier类。接着通过对刚才得到的设备节点描述下ioctl的命令获取设备的一些简单信息,譬如:设备的名字,设备驱动的版本号,设备的唯一id,和描述符轮询的方式。得到的这些信息保存在InputDeviceIdentifier类里面。最后又构造了一个Device类,其中设备描述符和刚才的构造InputDeviceIdentifier类作为参数重新构造了Device类。然后在构造成功了Device类又会通过ioctl系统调用获取input设备的一些比较重要的参数。比如:设备上报事件的类型是相对事件还是绝对事件,相对事件一般是指像鼠标滑动,绝对事件就好比触摸屏上报的坐标,设备所属的class等一些比较重要的信息。举一些例子:INPUT_DEVICE_CLASS_KEYBOARD(按键类型),INPUT_DEVICE_CLASS_CURSOR(带游标类型:鼠标和轨迹球等),INPUT_DEVICE_CLASS_TOUCH(触摸类型:单点触摸或多点触摸),INPUT_DEVICE_CLASS_TOUCH_MT(这个类型特指多点触摸)等。如果一个设备的驱动没有指明设备的类型的话,那么他在android中上报的数据时不会被处理的。这个函数的最后是将input设备的文件描述符加入到轮询的集合中去,如果接收到事件就会去处理。Framework/base/services/input/InputReader.cppvoid InputReader:loopOnce() size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);if (count) processEventsLocked(mEventBuffer, count); 回到之前的InputReader的线程中,通过EventHub的getevents方法得到了input事件。函数返回的是获取的事件数目。如果事件不为零或者负数就会调用processEventLocked来处理。Framework/base/services/input/InputReader.cppvoid InputReader:processEventsLocked(const RawEvent* rawEvents, size_t count) processEventsForDeviceLocked(deviceId, rawEvent, batchSize);case EventHubInterface:DEVICE_ADDED: addDeviceLocked(rawEvent->when, rawEvent->deviceId);case EventHubInterface:DEVICE_REMOVED: removeDeviceLocked(rawEvent->when, rawEvent->deviceId);case EventHubInterface:FINISHED_DEVICE_SCAN: handleConfigurationChangedLocked(rawEvent->when);在处理input的事件时候,如果不是设备的添加,删除和完成扫描的时候。就会调用processEventsForDeviceLocked来处理。Framework/base/services/input/InputReader.cppvoid InputReader:processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) device->process(rawEvents, count);这个函数也很简单直接回调了device的process方法来进行处理,这个device就是在之前的eventHub打开设备时候构造了一个device类。下面来具体看看device的process是如何进行处理的。Framework/base/services/input/InputReader.cppvoid InputDevice:process(const RawEvent* rawEvents, size_t count) for (size_t i = 0; i < numMappers; i+) InputMapper* mapper = mMappersi; mapper->process(rawEvent); 这里直接调用了mapper的process。那InputMapper是什么时候初始化的呢?前面提到如果设备不是设备的添加或删除的时候就调用processEventsForDeviceLocked来处理。也就是说当设备第一次添加的时候,就会调用addDeviceLocked。InputMapper就是从这个地方注册过来的。具体看下面的代码:Framework/base/services/input/InputReader.cppvoid InputReader:addDeviceLocked(nsecs_t when, int32_t deviceId) InputDevice* device = createDeviceLocked(deviceId, name, classes);InputDevice* InputReader:createDeviceLocked(int32_t deviceId,const String8& name, uint32_t classes) InputDevice* device = new InputDevice(&mContext, deviceId, name, classes);if (classes & INPUT_DEVICE_CLASS_SWITCH) device->addMapper(new SwitchInputMapper(device);if (classes & INPUT_DEVICE_CLASS_CURSOR) device->addMapper(new CursorInputMapper(device);if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) device->addMapper(new MultiTouchInputMapper(device); else if (classes & INPUT_DEVICE_CLASS_TOUCH) device->addMapper(new SingleTouchInputMapper(device);if (classes & INPUT_DEVICE_CLASS_JOYSTICK) device->addMapper(new JoystickInputMapper(device);return device;从上面的代码可以非常明显的看出,InputMapper是根据inputDevice的class来构造的这么一个类。也就是说如果我们的设备是Switch类就为这个设备构造一个SwitchInputMapper类,我们假设我们现在的事件是由触摸屏上报的事件处理流程。在前面处理的过程中我们就会调用MultiTouchInputMapper的process来继续处理。也就是说当处理到mapper->process的时候,代码就会根据具体的设备class类型来处理相应的事件。这么多inputMapper,我们就以MultiTouchInputMapper的流程继续来分析事件的处理流程。Framework/base/services/input/InputReader.cppvoid MultiTouchInputMapper:process(const RawEvent* rawEvent) TouchInputMapper:process(rawEvent); mMultiTouchMotionAccumulator.process(rawEvent);void MultiTouchMotionAccumulator:process(const RawEvent* rawEvent) switch (rawEvent->scanCode) case ABS_MT_POSITION_X: slot->mInUse = true; slot->mAb