Android 事件处理(1):Framework层如何获得输入事件与储存

Android 事件处理(1):Framework层如何获得输入事件与储存

背景

这篇写得不够明白,虽然很多知识都很全面,但是就是太乱了。

在QMMI开发,遇到key-testcase里面的按power键测试无法通过的问题。最后才知道,原来powerkey是特殊的按键,一般不对应用层发放。

改完这个以后,趁着有时间,我就展开了对于Android这块的学习。

分析代码版本:Android 9

参考:

基本概念

对于Android上层(Native framework/framework, c++/java)的分析,我一般采用的是由上而下的分析,即从其初始化(main,构造,onFirstRef())开始, 通常在其初始化时候,会重一些很重要的上下层的连接,如果由下往上看,会麻烦点,

按键分类

一个Android产品所有的按键分为三大类:

  • global key :通用按键,例如KEYCODE_TV,这些key我们可以修改frameworks/base/core/res/res/xml/global_keys.xm自己指定。
  • system key:系统按键,例如KEYCODE_HOME(home键) 、KEYCODE_VOLUME_DOWN(音量键)。
  • user key: 用户按键,例如KEYCODE_A、KEYCODE_B、KEYCODE_1等。

做Android开发的少不了对触摸、按键事件进行处理,对于手机来说,主要是手势移动、触摸按下等,而TV主要通过遥控器、按键操作,按键事件不同于触摸事件,必须先获得焦点,然后才能移动、选择。

按键输入事件主要包括按键(keyEvent),触屏(motionEvent)

Android框架

Android输入设备支持鼠标、键盘(按键)、触摸屏(单点、多点)、轨迹球等,这些设备所产生的输入事件Input Event从底层驱动开始经过input子系统核心层到达Event Handler事件层,最终把事件copy_to_user到用户空间,然后由用户空间层获取这些事件进行分发、传递。整个过程涉及到内核层、Framework层以及应用层。

拓展阅读:Android系统架构

输入事件的投递流程

事件投递的过程分为几个部分,源信息的采集->WindowManagerService分配->应用程序处理。

1、内核驱动上报事件

Android设备中的各种输入设备,例如键盘都需要编写驱动来进行控制支持。

有内核驱动基础的读者,可以看看下列的文章,没有涉及很深的驱动知识:

一句话概括来说,就是:驱动中控制并识别出了外设的某种输入情况,将这种情况通过input_report_key(按键)/input_report_abs(坐标)等接口进行上报。此后,允许应用层阶段将这种内容读取出来。

2、Framework层接收事件、对应用层发送

3、应用程序处理输入事件

接下来,我们就按键事件为例,看看按键事件是如何在Framework层、应用层进行传递的。

在startOtherServices中启动input服务

要分析输入系统,最好先从输入事件系统服务InputManagerService入手,这是了解输入系统的起点,所有其他相关类、线程都因此被创建或间接创建。

输入系统的核心服务是InputManagerService,由SystemServer启动。

路径:frameworks/base/services/java/com/android/server/SystemServer.java

Android系统启动时,Android部分第一个用户空间进程zygote首先fork创建了SystemServer对象,随后SystemServer创建了核心服务ActivityManagerServicePowerManagerServicePackageManagerServiceInputManagerService等,相关代码在SystemServer.java的run方法中,与InputManagerService相关部分:

private void startOtherServices() {
    final Context context = mSystemContext;
    WindowManagerService wm = null;
    InputManagerService inputManager = null;

    // 1
    inputManager = new InputManagerService(context);

    // 2
    wm = WindowManagerService.main(context, inputManager,
                                   mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                                   !mFirstBoot, mOnlyCore, new PhoneWindowManager());
    
    // 3
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                              DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                              /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);

    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    // 4
    inputManager.start();
}

InputManagerService的初始化

路径:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

构造InputManagerService时做了什么

    public InputManagerService(Context context) {
        this.mContext = context;
        // 创建了一个InputManagerHandler对象,参数是HandlerThread中创建的looper对象
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

        mUseDevInputEventForAudioJack =
                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        
        // 用Looper中的消息队列作为参数,调用本地方法nativeInit,
        // 返回C++中的NativeInputManager对象地址赋给mPtr,mPtr在1.5节会用到。
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

        String doubleTouchGestureEnablePath = context.getResources().getString(
                R.string.config_doubleTouchGestureEnableFile);
        mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
            new File(doubleTouchGestureEnablePath);

        //  把InputManagerInternal.class和LocalService对象作为一对映射添加到`ArrayMap<Class<?>, Object>`中
        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }
nativeInit方法
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
                        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    // 获得了C++层NativeMessageQueue对象(NativeMessageQueue对象是在创建Looper对象时创建的)
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    
	// 创建了NativeInputManager对象,返回到java层的nativeInit,并将地址赋给mPtr;
    // getLooper()获得c++层的Looper对象。
    // 注:在Handler机制中,此时Looper对象也已初始化,同时创建了管道并注册在epoll兴趣列表中
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
                                                    messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

新建了一个NativeInputManager对象,并且把这个对象返回了保存在了InputManagerService的mPtr对象中。

这只是保存了c层对象的地址,所以只要使用long保存地址就行了。

android_os_MessageQueue_getMessageQueue

路径:frameworks/base/core/jni/android_os_MessageQueue.cpp

sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobjectmessageQueueObj) {
    jlongptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
    return reinterpret_cast<NativeMessageQueue*>(ptr);
}
NativeInputManager

路径:framework/base/services/core/jni/com_android_server_input_InputManagerService.cpp

NativeInputManager::NativeInputManager(jobject contextObj,
                                       jobject serviceObj, const sp<Looper>& looper) :
                                         mLooper(looper), mInteractive(true)
                                         // 把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法
{
    JNIEnv* env = jniEnv();

    // 创建c++层中全局变量mContextObj, mServiceObj
    mContextObj = env->NewGlobalRef(contextObj);
    mServiceObj = env->NewGlobalRef(serviceObj);

    {
        AutoMutex _l(mLock);
        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
        mLocked.pointerCapture = false;
    }
    mInteractive = true;

    // 创建EventHub对象并作为参数创建InputManager对象
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

待会我们就分析一下EventHub以及InputManager都做了什么初始化。

   sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);

WindowManagerService的main方法

    wm = WindowManagerService.main(context, inputManager,
                                   mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                                   !mFirstBoot, mOnlyCore, new PhoneWindowManager());

从这里看出,InputManagerService和WindowManagerService是有联系的,因为事件的分发要先传到WindowManagerservice来分配给具体窗口,所以他们之间必然要有实例引用。

WindowManagerService是窗口的管理者,记录了系统中所有窗口的状态信息;所以它能够判断应该把事件投递到哪个应用进程处理,按键事件直接发给最前端的窗口,触摸消息要先计算这个触目点落在那个区域,然后传给相应的窗口单元。

路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                        onlyCore, policy), 0);
        return sInstance;
    }

main方法做了2件事情:

1、创建了WindowManagerService对象

2、将第二个参数InputManagerService对象,传递到WindowManagerService的构造函数中赋给了mInputManager

调用ServiceManager的方法

addService

    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                              /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);

把InputManagerService服务注册到ServiceManager中

setWindowManagerCallbacks

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());

把InputMonitor对象传递到InputManagerService中去,方便回调。

最终会通过该参数回调InputMonitor中的interceptKeyBeforeQueueing方法(7.2)

这个InputMonitor实现的是InputManagerService中的接口WindowManagerCallbacks,是WindowManagerService(WMS)跟InputManagerService(IMS)中InputDispatch之间沟通的桥梁,InputDispatch是负责事件分发的。

比如,一个key事件的拦截,首先是InputDispatch(cpp)的函数dispatchKeyLocked,然后经有NativeInputManager(cpp)调用到InputManagerService.java(java)中的interceptKeyBeforeDispatching,再由InputManagerService.java中那个回调就是wm.getInputMonitor(),也即是mWindowManagerCallbacks,这样就到了WindowManagerService.java,InputMonitor是WMS的一个成员变量,最后由InputMonitor.java转到具体的窗口管理策略PhoneWindowManager.java中,执行实际的拦截处理。

InputManagerService的start方法

路径:framework/base/services/core/java/com/android/server/input/InputManagerService.java

    public void start() {
        Slog.i(TAG, "Starting input manager");
        nativeStart(mPtr);

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);

        registerPointerSpeedSettingObserver();
        registerShowTouchesSettingObserver();
        registerAccessibilityLargePointerSettingObserver();

        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                updatePointerSpeedFromSettings();
                updateShowTouchesFromSettings();
                updateAccessibilityLargePointerFromSettings();
            }
        }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);

        updatePointerSpeedFromSettings();
        updateShowTouchesFromSettings();
        updateAccessibilityLargePointerFromSettings();
    }

start方法中有两句调用值得我们关心:

nativeStart(mPtr);
Watchdog.getInstance().addMonitor(this);

通过nativeStart 找到对应的c实现

nativeStart的本地实现:

public void start() {
        Slog.i(TAG, "Starting input manager");
        nativeStart(mPtr);
    	// ...
}

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

mptrNativeInputManager的对象地址,只不过是长整形;此处再转化成NativeInputManager型指针。

要彻底理解mPtr,就要看看InputManagerService的构造函数。

public InputManagerService(Context context){
    mPtr= nativeInit(this, mContext, mHandler.getLooper().getQueue());
}

这里也是通过naive方法nativeInit完成的mPtr的赋值,接着看native实现

// 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);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    // 创建一个native类型的NativeInputManager实例
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

nativeStart的传递过程: nativeStart —-> com_android_server_input_InputManagerService.cppnativeStart —-> InputManager.cppstart()

Watchdog

Watchdog.getInstance().addMonitor(this);

Watchdog是看门狗,一个单例类,addMonitor方法把InputManagerService对象添加到Watchdog,便于回调。

Q:这里的创建了看门狗,有什么用?

A:最终我们会知道,在getEvents中,这个看门狗是为了确保epoll不会因为超时而退出。

NativeInputManager中的2个重要对象

NativeInputManager::NativeInputManager(jobject contextObj,
                                       jobject serviceObj, const sp<Looper>& looper) :
                                         mLooper(looper), mInteractive(true)
                                         // 把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法
{
    // ...

    // 创建EventHub对象并作为参数创建InputManager对象
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

EventHub的本质是一个pipe(管道),它通过读取/dev/input/event文件来判断是不是有新的事件,然后通知InputReader

所以,InputReader并不是直接去读设备节点,而是通过与EventHub来完成读取事件。

EventHub

EventHub监听/dev/input/目录中的变化,并对应地更新好设备的输入事件变化。

在构造函数中进行监听目录变化

路径:framework/native/services/inputflinger/EventHub.cpp

EventHub::EventHub(void) :
         mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
         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);

    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);

   // INotify机制 初始化
    mINotifyFd = inotify_init(); 
    // 监控dev/input目录
    /*
       第一个参数:把监控项添加到mINotifyFd对象的监控列表中,
       第二个参数:DEVICE_PATH就是被监控对象,该对象一般是文件或目录,本文中被监控的DEVICE_PATH就是/dev/input目录;
       第三个参数:一个位掩码,表示被监控对象上发生的具体事件,可以由1个或多个掩码位或门组成。
           IN_DELETE:当被监控目录内删除文件或目录时触发该事件;
           IN_CREATE:当被监控目录内创建文件或目录时触发该事件。比如,插入、拔出鼠标时,就会触发该事件。
    */
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); 
    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;
    // 将这个iNotify的fd加入
    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];
    //创建了管道,用来唤醒epoll 
    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;
    // 把inotify对象mINotifyFd添加到epoll对象的兴趣列表中
    // 此处采用inotify与epoll机制结合起来检查文件
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
                        errno);

    int major, minor;
    getLinuxRelease(&major, &minor);
    // EPOLLWAKEUP was introduced in kernel 3.5
    mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}

在构造函数中,初始化必要的变量(记住这些变量的值,待会在getEvents的时候会用到),使用了INotify机制监控下文件的创建和删除动作,采用inotify机制监听/dev/input下文件或目录的移动、读取、写入或删除等事件,并且将INotify的fd加入epoll机制;还创建了一个pipe,将read端的fd加入了epoll,用来唤醒epoll。

从这里我们也看出,一共注册了mINotifyFd以及pipe的读写fd(mWakeReadPipeFd和mWakeWritePipeFd,用来实现唤醒通信)。

要使epoll_wair能继续执行,管道肯定要被写入数据才可以,因为管道有数据了,表明发生了I/O事件,epoll机制才会触发,也就意味着InputRead线程被唤醒。

读到此处,提出2个问题:

  • Q1:epoll机制一般有3个系统调用接口,而截止到此处,只提到epoll_create, epoll_ctl,还缺epoll_wait(否则epoll机制无法工作),那么epoll_wait的调用会在哪里呢?
  • Q2 :如果找到了epoll_wait,调用进程处于等待状态,那么肯定还有一处用来唤醒调用进程的代码,比如Looper的wake函数或者其他地方调用了write系统调用

记住这两个问题,有助于在分析多进程控制时的代码走向。

在openDeviceLocked中获取设备的属性

EventHub在构造时,进行了扫描目录,其中就调用了这个方法。

openDeviceLocked会调用一些的ioctl判断这个设备是否支持某些特性,并记录这些特性。

status_t EventHub::openDeviceLocked(const char *devicePath) {
    char buffer[80];

    ALOGV("Opening device: %s", devicePath);

    int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
    if(fd < 0) {
        ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
        return -1;
    }

    InputDeviceIdentifier identifier;

    // Get device name.
    if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
        //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.name.setTo(buffer);
    }

    // Get device driver version.
    int driverVersion;
    if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
        ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
        close(fd);
        return -1;
    }

    // Get device identifier.
    struct input_id inputId;
    if(ioctl(fd, EVIOCGID, &inputId)) {
        ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
        close(fd);
        return -1;
    }
    identifier.bus = inputId.bustype;
    identifier.product = inputId.product;
    identifier.vendor = inputId.vendor;
    identifier.version = inputId.version;

    // Get device unique id.
    if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
        //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.uniqueId.setTo(buffer);
    }

    // Fill in the descriptor.
    assignDescriptorLocked(identifier);

    // Allocate device.  (The device object takes ownership of the fd at this point.)
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);

    ALOGV("add device %d: %s\n", deviceId, devicePath);
    ALOGV("  bus:        %04x\n"
         "  vendor      %04x\n"
         "  product     %04x\n"
         "  version     %04x\n",
        identifier.bus, identifier.vendor, identifier.product, identifier.version);
    ALOGV("  name:       \"%s\"\n", identifier.name.string());
    ALOGV("  location:   \"%s\"\n", identifier.location.string());
    ALOGV("  unique id:  \"%s\"\n", identifier.uniqueId.string());
    ALOGV("  descriptor: \"%s\"\n", identifier.descriptor.string());
    ALOGV("  driver:     v%d.%d.%d\n",
        driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);

    // Load the configuration file for the device.
    loadConfigurationLocked(device);
    
    // Figure out the kinds of events the device reports.
    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
    ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
    ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
    ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
    ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);

    // See if this is a keyboard.  Ignore everything in the button range except for
    // joystick and gamepad buttons which are handled like keyboards for the most part.
    bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
            || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
                    sizeof_bit_array(KEY_MAX + 1));
    bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
                    sizeof_bit_array(BTN_MOUSE))
            || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
                    sizeof_bit_array(BTN_DIGI));
    if (haveKeyboardKeys || haveGamepadButtons) {
        device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
    }

    // See if this is a cursor device such as a trackball or mouse.
    if (test_bit(BTN_MOUSE, device->keyBitmask)
            && test_bit(REL_X, device->relBitmask)
            && test_bit(REL_Y, device->relBitmask)) {
        device->classes |= INPUT_DEVICE_CLASS_CURSOR;
    }

    // See if this is a rotary encoder type device.
    String8 deviceType = String8();
    if (device->configuration &&
        device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
            if (!deviceType.compare(String8("rotaryEncoder"))) {
                device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
            }
    }

    // See if this is a touch pad.
    // Is this a new modern multi-touch driver?
    if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
            && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
        // Some joysticks such as the PS3 controller report axes that conflict
        // with the ABS_MT range.  Try to confirm that the device really is
        // a touch screen.
        if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
            device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
        }
    // Is this an old style single-touch driver?
    } else if (test_bit(BTN_TOUCH, device->keyBitmask)
            && test_bit(ABS_X, device->absBitmask)
            && test_bit(ABS_Y, device->absBitmask)) {
        device->classes |= INPUT_DEVICE_CLASS_TOUCH;
    // Is this a BT stylus?
    } else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
                test_bit(BTN_TOUCH, device->keyBitmask))
            && !test_bit(ABS_X, device->absBitmask)
            && !test_bit(ABS_Y, device->absBitmask)) {
        device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
        // Keyboard will try to claim some of the buttons but we really want to reserve those so we
        // can fuse it with the touch screen data, so just take them back. Note this means an
        // external stylus cannot also be a keyboard device.
        device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;
    }

    // See if this device is a joystick.
    // Assumes that joysticks always have gamepad buttons in order to distinguish them
    // from other devices such as accelerometers that also have absolute axes.
    if (haveGamepadButtons) {
        uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
        for (int i = 0; i <= ABS_MAX; i++) {
            if (test_bit(i, device->absBitmask)
                    && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
                device->classes = assumedClasses;
                break;
            }
        }
    }

    // Check whether this device has switches.
    for (int i = 0; i <= SW_MAX; i++) {
        if (test_bit(i, device->swBitmask)) {
            device->classes |= INPUT_DEVICE_CLASS_SWITCH;
            break;
        }
    }

    // Check whether this device supports the vibrator.
    if (test_bit(FF_RUMBLE, device->ffBitmask)) {
        device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
    }
    if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
        // Load the virtual keys for the touch screen, if any.
        // We do this now so that we can make sure to load the keymap if necessary.
        status_t status = loadVirtualKeyMapLocked(device);
        if (!status) {
            device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
        }
    }

    // Load the key map.
    // We need to do this for joysticks too because the key layout may specify axes.
    status_t keyMapStatus = NAME_NOT_FOUND;
    if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
        // Load the keymap for the device.
        keyMapStatus = loadKeyMapLocked(device);
    }

    // Configure the keyboard, gamepad or virtual keyboard.
    if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        // Register the keyboard as a built-in keyboard if it is eligible.
        if (!keyMapStatus
                && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD
                && isEligibleBuiltInKeyboard(device->identifier,
                        device->configuration, &device->keyMap)) {
            mBuiltInKeyboardId = device->id;
        }

        // 'Q' key support = cheap test of whether this is an alpha-capable kbd
        if (hasKeycodeLocked(device, AKEYCODE_Q)) {
            device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
        }

        // See if this device has a DPAD.
        if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
                hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
                hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
                hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
                hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
            device->classes |= INPUT_DEVICE_CLASS_DPAD;
        }

        // See if this device has a gamepad.
        for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
            if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
                device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
                break;
            }
        }
    }

    // If the device isn't recognized as something we handle, don't monitor it.
    if (device->classes == 0) {
        ALOGV("Dropping device: id=%d, path='%s', name='%s'",
                deviceId, devicePath, device->identifier.name.string());
        delete device;
        return -1;
    }

    // Determine whether the device has a mic.
    if (deviceHasMicLocked(device)) {
        device->classes |= INPUT_DEVICE_CLASS_MIC;
    }

    // Determine whether the device is external or internal.
    if (isExternalDeviceLocked(device)) {
        device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
    }

    if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD)
            && device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        device->controllerNumber = getNextControllerNumberLocked(device);
        setLedForControllerLocked(device);
    }


    if (registerDeviceForEpollLocked(device) != OK) {
        delete device;
        return -1;
    }

    configureFd(device);

    ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
            "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
         deviceId, fd, devicePath, device->identifier.name.string(),
         device->classes,
         device->configurationFile.string(),
         device->keyMap.keyLayoutFile.string(),
         device->keyMap.keyCharacterMapFile.string(),
         toString(mBuiltInKeyboardId == deviceId));

    addDeviceLocked(device);
    return OK;
}

InputManager

路径:frameworks/native/services/inputflinger/InputManager.cpp

// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
                                       jobject serviceObj, const sp<Looper>& looper) :
                                         mLooper(looper), mInteractive(true)
                                         // 把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法
{
    // ...

    // 创建EventHub对象并作为参数创建InputManager对象
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

构造

InputManager::InputManager(
    const sp<EventHubInterface>& eventHub,
    const sp<InputReaderPolicyInterface>& readerPolicy,
    const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    // 新建了InputDispatcher和InputReader两个对象
    // 将InputReader和InputDispatch建立了关联
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    // 然后调用了initialize函数
    initialize();
}

第一个参数是刚创建的EventHub对象,第二、三个参数都是this。

Q:当前NativeInputManager对象,传递过去后分别是InputReaderPolicyInterface、InputDispatcherPolicyInterface类型,这是为何?

A:因为NativeInputManager继承了InputReaderPolicyInterface、InputDispatcherPolicyInterface类,实际类型还是NativeInputManager。

继续看。

构造InputDispatcher对象
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
    mPolicy(policy),
    mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
    // ...
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
    mLooper = new Looper(false);

    mKeyRepeatState.lastKeyEntry = NULL;

    policy->getDispatcherConfiguration(&mConfig);
}

把传递过来的NativeInputManager对象赋给mPolicy(该NativeInputManager对象又是InputDispatcherPolicyInterface接口类型);

然后又初始化了其他变量;

并调用了mLooper = new Looper(false); 创建并初始化了Looper对象,在Looper构造方法创建了管道并采用epoll机制把管道加入到兴趣列表中。

注意,此处创建的Looper对象是在InputDispatcher中的,与主线程中的Looper没有关系。

Q3 既然有了epoll_ctl,那肯定某处会调用epoll_wait

构造InputReader对象
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);
 
    { // acquire lock
        AutoMutex_l(mLock);
 
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

第一个参数是EventHub对象,

第二个policy赋值给mPolicy,policy是InputReaderPolicyInterface接口类型,实际是NativeInputManager对象类型。

第三个listener实际是InputDispatcher类型,因为InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此policy也是InputListenerInterface对象类型

mQueuedListener = new QueuedInputListener(listener);

把InputListenerInterface对象类型listener作为参数创建QueuedInputListener对象,传递过去后赋给mInnerListener变量。

initialize初始化2个线程对象
void InputManager::initialize() { 
    // 这两个变量就是两个线程我们就不看其构造函数了。
    
    // 在InputReaderThread这个线程中,不断调用InputReader的loopOnce函数,这个函数会调用EventHub的getEvents函数
    mReaderThread = new InputReaderThread(mReader);  
    mDispatcherThread = new InputDispatcherThread(mDispatcher);  
}  

一个InputReaderThread负责从驱动节点读取Event,一个InputDispatcherThread负责把event分发出去。

Q:既然initialize创建了2个线程对象,那么会在哪里启动线程呢?

A:InputManagerService调用Start函数后,会在InputManager对象中,开启InputReaderThread,和InputDispatcherThread线程。

在start中启动线程

// frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }

    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);

        mDispatcherThread->requestExit();
        return result;
    }

    return OK;
}

既然InputReaderThread是继承了Android Framework中的Thread类,那么执行run方法的时候,最终会调用到threadLoop()方法,以一直循环处理数据(直到 threadLoop中返回return false;为止)。

这里参考了:Android Framework 中的Thread 类

Thread被创建 ->Thread中的run被调用-> __threadLoop()被调用, readyToRun()被调用, 然后循环调用threadLoop()

并且在threadLoop()返回false时,可以退出循环。

// frameworks/native/services/inputflinger/InputReader.h
/* Reads raw events from the event hub and processes them, endlessly. */
class InputReaderThread : public Thread {
public:
    explicit InputReaderThread(const sp<InputReaderInterface>& reader);
    virtual ~InputReaderThread();

private:
    sp<InputReaderInterface> mReader;

    virtual bool threadLoop();
};
InputDispatcherThread调用dispatchOnce

分发器线程InputDispatcherThread

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
InputReaderThread调用loopOnce
// frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() {
    // 一直调用InputReader的loopOnce函数
    mReader->loopOnce();
    return true;
}

InputReaderThread轮询设备节点是不是有新的事件发生

关于上述的2个线程对象所调用的对应方法,篇幅毕竟长,我们接下来进行分析。

InputManager中的2个重要线程对象

从上面我们知道,InputManager::initialize初始化的2个线程对象会在start方法中通过run以后执行threadLoop中的内容。

InputReader的loopOnce

InputReaderThread轮询设备节点是不是有新的事件发生。

这个函数先从EventHub中通过调用getEvents获取事件,再调用processEventLocked进行处理。

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices; // 描述输入设备特征和特性的类
    
    //...

    // 获取事件
    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);
    }

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

在getEvents中获取各个输入事件

getEvents完成了对于设备事件的读取与处理。

分析getEvents的时候要动态去分析:

  • 当没有事件的时候,它做了什么
  • 当事件来临以后,它又做了什么

先看第一次执行吧,也就是输入事件还没来的时候;代码是怎么样的。

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);

    AutoMutex _l(mLock);

    struct input_event readBuffer[bufferSize];

    // 这里的 event 以及 buffer 都 看成是 数组
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

        // 1、打开设备
        //  mNeedToReopenDevices,mClosingDevices一开始都为false,不执行
        if (mNeedToReopenDevices) {
            // ... pass;
        }
        while (mClosingDevices) {
            // ... pass;
            break; 
        }

        // 2、扫描设备
        // mNeedToScanDevices的初始值为true,会执行scanDevicesLocked
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked(); // 注意这个函数。
            mNeedToSendFinishedDeviceScan = true;
        }

        // mOpeningDevices不为空,进入到while循环
        while (mOpeningDevices != NULL) {
            /*
            该while循环把所有设备Device的内容保存到RawEvent结构体中
            */
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n",
                 device->id, device->path.string());
            mOpeningDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            // 事件被成功添加到RawEvent
            event->type = DEVICE_ADDED;
            event += 1;
            // 意味着结束设备扫描。
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }

        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }

        // 3、监听设备
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            break;
            // ...

            // 虽然现在没有执行到这里,但是需要留意一下这段代码
            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                if (eventItem.events & EPOLLIN) {
                    ALOGV("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 {
                    ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                            eventItem.events);
                }
                continue;
            }

            // ... pass;
        }

        // 扫描到设备的添加或者删除
        //  mPendingINotify初始化为false,跳过这个if
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked(); // 读取INotify的内容,然后执行相应的打开设备或者关闭设备。
            deviceChanged = true;
        }

        // 如果设备改变了,添加或者删除设备了,就要重新循环
        //  第一次时,deviceChanged也为false,跳过这个if
        if (deviceChanged) {
            continue;
        }

        // 此时条件不成立
        if (event != buffer || awoken) {
            break;
        }

        mPendingEventIndex = 0;

        mLock.unlock();
        release_wake_lock(WAKE_LOCK_ID);

        // 等待,数据加入mPendingEventItems
        // epoll将内容放入mPendingEventItems,等下次循环处理
        /* 一开始,队列等待中的事件都为0(mPendingEventIndex = 0);
        调用epoll_wait机制监控epoll对象mEpollFd的兴趣列表中的事件,陷入等待
        
         当有事件以后,epoll_wait返回发生事件的个数赋给pollResult,
         此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环
        */
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
        mLock.lock();

        if (pollResult == 0) 
            mPendingEventCount = 0;
            break;
        }

        if (pollResult < 0) {
            mPendingEventCount = 0;

            if (errno != EINTR) {
                ALOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            mPendingEventCount = size_t(pollResult);
        }
    } // end: for(;;;)
    return event - buffer;// 返回读取的事件个数
}

这个函数实现比较长,我们分为几个部分来看。

第二个参数RawEvent* buffer:存储底层上报的输入事件,因为包含各种事件,没有过滤,称为原始事件

第三个参数bufferSize:已分配input_event的buffer大小,256字节,input_event是所获取事件的结构体:

struct input_event {
    struct timevaltime;
    __u16type;
    __u16code;
    __s32value;
};

time:事件的执行时间

type:事件类型,EV_KEY(按键),EV_ABS(绝对坐标,如触摸屏),EV_REL(相对坐标,轨迹,如鼠标),除此之外,还有EV_LED(LED),EV_FF(力反馈)等

code:如果type是按键,code表示原始按键码值;如果是EV_REL,code表示轨迹类型,包括REL_X(X轴方向),REL_Y(Y轴方向);如果是EV_ABS,code表示坐标类型,ABS_X(X轴坐标),ABS_Y(Y轴坐标)

value:事件值,如果type是按键,按下时value为1,松开时为0;如果是EV_REL,value表示x,y方向上的偏移量;如果是EV_ABS,value表示x,y轴坐标值

打开设备

虽然在第一次调用这个方法时不执行下列的语句,但是在此后配置发生改变时,标记重新打开所监听的设备。

        if (mNeedToReopenDevices) { // 第一次调用时,mNeedToReopenDevices => false
            mNeedToReopenDevices = false;

            ALOGI("Reopening all input devices due to a configuration change.");

            closeAllDevicesLocked(); // 如果之前打开了设备,则关闭所有的设备,准备重新扫描
            mNeedToScanDevices = true;
            break;
        }

		// 回收注册的资源
        while (mClosingDevices) {
            Device* device = mClosingDevices;
            ALOGV("Reporting device closed: id=%d, name=%s\n",
                 device->id, device->path.string());
            mClosingDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
            event->type = DEVICE_REMOVED;
            event += 1;
            delete device;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }
扫描目录

第一次循环中,由于mNeedToScanDevices 的初始值是 true,那么会进行目录扫描。

因为InputReaderThread是循环执行的,进程又一次又一次进入到getEvents函数中,开始执行for循环,

scanDevicesLocked完成以后,就不再执行扫描获取设备,除非遇到了设备的插拔。

        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked(); // 注意这个函数。
            mNeedToSendFinishedDeviceScan = true;
        }

注意scanDevicesLocked这个函数:调用了scanDirLocked函数扫描/dev/input下的所有设备,然后调用openDeviceLocked函数

void EventHub::scanDevicesLocked() {
    status_t res = scanDirLocked(DEVICE_PATH);
    if(res < 0) {
        ALOGE("scan dir failed for %s\n", DEVICE_PATH);
    }
    if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
        createVirtualKeyboardLocked();
    }
}

status_t EventHub::scanDirLocked(const char *dirname)
{
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    dir = opendir(dirname);
    if(dir == NULL)
        return -1;
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';
    while((de = readdir(dir))) {
        if(de->d_name[0] == '.' &&
           (de->d_name[1] == '\0' ||
            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;
        strcpy(filename, de->d_name);
        openDeviceLocked(devname);
    }
    closedir(dir);
    return 0;
}
openDeviceLocked

至于openDeviceLocked,使用ioctrl获取设备的各种参数,并标记设备对应的属性,最后将设备注册进去。

status_t EventHub::openDeviceLocked(const char *devicePath) {  
    char buffer[80];  

    ALOGV("Opening device: %s", devicePath);  

    int fd = open(devicePath, O_RDWR | O_CLOEXEC);
    // devicePath为传过来的设备节点路径,比如按键为:/dev/input/event0

    // ...... 调用ioctl,
    /*
    获取device的各种参数,其中包括类型检查,比如:
        检查该设备是按键或键盘,就把设备对象的classes属性标为INPUT_DEVICE_CLASS_KEYBOARD,
        表示当前发生输入事件的设备是按键或键盘;
        
    然后做了一系列动作:
        获取设备名称、检查是否已经包含在容器mExcludedDevices中、获取该设备的驱动版本、
        获取设备身份信息保存到inputid结构体中并赋给InputDeviceIdentifier对象中的变量、
        获取设备的物理地址和设备唯一id号,设置文件描述符fd为非阻塞模式O_NONBLOCK属性等。
    */

    // 添加设备
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
    ALOGV("add device %d: %s\n", deviceId, devicePath);

    //...   
    /*
     从底层获取了该设备的所有信息后,创建临时Device对象代表该设备,比如,代表按键设备节点对象

    在这段代码之后,系统做了很多事情,
             比如,继续判断设备是不是轨迹球或鼠标、多点触摸屏、单点触摸屏、操作杆、开关、振动器、虚拟按键;
             如果是键盘、操纵杆等,就配置其布局。
    这一过程的代码与本文关联性不大,没有贴上,如果研究鼠标、触摸屏等,可参考这段代码。
    */

    addDeviceLocked(device);//加入mDevices  
    return 0;  
}
addDeviceLocked
void EventHub::addDeviceLocked(Device* device) {
    mDevices.add(device->id, device);
    device->next = mOpeningDevices;
    mOpeningDevices = device;
}

把设备id、设备对象作为一对映射保存到mDevices容器中,并设device->next = mOpeningDevices

在一开始构建EventHub对象的时候,mOpeningDevices初始化为0,表示下一个设备还没有;而此时,已经设mOpeningDevices为当前设备对象。

监听设备的事件

此时,由于没有事件发生,因此mPendingEventIndex < mPendingEventCount不成立。

        bool deviceChanged = false;
        // 第一次时,不执行,跳过;
        // 但是在有了输入事件以后
        while (mPendingEventIndex < mPendingEventCount) {
            // ...
        }

只有到有输入事件来临的时候,这个while (mPendingEventIndex < mPendingEventCount)才会执行。

什么时候会被唤醒

getEvents中,epoll监听mWakeReadPipeFd的变化,而待会我们就会知道,由于wake方法会往这个pipe写入数据,从而唤醒epoll_wait

wake()会写入'w',因此mWakeReadPipeFd从这个管道中读取的字符有且只有'w'

EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
        mOpeningDevices(0), mClosingDevices(0),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false), mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
    // ...
    int wakeFds[2];
    result = pipe(wakeFds);

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
            
    // ...

    eventItem.data.u32 = EPOLL_ID_WAKE;
    // 添加进 epoll 监听
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
}

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    // ...
    
    // 等待 
    int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    // ...
}

void EventHub::wake() {
    ALOGV("wake() called");

    ssize_t nWrite;
    do {
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite != 1 && errno != EAGAIN) {
        ALOGW("Could not write wake signal, errno=%d", errno);
    }
}

我们先不在乎谁调用了wake唤醒了epoll_wait的阻塞;当唤醒了以后,int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);返回发生事件的个数赋给pollResult。

此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环。

    // 等待,数据加入mPendingEventItems
    // epoll将内容放入mPendingEventItems,等下次循环处理
    /* 一开始,队列等待中的事件都为0(mPendingEventIndex = 0);
    调用epoll_wait机制监控epoll对象mEpollFd的兴趣列表中的事件,陷入等待
    
     当有事件以后,epoll_wait返回发生事件的个数赋给pollResult,
     此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环
    */
    int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    mLock.lock();

    if (pollResult == 0) 
        mPendingEventCount = 0;
        break;
    }

    if (pollResult < 0) {
        mPendingEventCount = 0;

        if (errno != EINTR) {
            ALOGW("poll failed (errno=%d)\n", errno);
            usleep(100000);
        }
    } else {
        mPendingEventCount = size_t(pollResult);
    }

我们需要从for循环的头部重新开始看了。

唤醒之后做了什么
        bool deviceChanged = false;
        // 有了输入事件以后,满足这个条件
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];

注意接下来的3个if,都是用来判断此时的事件是什么。

为什么不写成switch-case呢?

            /* 判断此时的事件是什么*/
            // 1、如果是设备的插拔,那么跳过。
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                // notify监测到有新设备,或者设备拔出,跳过。
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                }
                continue;
            }

            // 2、如果是被wake唤醒,那么就继续循环
            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                if (eventItem.events & EPOLLIN) {
                    /*
                    从管道中读取数据,该数据就是一个字符w,
                    执行完后continue跳出循环,又执行到epoll_wait,InputReader又被阻塞等待;
                    
                    然后,
                        wake()函数又调用write写入字符w触发了epoll_wait,
                        唤醒了InputReader线程,然后又开始执行for循环
                    */
                    ALOGV("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));
                }
                continue;
            }

            // ...(省略的这部分是 准备处理真正的设备输入事件)

        }

我们看到,如果是通过调用wake唤醒的epoll_wait,其实最终会因为重新进入循环而导致InputReader又被阻塞等待。

Q:这样循环下去的意义何在?

A:循环的意义:确保InputReader线程始终处于活动状态,能及时获取输入事件。

因为按键、触摸等输入事件还没有发生时,InputReader线程处于阻塞状态,当一定的时间过去后,仍然没有输入事件,epoll_wait可能超时返回0,因此,下段语句就会成立,跳出for循环返回了:

if (pollResult == 0) {
    // Timed out.
    mPendingEventCount = 0;
    break;
}

也就是说,如果此时有事件发生的话,因为epoll_wait还没有执行到,可能会漏掉事件处理。为了保障InputReader线程能够及时获取事件,系统创建了看门狗线程,每隔一段时间向管道中写入数据唤醒InputReader线程去读取事件,这样,Q5就有了答案,看门狗WatchDog实际上也是一个线程,只不过会定时发送消息给InputReader线程读取输入事件。

我们继续看上段代码中被省略的部分;到了这里,就是准备处理真正的设备输入事件了。

当有输入事件时

根据设备id从容器中取出设备对象,如果发生了可读事件,调用read从该设备文件中读取事件保存到readBuffer中

            // 获取设备ID(此时eventItem.data.u32的值是设备id)
            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
            if (deviceIndex < 0) {
                continue;
            }

            // 分析是否是对应设备的事件,并填充event值
            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                
                // 从设备文件中读取 输入子系统的事件 到 readBuffer
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                
                /* 出错判断与处理*/
                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                    ALOGW("could not get event, removed? (fd: %d size: %" PRId32
                            " bufferSize: %zu capacity: %zu errno: %d)\n",
                            device->fd, readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
                    closeDeviceLocked(device);
                } else if (readSize < 0) {
                    if (errno != EAGAIN && errno != EINTR) {
                        ALOGW("could not get event (errno=%d)", errno);
                    }
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    ALOGE("could not get event (wrong size: %d)", readSize);
                } else {
                    
                    /* 成功执行以后的处理 */
                    /*
                       把所有的事件并取出来,
                       并逐一保存到input_event结构体变量iev中,
                       同时,然后计算相应的事件执行时间,
                       再把input_event结构体变量中的内容保存到原始事件RawEvent中,
                    */
                    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++) {
                        struct input_event& iev = readBuffer[i];
                        ALOGV("%s got: time=%d.%06d, 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);

                        if (iev.type == EV_MSC) {
                            if (iev.code == MSC_ANDROID_TIME_SEC) {
                                device->timestampOverrideSec = iev.value;
                                continue;
                            } else if (iev.code == MSC_ANDROID_TIME_USEC) {
                                device->timestampOverrideUsec = iev.value;
                                continue;
                            }
                        }

                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                + nsecs_t(iev.time.tv_usec) * 1000LL;
                        ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);

                        if (event->when >= now + 10 * 1000000000LL) {
                            // Double-check.  Time may have moved on.
                            nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);
                            if (event->when > time) {
                                ALOGW("An input event from %s has a timestamp that appears to "
                                        "have been generated using the wrong clock source "
                                        "(expected CLOCK_MONOTONIC): "
                                        "event time %" PRId64 ", current time %" PRId64
                                        ", call time %" PRId64 ".  "
                                        "Using current time instead.",
                                        device->path.string(), event->when, time, now);
                                event->when = time;
                            }
                        }
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    if (capacity == 0) {
                        mPendingEventIndex -= 1;
                        break;
                    }
                }
            } else if (eventItem.events & EPOLLHUP) {
                ALOGI("Removing device %s due to epoll hang-up event.",
                        device->identifier.name.string());
                deviceChanged = true;
                closeDeviceLocked(device);
            }else {
                ALOGW("Received unexpected epoll event 0x%08x for device %s.",
                        eventItem.events, device->identifier.name.string());
            }

此后,由于事件是设备输入事件,那么下列语句被跳过。

        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        }

        // Report added or removed devices immediately.
        if (deviceChanged) {
            continue;
        }

那么,来到了if (event != buffer || awoken)这里。

由于event指向的内存现在已经保存了事件,而buffer还是指向首地址,两者必定不相等。

        // 当有内容可读时,event指针向前走,所以event 不等于 buffer
        if (event != buffer || awoken) {
            break; //跳出 for 循环
        }

之后,执行函数末尾的return event - buffer;,返回读取事件的个数。

C语言功底好的读者都知道,如果两个指针向同一个数组,它们就可以相减,其为结果为两个指针之间的元素数目。

在processEventsLocked中处理事件与储存

把获取的原始按键码值转化成应用层按键值,对按键事件进行检查,处理特殊按键,最后把按键事件封装成EventEntry后临时存储到InboundQueue队列中等待处理。

processEventLocked处理了2件事情:

  • 设备的输入事件,以及调用mapper完成一些特殊处理。
  • 处理关于设备本身的2个事件:设备增加 与 设备移除。
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type; // 表示事件的类型
        size_t batchSize = 1;
        // 正常的事件都小于EventHubInterface::FIRST_SYNTHETIC_EVENT
        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;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif
            // 处理设备事件
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize); 
        } else {
            switch (rawEvent->type) {
            case EventHubInterface::DEVICE_ADDED: //设备增加
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::DEVICE_REMOVED: //设备移除 
                removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::FINISHED_DEVICE_SCAN:
                handleConfigurationChangedLocked(rawEvent->when);
                break;
            default:
                ALOG_ASSERT(false); // can't happen
                break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}
processEventsForDeviceLocked

处理设备对应的事件

rawEvents相当于EventHub::getEvents方法中的buffer,即Linux 应用层调用read系统调用获取底层驱动所获取的输入子系统数据。

// frameworks/native/services/inputflinger/EventHub.h
struct RawEvent {
    nsecs_t when;
    int32_t deviceId;
    int32_t type;
    int32_t code;
    int32_t value;
};


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

    // 执行process进行 input子系统 数据处理
    device->process(rawEvents, count);
}
根据mapper进行process
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
    // We cannot simply ask each mapper to process them in bulk because mappers may
    // have side-effects that must be interleaved.  For example, joystick movement events and
    // gamepad button presses are handled by different mappers but they should be dispatched
    // in the order received.
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {

        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
            }
        } 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];
                // 调用mappers的process方法。
                mapper->process(rawEvent);
            }
        }
        --count;
    }
}

这个时候,mapper对应的process就会针对本次输入事件做额外的处理。

processKey

我们以最熟悉的按键事件来说吧,按键事件对应的是KeyboardInputMapper对象。

因此,执行到了KeyboardInputMapper的process方法,该方法中,会把获得原始按键值转化成framework层按键值(暂时这么称呼)。

调用顺序是device->process(rawEvents, count) —-> mapper->process(rawEvent) —-> KeyboardInputMapper::processKey

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;

        if (isKeyboardOrGamepadKey(scanCode)) {
            // 注意这里
            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
        }
        break;
    }
    case EV_MSC: {
        if (rawEvent->code == MSC_SCAN) {
            mCurrentHidUsage = rawEvent->value;
        }
        break;
    }
    case EV_SYN: {
        if (rawEvent->code == SYN_REPORT) {
            mCurrentHidUsage = 0;
        }
    }
    }
}

再根据按键类型,执行到KeyboardInputMapper::processKey函数

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

    // ...主要代码在最后部分

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

NotifyKeyArgs

NotifyKeyArgs类用来描述一个按键事件,包括特性属性和notify函数;倒数第二句调用构造函数初始化特征属性,NotifyKeyArgs特征属性包括:

  • nsecs_t eventTime:按键事件的发生时间
  • int32_t deviceId:按键的设备id
  • uint32_t source:输入源类型,此处由1.5.3.1-2节设置为AINPUT_SOURCE_KEYBOARD,表示按键或键盘
  • uint32_t policyFlags:按键功能标志位,比如,如果是外插键盘,应该具备唤醒设备的功能,就把policyFlags设为POLICY_FLAG_WAKE,如果是内置设备,就不设置该标志。
  • int32_t action:按键动作标志,是按下按键还是松开按键
  • int32_t flags:操作按键的主体是谁,如果是用户操作,就设置为AKEY_EVENT_FLAG_FROM_SYSTEM
  • int32_t keyCode:按键值,按键值在底层和上层值不一样,上层的值一般在KeyEvent.java中设置
  • int32_t scanCode:原始按键值,就是从底层获取到的按键值
  • int32_t metaState:功能按键状态,比如如果有ALT、SHIFT、CTRL按键按下,就会设置一个具体的状态,如果是普通按键,比如方向键,数字键等,此变量就设为0
  • nsecs_t downTime:按键按下的时间,与eventTime不一样,eventTime从底层传过来,downTime是在framework层计算的变量

getListener

//frameworks/native/services/inputflinger/InputReader.cpp
InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}

mQueuedListener创建InputReader时创建的,创建QueuedInputListener时期参数listener实际类型是InputDispatcher,而InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此listener也是InputListenerInterface对象类型。

而notifyKey在InputDispatcher中有实现,这样进程就执行到了InputDispatcher的notifyKey函数。

如果在InputDispatcher中没有实现,就要查看InputDispatcherInterface, InputListenerInterface中是否有。

notifyKey

检查这个key,必要时将这个进行key进行特殊处理;如果可以,唤醒InputDispatcherThread::pollOnce

// frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    
    // 对传递过来的按键事件进行检查、验证
    if (!validateKeyEvent(args->action)) {
        return;
    }

    uint32_t policyFlags = args->policyFlags;
    int32_t flags = args->flags;
    int32_t metaState = args->metaState;
    // 检查按键是否按下、松开
    if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
        policyFlags |= POLICY_FLAG_VIRTUAL;
        flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
    }
    //设置flags标致为AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY,虚拟硬件
    // 比如手机上除了屏幕显示外的MENU、HOME、BACK键
    if (policyFlags & POLICY_FLAG_FUNCTION) {
        metaState |= AMETA_FUNCTION_ON;
    }

    // 设置metaState为meta键标志
    // 设置该标志表示按键事件是可信任的,
    // 比如,一个直接与Android机器相连接的按键设备
    policyFlags |= POLICY_FLAG_TRUSTED;

    int32_t keyCode = args->keyCode;
    if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {
        int32_t newKeyCode = AKEYCODE_UNKNOWN;
        if (keyCode == AKEYCODE_DEL) {
            newKeyCode = AKEYCODE_BACK;
        } else if (keyCode == AKEYCODE_ENTER) {
            newKeyCode = AKEYCODE_HOME;
        }
        if (newKeyCode != AKEYCODE_UNKNOWN) {
            AutoMutex _l(mLock);
            struct KeyReplacement replacement = {keyCode, args->deviceId};
            mReplacedKeys.add(replacement, newKeyCode);
            keyCode = newKeyCode;
            metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
        }
    } else if (args->action == AKEY_EVENT_ACTION_UP) {
        // In order to maintain a consistent stream of up and down events, check to see if the key
        // going up is one we've replaced in a down event and haven't yet replaced in an up event,
        // even if the modifier was released between the down and the up events.
        AutoMutex _l(mLock);
        struct KeyReplacement replacement = {keyCode, args->deviceId};
        ssize_t index = mReplacedKeys.indexOfKey(replacement);
        if (index >= 0) {
            keyCode = mReplacedKeys.valueAt(index);
            mReplacedKeys.removeItemsAt(index);
            metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
        }
    }

    // 把参数传递给KeyEvent对象event
    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->action,
            flags, keyCode, args->scanCode, metaState, 0,
            args->downTime, args->eventTime);

    android::base::Timer t;
    // 拦截按键进行特殊处理,比如HOME、音量键、拨号键,电源等
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
                std::to_string(t.duration().count()).c_str());
    }

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

        if (shouldSendKeyToInputFilterLocked(args)) {
            mLock.unlock();

            policyFlags |= POLICY_FLAG_FILTERED;
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }

            mLock.lock();
        }

        int32_t repeatCount = 0;
        // 创建KeyEntry对象并把按键事件作为该对象的内容
        KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);

        // 根据输入事件的类型作进一步处理
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

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

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    // 一开始没有数据为空
    bool needWake = mInboundQueue.isEmpty();
    
    // 把按键事件对象KeyEntry插入到mInboundQueue队尾
    mInboundQueue.enqueueAtTail(entry);
    traceInboundQueueLengthLocked();

    // 用来切换app,如果是HOME、挂号按键,就设为true,表示可以唤醒InputDispatcherThread来分发按键事件,
    // 否则,不唤醒InputDispatcherThread,那么按键就不会处理
    switch (entry->type) {
    case EventEntry::TYPE_KEY: {
        // Optimize app switch latency.
        // If the application takes too long to catch up then we drop all events preceding
        // the app switch key.
        KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
        if (isAppSwitchKeyEventLocked(keyEntry)) {
            if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
                mAppSwitchSawKeyDown = true;
            } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
                if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
                    ALOGD("App switch is pending!");
#endif
                    mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
                    mAppSwitchSawKeyDown = false;
                    needWake = true;
                }
            }
        }
        break;
    }
    // ...
    }

    return needWake;
}

wake函数会调用write系统调用向管道中写入数据唤醒读端线程,读端线程就是InputDispatcherThread线程(执行到了Looper对象的pollOnce函数时处于阻塞状态),此时epoll机制检测到管道中有数据写入事件发生。

读端线程InputDispatcherThread被唤醒,InputDispatcherThread线程从dispatchOnce返回到threadLoop,threadLoop的返回值为true,表明是线程是循环执行的,又一次调用dispatchOnce继续执行。唤醒了InputDispatcherThread。

addDeviceLocked

我们来看看mapper这些东西是如何添加进去的。

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

    // 获取设备的ID
    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);

    if (device->isIgnored()) {
        ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
                identifier.name.string());
    } else {
        ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
                identifier.name.string(), device->getSources());
    }

    mDevices.add(deviceId, device);
    bumpGenerationLocked();

    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
        notifyExternalStylusPresenceChanged();
    }
}

removeDeviceLocked只不过是addDeviceLocked的反向操作而已,不再介绍了。

获取设备Class

看看如何获取到设备的类(实际上,id也是通过相同的方式获取的)。

之前注册的设备都绑定了一个ID,用于寻找设备。

identifier与classes的赋值在EventHub::openDeviceLocked()中就已经完成了。

这个class属性会用在创建设备时用于设置设备的属性以及mapper。

InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    if (device == NULL) return InputDeviceIdentifier();
    return device->identifier;
}

uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    if (device == NULL) return 0;
    return device->classes;
}

EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
    if (deviceId == BUILT_IN_KEYBOARD_ID) {
        deviceId = mBuiltInKeyboardId;
    }
    ssize_t index = mDevices.indexOfKey(deviceId);
    return index >= 0 ? mDevices.valueAt(index) : NULL;
}

添加mapper

根据classes所支持的属性而增加各种Mapper。

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

    // External devices.
    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
        device->setExternal(true);
    }

    // Devices with mics.
    if (classes & INPUT_DEVICE_CLASS_MIC) {
        device->setMic(true);
    }

    // ...

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

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

    // External stylus-like devices.
    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
        device->addMapper(new ExternalStylusInputMapper(device));
    }

    return device;
}

void InputDevice::addMapper(InputMapper* mapper) {
    mMappers.add(mapper);
}

当我们在InputReader增加设备后,就可以在processEventsForDeviceLocked中来进行处理啦。

现在,按键事件读取后的处理已经完成,cpu就把控制权从InputReaderThread交给了InputDispatcherThread线程,开始分发事件。

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

一开始,CommandQueue队列中没有任何命令,haveCommandsLocked为false,if语句成立,执行dispatchOnceInnerLocked

dispatchOnceInnerLocked
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();

    // ...

    // Ready to start a new event.
    // If we don't already have a pending event, go grab one.
    /* mPendingEvent初始化为空,mInboundQueue队列也为空,满足此条件,执行*/
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {
            if (isAppSwitchDue) {
                // The inbound queue is empty so the app switch key we were waiting
                // for will never arrive.  Stop waiting for it.
                resetPendingAppSwitchLocked(false);
                isAppSwitchDue = false;
            }

            // Synthesize a key repeat if appropriate.
            if (mKeyRepeatState.lastKeyEntry) {
                if (currentTime >= mKeyRepeatState.nextRepeatTime) {
                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
                } else {
                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                    }
                }
            }

            // Nothing to do if there is no pending event.
            if (!mPendingEvent) {
                return;
            }
        } else {
            // Inbound queue has at least one entry.
            mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();
        }

        // Poke user activity for this event.
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(mPendingEvent);
        }

        // Get ready to dispatch the event.
        resetANRTimeoutsLocked();
    }

    // Now we have an event to dispatch.
    // All events are eventually dequeued and processed this way, even if we intend to drop them.
    ALOG_ASSERT(mPendingEvent != NULL);
    bool done = false;
    DropReason dropReason = DROP_REASON_NOT_DROPPED;
    if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
        dropReason = DROP_REASON_POLICY;
    } else if (!mDispatchEnabled) {
        dropReason = DROP_REASON_DISABLED;
    }

    if (mNextUnblockedEvent == mPendingEvent) {
        mNextUnblockedEvent = NULL;
    }

    switch (mPendingEvent->type) {
    case EventEntry::TYPE_CONFIGURATION_CHANGED: {
        ConfigurationChangedEntry* typedEntry =
                static_cast<ConfigurationChangedEntry*>(mPendingEvent);
        done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
        dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
        break;
    }

    case EventEntry::TYPE_DEVICE_RESET: {
        DeviceResetEntry* typedEntry =
                static_cast<DeviceResetEntry*>(mPendingEvent);
        done = dispatchDeviceResetLocked(currentTime, typedEntry);
        dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
        break;
    }

    case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        if (isAppSwitchDue) {
            if (isAppSwitchKeyEventLocked(typedEntry)) {
                resetPendingAppSwitchLocked(true);
                isAppSwitchDue = false;
            } else if (dropReason == DROP_REASON_NOT_DROPPED) {
                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 = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }

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

    default:
        ALOG_ASSERT(false);
        break;
    }

    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;

        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}

mPendingEvent初始化为空,mInboundQueue队列也为空,紧接着来到这个if语句:

if (isAppSwitchDue) {
    // The inbound queue is empty so the app switch key we were waiting
    // for will never arrive.  Stop waiting for it.
    resetPendingAppSwitchLocked(false);
    isAppSwitchDue = false;
}

如果isAppSwitchDue为true,表明切换按键(比如HOME、拨号键)的执行时间还没有到,系统就自动放弃这一次的按键处理,并把isAppSwitchDue设置false。

紧接着看这里:

if (mKeyRepeatState.lastKeyEntry) { //  mKeyRepeatState.lastKeyEntry初始化为false,跳过

这条语句,继续:

if (!mPendingEvent) {
    return;
}

mPendingEvent初始化为空,表示还没有等待处理的输入事件,if语句为true,执行return返回

runCommandsLockedInterruptible
if (runCommandsLockedInterruptible()) {
    nextWakeupTime = LONG_LONG_MIN;
}

一开始,没有命令待处理,runCommandsLockedInterruptible返回false,跳过语句;如果有命令的话,就取出来处理

pollOnce
mLooper->pollOnce(timeoutMillis);

看到熟悉的语句了,pollOnce一直执行到epoll_wait监控管道读取端:

  • 如果管道有I/O事件发生,epoll_wait就会返回发生事件的个数;
  • 如果还没有事件,进程InputDispatcherThread在此等待,

这就回答了在构造InputDispatcher中所newLooper

既然有Looper的pollOnce方法,就有对应的唤醒方法,在哪里呢?

这2条线程是如何协作的

现在我们来讨论inputDispatchThreadInputReaderThread的协作。

建立关联

前面说过这两个线程是在frameworks/native/services/inputflinger/InputManager.cpp中创建的。

InputManager::InputManager(...){

       mDispatcher= new InputDispatcher(dispatcherPolicy);
       mReader= new InputReader(eventHub, readerPolicy, mDispatcher);
}

在创建时,就将InputReader和InputDispatch建立了关联。

Listener

在InputReader的looponce中,事件是通过InputDispatch来送到Listener的。

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices; // 描述输入设备特征和特性的类
    
    //... 省略掉获取事件与处理事件


    // 这里的 mQueuedListener实际是对InputDispatch的一个封装
    mQueuedListener->flush();
}

这样InputDispatch就可以持续获取到设备中发生的事件。并且可以向InputReader注册监听多种事件。

class QueuedInputListener : public InputListenerInterface {
protected:
    virtual ~QueuedInputListener();

public:
    explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener);

    // 向InputReader注册监听多种事件
    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
    virtual void notifyKey(const NotifyKeyArgs* args);
    virtual void notifyMotion(const NotifyMotionArgs* args);
    virtual void notifySwitch(const NotifySwitchArgs* args);
    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);

    void flush();

private:
    sp<InputListenerInterface> mInnerListener;
    Vector<NotifyArgs*> mArgsQueue;
};

注册监听

我们以key事件为例,看下是怎么注册这个监听的。

源头在EventHub,我们知道EventHub负责原始事件的读取,然后通知到InputReader,EventHub还监听设备的插拔,当然这也是事件。

void InputReader::loopOnce() {

    //...

    // 获取事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    if (count) {
        // 处理事件
        processEventsLocked(mEventBuffer, count);
    }

    // ...
}

getEvents是EventHub很重要的一个方法,就是获取原事件,接着 processEventsLocked开始处理事件。

如果是添加了输入设备,event->type 会等于 DEVICE_ADDED,而FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,同时我们知道系统肯定是要创建一个输入设备的,那么processEventsLocked会执行ifelse分支:

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {

    //这里实际是一个循环,处理读取到的所有原始事件。

    RawEvent*rawEvent = rawEvents;

    // ...处理读取到的所有原始事件。
    if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
        // ..
    } else {
        // 执行这里,此时 type == EventHubInterface::FIRST_SYNTHETIC_EVENT
        switch (rawEvent->type) {
            case EventHubInterface::DEVICE_ADDED: //设备增加
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
        }
    }

    // ...
}

我们直接找往InputReader注册监听的地方,我们知道如果这个设备的class会被配置,并添加对应的mapper。

比如,键盘设备添加到了Vector<InputMapper*>mMappers;

通知

接下来processEventsLocked会执行 mMappers的所有设备的process()方法,当然也会执行到KeyboardInputMapperprocess方法,最终调用到了processKey

void KeyboardInputMapper::process(constRawEvent* rawEvent) {

    // ...
    if (isKeyboardOrGamepadKey(scanCode)) {
        processKey(rawEvent->when, rawEvent->value != 0, scanCode,usageCode);
    }
}

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
                                     int32_t usageCode) {

    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
                       down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
                       AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,            
                       downTime);

    // 获取的listener实际就是InputReader中mQueuedListener,我们看mQueuedListener的处理。
    getListener()->notifyKey(&args);

}

这里获取的listener实际就是InputReader中mQueuedListener,我们看mQueuedListener的处理。

// frameworks/native/services/inputflinger/InputListener.cpp
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    // 把NotifyKeyArg 这个实例 添加到 Vector<NotifyArgs*> mArgsQueue 中。
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

还记得前面说InputReader的looponce中,把事件通知给监听这是通过mQueuedListener->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();
}

在这个方法中会回调具体实例的notify函数,比如 NotifyKeyArgs的notify函数。

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

注意这里的 mInnerListener来自QueuedInputListener的构造函数,

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

QueuedInputListener又是在InputReader的构造函数实例化的,同时传入的参数是const sp<InputListenerInterface>& listener,实际就是InputDispatcher,因此,最终调用的是InputDispatch::notifyKey

class InputDispatcher : public InputDispatcherInterface

class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface;

通过上面的类声明,我们看到InputDispatcher 间接继承了 InputListenerInterface

也就是这样,InputReader就把事件传到了InputDispatcher

posted @ 2021-06-03 15:25  schips  阅读(1576)  评论(0编辑  收藏  举报