android input(一) 初始化及InputReader流程

android input(二) InputDispatcher

概述

当输入设备可用时,Linux内核会在/dev/input/下创建对应的名为event0~n或其他名称的设备节点。而当输入设备不可用时,则会将对应的节点删除
Android输入系统的工作原理概括来说,就是监控/dev/input/下的所有设备节点,当某个节点有数据可读时,将数据读出并进行一系列的翻译加工,然后在所有的窗口中寻找合适的事件接收者,并派发给它

  • getevent与sendevent
    getevent与sendevent两个工具可以从设备节点中直接读取输入事件或写入输入事件
    getevent

由于getevent不会对事件数据做任何加工,因此其输出的内容是由内核提供的最原始的事件,输出是十六进制的
adb shell getevent [-选项] [device_path]
adb shell getevent –t 查看当前按下按键的值,值0x01表示按下,0x00则表示抬起
按下返回
[1556162527.777123] /dev/input/event6: 0001 009e 00000001
[1556162527.777123] /dev/input/event6: 0000 0000 00000000
送开返回
[1556162530.504152] /dev/input/event6: 0003 0030 00000000
[1556162530.504152] /dev/input/event6: 0003 0032 00000000
[1556162530.504152] /dev/input/event6: 0000 0002 00000000
[1556162530.504152] /dev/input/event6: 0000 0000 00000000
[1556162530.525527] /dev/input/event6: 0001 009e 00000000
[1556162530.525527] /dev/input/event6: 0000 0000 00000000

事件类型(0001),事件代码(009e)以及事件的值(00000001)

sendevent
实现模拟用户输入的功能,sendevent的参数为十进制

sendevent <节点路径> <类型><代码> <值>
adb shell sendevent /dev/input/event0 1 116 1 #按下电源键
adb shell sendevent /dev/input/event0 1 116 0 #抬起电源键

adb shell input keyevent 4 返回按键

  • android系统input事件处理流程
    在这里插入图片描述

内核将原始事件写入到设备节点中,InputReader不断地通过EventHub将原始事件取出来并翻译加工成Android输入事件,然后交给InputDispatcher。InputDispatcher根据WMS提供的窗口信息将事件交给合适的窗口。窗口的ViewRootImpl对象再沿着控件树将事件派发给感兴趣的控件。控件对其收到的事件作出响应,更新自己的画面、执行特定的动作

  • Linux内核,接受输入设备的中断,并将原始事件的数据写入到设备节点中。

  • 设备节点,作为内核与IMS的桥梁,它将原始事件的数据暴露给用户空间,以便IMS可以从中读取事件。

  • InputManagerService,一个Android系统服务,它分为Java层和Native层两部分。Java层负责与WMS的通信。而Native层则是InputReader和InputDispatcher两个输入系统关键组件的运行容器。

  • EventHub,直接访问所有的设备节点。并且正如其名字所描述的,它通过一个名为getEvents()的函数将所有输入系统相关的待处理的底层事件返回给使用者。这些事件包括原始输入事件、设备节点的增删等。

  • InputReader,是IMS中的关键组件之一。它运行于一个独立的线程中,负责管理输入设备的列表与配置,以及进行输入事件的加工处理。它通过其线程循环不断地通过getEvents()函数从EventHub中将事件取出并进行处理。对于设备节点的增删事件,它会更新输入设备列表于配置。对于原始输入事件,InputReader对其进行翻译、组装、封装为包含了更多信息、更具可读性的输入事件,然后交给InputDispatcher进行派发。

  • InputReaderPolicy,它为InputReader的事件加工处理提供一些策略配置,例如键盘布局信息等。

  • InputDispatcher,是IMS中的另一个关键组件。它也运行于一个独立的线程中。InputDispatcher中保管了来自WMS的所有窗口的信息,其收到来自InputReader的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口。

  • InputDispatcherPolicy,它为InputDispatcher的派发过程提供策略控制。例如截取某些特定的输入事件用作特殊用途,或者阻止将某些事件派发给目标窗口。一个典型的例子就是HOME键被InputDispatcherPolicy截取到PhoneWindowManager中进行处理,并阻止窗口收到HOME键按下的事件。

  • WMS,虽说不是输入系统中的一员,但是它却对InputDispatcher的正常工作起到了至关重要的作用。当新建窗口时,WMS为新窗口和IMS创建了事件传递所用的通道。另外,WMS还将所有窗口的信息,包括窗口的可点击区域,焦点窗口等信息,实时地更新到IMS的InputDispatcher中,使得InputDispatcher可以正确地将事件派发到指定的窗口。

  • ViewRootImpl,对于某些窗口,如壁纸窗口、SurfaceView的窗口来说,窗口即是输入事件派发的终点。而对于其他的如Activity、对话框等使用了Android控件系统的窗口来说,输入事件的终点是控件(View)。ViewRootImpl将窗口所接收到的输入事件沿着控件树将事件派发给感兴趣的控件。

初始化

在这里插入图片描述

InputManagerService初始化

* frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
	...
	 traceBeginAndSlog("StartInputManagerService");
     inputManager = new InputManagerService(context);
     traceEnd();
     ...
     traceBeginAndSlog("StartInputManager");
     inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
     inputManager.start();
     traceEnd();
     ...      
}

* frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public InputManagerService(Context context) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
		...
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
		...
        LocalServices.addService(InputManagerInternal.class, new LocalService());
}

* frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
   ...
   /* 新建了一个NativeInputManager对象,NativeInputManager,
   此对象将是Native层组件与Java层IMS进行通信的桥梁 */
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,  messageQueue->getLooper());
    im->incStrong(0);
    //返回了NativeInputManager对象的指针给Java层的IMS,IMS将其保存在mPtr成员变量中
    return reinterpret_cast<jlong>(im);
}

在nativeInit函数中,将Java层的MessageQueue转换为native层的MessageQueue,然后再取出Looper用于NativeInputManager的初始化。NativeInputManager是Java层与Native层互相通信的桥梁,它实现了InputReaderPolicyInterface与InputDispatcherPolicyInterface两个接口,通过JNI回调Java层的IMS,由它完成决策。

NativeInputManager的初始化

这个过程做了以下事情:

  • 将Java层的InputManagerService转换为native层的InputManagerService存储在mServiceObj中
    -创建InputManager
* frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    mReader = createInputReader(readerPolicy, mClassifier);
    initialize();
}

sp<InputReaderInterface> createInputReader(
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) {
    return new InputReader(new EventHub(), policy, listener);
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputManager的初始化
InputManager的构造函数也比较简洁,它创建了四个对象,分别为IMS的核心参与者InputReader与InputDispatcher,以及它们所在的线程InputReaderThread与InputDispatcherThread。注意InputManager的构造函数的参数readerPolicy与dispatcherPolicy,它们都是NativeInputManager
InputDispatcher会创建自己线程的Looper,以及设置根据传入的dispatchPolicy设置分发规则。InputReader则会将传入的InputDispatcher封装为监听对象存起来,并创建一个EventHub

在这里插入图片描述

启动

SystemServer执行InputManagerService.start()函数以启动IMS,在start()方法中,做了以下事情

  1. 调用nativeStart方法,其实就是调用InputManager的start()方法
  2. 将InputManagerService交给WatchDog监控
  3. 注册触控点速度、显示触控的观察者,并注册广播监控它们
  4. 主动调用updateXXX方法更新(初始化)

InputManager的start()启动InputDispatcherThread和InputReaderThread开始监听

当两个线程启动后,InputReader在其线程循环中不断地从EventHub中抽取原始输入事件,进行加工处理后将加工所得的事件放入InputDispatcher的派发发队列中。InputDispatcher则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中。窗口事件接收线程的Looper从管道中将事件取出,交由事件处理函数进行事件响应
在这里插入图片描述

* frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
 public void start() {
        Slog.i(TAG, "Starting input manager");
        nativeStart(mPtr);
        ...
        updatePointerSpeedFromSettings();
        updateShowTouchesFromSettings();
        updateAccessibilityLargePointerFromSettings();
}
    
* frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    ...
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
   ...
}

InputReaderThread

  • 启动后循环执行mReader->loopOnce(),loopOnce()中会调用mEventHub->getEvents读取事件,读取的结果存储在参数mEventBuffer中,返回值表示事件的个数,当EventHub中无事件可以抽取时,此函数的调用将会阻塞直到事件到来或者超时
  • 读到了事件就会调用processEventsLocked处理事件,对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。对于原始输入事件,则在进行转译、封装与加工后将结果暂存到mQueuedListener中,处理完成后调用getInputDevicesLocked获取输入设备信息
  • 调用mPolicy->notifyInputDevicesChanged函数利用InputManagerService的代理通过Handler发送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知输入设备发生了变化
  • 最后调用mQueuedListener->flush(),将事件队列中的所有事件交给在InputReader中注册过的InputDispatcher

注意 C++层的Thread类与Java层的Thread类有着一个显著的不同。C++层Thread类内建了线程循环,threadLoop()就是一次循环而已,只要返回值为true,threadLoop()将会不断地被内建的循环调用。这也是InputReader.loopOnce()函数名称的由来。而Java层Thread类的run()函数则是整个线程的全部,一旦其退出,线程也便完结。

* frameworks/native/services/inputflinger/InputReaderBase.cpp
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

* frameworks/native/services/inputflinger/InputReader.cpp
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();
}

EventHub

InputReader在其线程循环中的第一个工作便是从EventHub中读取一批未处理的事件
EventHub的直译是事件集线器,顾名思义,它将所有的输入事件通过一个接口getEvents()将从多个输入设备节点中读取的事件交给InputReader,是输入系统最底层的一个组件

* frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
        mOpeningDevices(nullptr), mClosingDevices(nullptr),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false), mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    //创建一个epoll对象
    mEpollFd = epoll_create1(EPOLL_CLOEXEC);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
    //创建一个inotify对象。这个inotify对象将被用来监听设备节点的增删事件
    mINotifyFd = inotify_init();
    //将存储设备节点的路径/dev/input作为监听对象添加到inotify对象中。当此文件夹下的设备节点
    //发生创建与删除事件时,都可以通过mINotifyFd读取事件的详细信息
    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
   ...
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mINotifyFd;
    //将mINotifyFd作为epoll的一个监控对象。当inotify事件到来时,epoll_wait()将
    //立刻返回,EventHub便可从mINotifyFd中读取设备节点的增删信息,并作相应处理
    int 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);
	
	/*创建了一个名为wakeFds的匿名管道,并将管道读取端的描述符的可读事件注册到epoll对象中。
	因为InputReader在执行getEvents()时会因无事件而导致其线程阻塞在epoll_wait()的调用里,
	然而有时希望能够立刻唤醒InputReader线程使其处理一些请求。
	此时只需向wakeFds管道的写入端写入任意数据,此时读取端有数据可读,使得epoll_wait()得以返回,
	从而达到唤醒InputReader线程的目的*/

    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];
	...
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    ...
}
  • INotify
    INotify是一个Linux内核所提供的一种文件系统变化通知机制。它可以为应用程序监控文件系统的变化,如文件的新建、删除、读写等。INotify机制有两个基本对象,分别为inotify对象与watch对象,都使用文件描述符表示。
  • inotify对象对应了一个队列,应用程序可以向inotify对象添加多个监听。当被监听的事件发生时,可以通过read()函数从inotify对象中将事件信息读取出来。Inotify对象可以通过inotify_init创建
  • watch对象则用来描述文件系统的变化事件的监听。它是一个二元组,包括监听目标和事件掩码两个元素。监听目标是文件系统的一个路径,可以是文件也可以是文件夹。而事件掩码则表示了需要需要监听的事件类型,掩码中的每一位代表一种事件。可以监听的事件种类很多,其中就包括文件的创建(IN_CREATE)与删除(IN_DELETE)。以下代码即可将一个用于监听输入设备节点的创建与删除的watch对象添加到inotify对象中:
    int wd = inotify_add_watch (inotifyFd, “/dev/input”,IN_CREATE | IN_DELETE);

完成上述watch对象的添加后,当/dev/input/下的设备节点发生创建与删除操作时,都会将相应的事件信息写入到inotifyFd所描述的inotify对象中,此时可以通过read()函数从inotifyFd描述符中将事件信息读取出来
事件信息使用结构体inotify_event进行描述

struct inotify_event {
       __s32           wd;             /* 事件对应的Watch对象的描述符 */
       __u32           mask;           /* 事件类型,例如文件被删除,此处值为IN_DELETE */
       __u32           cookie;
       __u32           len;            /* name字段的长度 */
       char            name[0];        /* 可变长的字段,用于存储产生此事件的文件路径*/
};

当有监听事件发生时,可以通过如下方式将一个或多个未读取的事件信息读取出来

size_t len = read (inotifyFd, events_buf,BUF_LEN);
其中events_buf是inotify_event的数组指针,能够读取的事件数量由取决于数组的长度。成功读取事件信息后,便可根据inotify_event结构体的字段判断事件类型以及产生事件的文件路径了

INotify机制的使用过程

  • 通过inotify_init()创建一个inotify对象。
  • 通过inotify_add_watch将一个或多个监听添加到inotify对象中。
  • 通过read()函数从inotify对象中读取监听事件。当没有新事件发生时,inotify对象中无任何可读数据。

通过INotify机制避免了轮询文件系统的麻烦,但是还有一个问题,INotify机制并不是通过回调的方式通知事件,而需要使用者主动从inotify对象中进行事件读取,这里借助linux的Epoll机制

  • Epoll
    Epoll可以使用一次等待监听多个描述符的可读/可写状态。等待返回时携带了可读的描述符或自定义的数据,使用者可以据此读取所需的数据后可以再次进入等待。因此不需要为每个描述符创建独立的线程进行阻塞读取,避免了资源浪费的同时又可以获得较快的响应速度
  • epoll_create(int max_fds):创建一个epoll对象的描述符,之后对epoll的操作均使用这个描述符完成。max_fds参数表示了此epoll对象可以监听的描述符的最大数量。
    int epoll_create1(int flag);
    当flag是0时,表示和epoll_create函数完全一样,不需要size的提示了
    当flag = EPOLL_CLOEXEC,创建的epfd会设置FD_CLOEXEC
    当flag = EPOLL_NONBLOCK,创建的epfd会设置为非阻塞
    FD_CLOEXEC是fd的一个标识说明,用来设置文件close-on-exec状态的。当close-on-exec状态为0时,调用exec时,fd不会被关闭;状态非零时则会被关闭,这样做可以防止fd泄露给执行exec后的进程
  • epoll_ctl (int epfd, int op,int fd, struct epoll_event *event):用于管理注册事件的函数。这个函数可以增加/删除/修改事件的注册。
    op表示了何种操作,包括EPOLL_CTL_ADD/DEL/MOD三种,分别表示增加/删除/修改注册事件。
  • int epoll_wait(int epfd, structepoll_event * events, int maxevents, int timeout):用于等待事件的到来。当此函数返回时,events数组参数中将会包含产生事件的文件描述符。函数返回值表示获取了多少个事件

struct epoll_event {
__uint32_tevents; /* 事件掩码,指明了需要监听的事件种类*/
epoll_data_t data; /* 使用者自定义的数据,当此事件发生时该数据将原封不动地返回给使用者 */
};
events字段是一个事件掩码,用以指明需要监听的事件种类,同INotify一样,掩码的每一位代表了一种事件。常用的事件有EPOLLIN(可读),EPOLLOUT(可写),EPOLLERR(描述符发生错误),EPOLLHUP(描述符被挂起)等
data字段是一个联合体,它让使用者可以将一些自定义数据加入到事件通知中,当此事件发生时,用户设置的data字段将会返回给使用者。在实际使用中常设置epoll_event.data.fd为需要监听的文件描述符,事件发生时便可以根据epoll_event.data.fd得知引发事件的描述符

Epoll机制的使用过程

  • 创建epoll对象
    Int epfd = epoll_create(MAX_FDS)
  • 填充epoll_event结构体,以描述监控事件
  • 通过epoll_ctl()函数将此描述符与epoll_event结构体注册进epoll对象,重复这个步骤可以将多个文件描述符的多种事件监听注册到epoll对象中
  • 使用epoll_wait()函数等待事件
    会使调用者陷入等待状态,直到其注册的事件之一发生之后才会返回,并且携带了刚刚发生的事件的详细信息,在如getEvents中调用
  • EventHub的创建过程
  • 创建mEpollFd用于监听是否有数据(有无事件)可读
  • 创建mINotifyFd将它注册到DEVICE_PATH(这里路径就是/dev/input)节点,并将它交给内核用于监听该设备节点的增删数据事件。那么只要有数据增删的事件到来,epoll_wait()就会返回,使得EventHub能收到来自系统的通知,并获取事件的详细信息
  • 调用epoll_ctl函数将mEpollFd和mINotifyFd注册到epoll中
  • 定义int wakeFd[2]作为事件传输管道的读写两端,并将读端注册到epoll中让mEpollFd监听
* frameworks/native/services/inputflinger/EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
	...
	for (;;) {
		...
		if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            //遍历/dev/input下所有可用的输入设备打开并存储到Device结构体
            //将设备节点的描述符的可读事件注册到Epoll中,当此设备的输入事件到来时,Epoll会在getEvents()函数的调用中产生一条epoll事件
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
         //遍历mClosingDevices链表,为每一个已卸载的设备生成DEVICE_REMOVED事件
        while (mClosingDevices) {
        ...
        }
        // 通过Epoll事件的data字段确定此事件表示了mINotifyFd可读,及Epoll事件的处理
         while (mPendingEventIndex < mPendingEventCount) {
         }
         //如果INotify事件待处理
          if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            //调用readNotifyLocked()函数读取并处理存储在mINotifyFd中的INotify事件
            readNotifyLocked();
            deviceChanged = true;
        }
    }
}

getEvents()通过Epoll事件的data.u32字段在mDevices列表中查找已加载的设备,并从设备的文件描述符中读取原始输入事件列表。从文件描述符中读取的原始输入事件存储在input_event结构体中,这个结构体的四个字段存储了事件的事件戳、类型、代码与值四个元素。然后逐一将input_event的数据转存到RawEvent中并保存至buffer以返回给调用者
在这里插入图片描述

InputReader

InputReader是在InputReaderThread中启动的,InputReaderThread和InputDispatcherThread的定义是类似的,也是继承了Thread并定义了threadLoop纯虚函数。如果处理的事件为键盘输入事件,则调用时序图如下所示。
在这里插入图片描述

原始事件的入口函数是ProcessEventsLocked,根据事件类型区分原始输入事件还是设备增删事件,对于原始输入事件,EventHub会讲属于同意输入设备的原始输入事件放在一起,因此使用processEventsForDeviceLocked同时处理来自同意输入设备的一批事件。

* frameworks/native/services/inputflinger/InputReader.cpp
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;
           	...
           	//数据事件的处理,batchSize表示属于此设备的输入事件个数
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
        	//处理设备增删事件
            ...
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    //InputReader保存了mDevices字典,以设备ID为键值存储了一系列的InputDevice对象
    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;
    }
    //调用InputDevice的process处理事件
    device->process(rawEvents, count);
}

processEventsForDeviceLocked中,InputReader保存了mDevices字典,在processEventsLocked的处理设备增删阶段初始化,然后调用InputDevice::process处理事件

InputDevice
InputReader的InputDevice存储输入设备的信息,与EventHub一样,InputDevice描述了一个输入设备,并且以设备ID为键保存在mDevices中,InputReader::InputDevice和EventHub::Device结构体类似,也保存了设备的ID、厂商信息、设备所属类别。只是InputDevice多了一个InputMapper列表。

InputMapper
InputMapper是InputReader中实际进行原始输入事件加工的场所,有一系列子类,分别用于加工不同类型的原始输入事件。这样使得InputDevice不需要知道哪一个InputMapper可以处理一个原始输入事件,只须将一个事件逐一交给每一个InputMapper尝试处理,如果InputMapper可以接受这个事件则处理,否则什么都不做。

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {//已添加的相同设备则不再添加
        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
        return;
    }
    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);

    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);
...
    mDevices.add(deviceId, device);
    ...
}

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);
   ...
   //添加键盘类设备InputMapper
   if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }
    ...
    //添加触摸屏设备InputMapper
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        device->addMapper(new SingleTouchInputMapper(device));
    }
    ...
}

InputDevice::process首先会遍历InputDevice中的所有的事件,真正加工原始输入事件的是InputMapper对象,由于原始输入事件的类型很多,因此在InputMapper有很多子类,用于加工不同的原始输入事件,比如KeyboardInputMapper用于处理键盘输入事件,TouchInputMapper用于处理触摸输入事件。

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        ...
         //mDropUntilNextSync的值默认为false,如果设备的输入事件缓冲区溢出,这个值会置为true。
        if (mDropUntilNextSync) {
        }else{
            for (InputMapper* mapper : mMappers) {
                mapper->process(rawEvent);
            }
        }
        ...
        --count;
    }
}
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {//表示为Keyboard事件
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;
        //排除对鼠标按键的处理
        if (isKeyboardOrGamepadKey(scanCode)) {
            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
        }
        break;
    }
    ...
    }
}

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;

    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
        keyCode = AKEYCODE_UNKNOWN;
        keyMetaState = mMetaState;
        policyFlags = 0;
    }
    if (down) {
        // 当按下时,首先需要根据屏幕的方向对按键的虚拟键值进行旋转转换
        if (mParameters.orientationAware) {
            keyCode = rotateKeyCode(keyCode, getOrientation());
        }
       // KeyboardInputMapper维护了一个mKeyDowns集合
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            //对于重复按下的按键,需要确保后续的处理过程中的虚拟键值与第一次按下时的一致,以免重复过程中屏幕的方向的变化导致虚拟键值的变化,使得后续InputDispatch无法正常识别重复按键的动作
            keyCode = mKeyDowns[keyDownIndex].keyCode;
        } else {
            //生成keydown结构体并添加到集合中
            if ((policyFlags & POLICY_FLAG_VIRTUAL)
                    && mContext->shouldDropVirtualKey(when,
                            getDevice(), keyCode, scanCode)) {
                return;
            }
            if (policyFlags & POLICY_FLAG_GESTURE) {
                mDevice->cancelTouch(when);
            }
            KeyDown keyDown;
            keyDown.keyCode = keyCode;
            keyDown.scanCode = scanCode;
            mKeyDowns.push_back(keyDown);
        }

        mDownTime = when;
    } else {
        ssize_t keyDownIndex = findKeyDown(scanCode);
        //对于抬起的按键,则将对应的keydown对象从集合中移除
        if (keyDownIndex >= 0) {
            keyCode = mKeyDowns[keyDownIndex].keyCode;
            mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
        } else {
            return;
        }
    }
    ...
    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
            getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);
}

status_t EventHub::mapKey(int32_t deviceId,
        int32_t scanCode, int32_t usageCode, int32_t metaState,
        int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    status_t status = NAME_NOT_FOUND;

    if (device) {
        // Check the key character map first.
        sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
        if (kcm != nullptr) {
            if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
                *outFlags = 0;
                status = NO_ERROR;
            }
        }
}

EventHub::openDeviceLocked会为此设备加载键盘布局配置文件,键盘布局配置文件的路径位于/system/usr/keylayout下的.lk文件

processKey函数会将加工后的键盘输入事件封装为NotifyKeyArgs,将NotifyKeyArgs通知给InputListenerInterface。
mArgsQueue的数据类型为Vector<NotifyArgs*>,将该key事件压人该vector中。
InputReader::loopOnce的最后调用mQueuedListener->flush()遍历整个mArgsQueue数组

InputDispatcher继承了InputDispatcherInterface,而InputDispatcherInterface继承了InputListenerInterface,因此实际上是调用了InputDispatcher的notifyKey函数,将NotifyKeyArgs交给InputDispatcher处理

* frameworks/native/services/inputflinger/InputListener.cpp
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push_back(new NotifyKeyArgs(*args));
}

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

在这里插入图片描述

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值