本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
Android10_原理机制系列_事件传递机制
talkchan · 511浏览 · 发布于2020-12-08 +关注

前言和概述

Android的输入设备,最常用的就是 触摸屏和按键 了。当然还有其他方式,比如游戏手柄,比如支持OTG设备,则可以链接鼠标、键盘等。
那么这些设备的操作 是如何传递到系统 并 控制界面的呢?系统如何知道是如何知道点击了某个界面按钮,按了某个按键,知道交给哪个应用处理的?
该篇主要介绍这些,即 输入事件从生成(存于设备节点中) 传递到目标View的过程。 在进入输入事件传递机制之前,首先了解一个东西---设备节点。

了解设备节点

当有输入事件时,Linux内核会将事件数据写入 设备节点 中,供上层读取最终传递到具体的View中。 该备节点 位于/dev/input/。

1. 查看:输入事件相关设备信息

与事件相关的设备信息位于:/proc/bus/input/devices。
下面是一部分,大致了解下。Name对应的Handlers注意下。

//查看所有事件相关设备信息$ adb shell cat /proc/bus/input/devices
    I: Bus=0019 Vendor=0000 Product=0000 Version=0000
    N: Name="ACCDET"
    P: Phys=
    S: Sysfs=/devices/virtual/input/input0
    U: Uniq=
    H: Handlers=event0
    B: PROP=0
    B: EV=23
    B: KEY=40 0 0 0 0 0 0 0 0 0 0 0 0 10 0 c0000 0 0 0
    B: SW=d4

    I: Bus=0019 Vendor=2454 Product=6500 Version=0010
    N: Name="mtk-kpd"
    P: Phys=
    S: Sysfs=/devices/platform/10010000.kp/input/input1
    U: Uniq=
    H: Handlers=event1
    B: PROP=0
    B: EV=3
    B: KEY=1000000 0 0 0 0 0 0 0 0 1c0000 0 0 0
    ...

2. 命令:getevent 和 sendenvent

2.1 getevent

通过设备的getevent命令,可以查看输入事件的信息。

//获取输入事件,这里是按了下电源键$ adb shell getevent
    add device 1: /dev/input/event0
      name:     "ACCDET"
    add device 2: /dev/input/event2
      name:     "sf-keys"
    add device 3: /dev/input/event3
      name:     "mtk-tpd"
    add device 4: /dev/input/event1
      name:     "mtk-kpd"
    /dev/input/event1: 0001 0074 00000001
    /dev/input/event1: 0000 0000 00000000
    /dev/input/event1: 0001 0074 00000000
    /dev/input/event1: 0000 0000 00000000//从/proc/bus/input/devices获取到要关注的设备的节点,可以单独获取//下面是获取也是按的电源键获取到的$ adb shell getevent  /dev/input/event1
    0001 0074 00000001
    0000 0000 00000000
    0001 0074 00000000
    0000 0000 00000000//多了解参数,这个-l就很清楚了//-l: label event types and names in plain text$ adb shell getevent -l /dev/input/event1
    EV_KEY       KEY_POWER            DOWN
    EV_SYN       SYN_REPORT           00000000
    EV_KEY       KEY_POWER            UP
    EV_SYN       SYN_REPORT           00000000

上面列出的3种,第一种没有参数 获取所有输入事件。加上 -l参数 的结果就很清晰了。
事件类型: 0001 即 EV_KEY,按键
事件代码: 0074 即 KEY_POWER,电源键
事件的值: 00000001 即 DOWN,按下;00000000 即 UP,抬起。

/dev/input/event1: 0001 0074 00000001 就是 电源键按下了。
/dev/input/event1: 0001 0074 00000000 就是 电源键抬起了。

注意:这里的值 都是 16进制的。

触摸屏幕也一样:

//触摸屏幕截取/dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   0000001e
/dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   00000000
/dev/input/event3: EV_ABS       ABS_MT_POSITION_X    000001b5
/dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    000001e1
/dev/input/event3: EV_SYN       SYN_MT_REPORT        00000000
/dev/input/event3: EV_SYN       SYN_REPORT           00000000

2.2 sendenvent

输入事件 设备节点也是可写的,通过sendevent可模拟用户输入。
但 sendevent 的参数是 十进制。

格式:sendevent <设备节点> <事件类型> <事件代码> <事件的值>

所以getevent中,电源按下/抬起的:事件类型即1,事件代码即116,事件的值即1/0。

//电源键按下$ adb shell sendevent /dev/input/event1 1 116 1//电源键抬起$ adb shell sendevent /dev/input/event1 1 116 0//由上述getevent个人理解,0 0 0上报后生效,同按一次电源键操作$ adb shell sendevent /dev/input/event1 0 0 0


概述

该篇也是基于Android10的代码分析。 该篇写时后期调整过几次标题编号,如果文中有参考的编号不对应,请指出。下面图片由于博客显示不是原图,可能部分不清晰,可以单独查看图片原图。
文章很长,但分的3个模块比较清晰,可以根据需要查看。
好,这里正式开始了。


下面是画的一张图,即本章的大致内容。也是方便自己查阅,主要介绍了 输入事件是如何从 设备节点中 传递到具体的View的。

整篇文章比较长,需要耐心。

若有不对,欢迎指出:

wms_ims

说明:

  • 图中3个红色虚线框: 即下面输入事件传递 介绍的3部分内容。IMS中事件的读取和派发;WMS中Window获取事件和传递;View中事件的传递和处理。

  • 图中2种颜色区域: 标识2个进程。system_server 和 应用进程。

  • 图中红色实线箭头: 文章介绍的 事件传递的 主要过程。

  • 图中2个红色虚线箭头: 列出了 两个比较常见的policy拦截的大致阶段 (当然不止这两个),说明了最终如何回调到PhoneWindowManager的同名方法。


输入事件的传递

输入事件的传递过程,如概述中所述,这里分成了3个部分来说明。

  • IMS中事件的读取和派发

  • WMS中Window获取事件和传递

  • View中事件的传递和处理

下面来具体看看。

1. IMS中事件的读取和派发

我们从IMS(InputManagerService)的创建和启动开始看。

IMS是在SystemServer的 startOtherServices() 方法中启动。(之前总结了AMS/PMS/WMS等,这里类似)

//SystemServer.javaprivate void startOtherServices() {
    WindowManagerService wm = null;
    InputManagerService inputManager = null;    try {        //参考1.1,创建IMS对象
        inputManager = new InputManagerService(context);
        wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,                new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);        //注册服务:"input"
        ServiceManager.addService(Context.INPUT_SERVICE, inputManager,                /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);        //参考1.4,设置回调
        inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());        //参考1.5, 启动
        inputManager.start();
    }    final InputManagerService inputManagerF = inputManager;
    mActivityManagerService.systemReady(() -> {        try {            if (inputManagerF != null) {
                inputManagerF.systemRunning();
            }
        }
    }, BOOT_TIMINGS_TRACE_LOG);
}

SystemServer中 关于IMS主要看3个内容:

  • new InputManagerService(),最后进入native 最终创建了InputManager及一系列相关内容。

  • inputManager.setWindowManagerCallbacks(),设置了回调,这里说明了 最终如何回调到PhoneWindowManager。

  • inputManager.start(),主要是IntputManager中两个线程运行起来。InputReaderThread读取和处理,InputDispatcherThread派发。

1.1 创建了InputManager

先看IMS的构造方法:

//InputManagerService.javapublic class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {    private static native long nativeInit(InputManagerService service,
        Context context, MessageQueue messageQueue);    
    public InputManagerService(Context context) {        this.mContext = context;        //创建了InputManagerHandler,其Looper是DisplayThead的Looper
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());        //进入native,并返回了mPtr
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }    
    private final class InputManagerHandler extends Handler {        public InputManagerHandler(Looper looper) {            super(looper, null, true /*async*/);
        }
    }
}

看到 this.mHandler 的Looper 是 DisplayThread的Looper。 这个Looper的消息队列 作为参数 传入到 nativeInit() 方法中。

下面进入 nativeInit() 。

//com_android_server_input_InputManagerService.cppstatic const JNINativeMethod gInputManagerMethods[] = {    /* name, signature, funcPtr */
    { "nativeInit",     "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J",
      (void*) nativeInit },
}static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    ...    //创建NativeInputManager
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());    //system/core/libutils/RefBase.cpp查看
    im->incStrong(0);    //返回给IMS,IMS后续会用到。IMS保存在mPtr。
    return reinterpret_cast<jlong>(im);
}//com_android_server_input_InputManagerService.cpp//NativeInputManager的构造方法:NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {    //创建InputManager
    mInputManager = new InputManager(this, this);
    defaultServiceManager()->addService(String16("inputflinger"),
            mInputManager, false);
}

这里看到,nativeInit() 中创建NativeInputManager。 返回给IMS的是 reinterpret_cast<jlong>(im) ,这是某种转换,可以看作是将NativeInputManager返回给了java层。

NativeInputManager中又创建了 InputManager。接着看InputManager的创建:

//InputManager.cppInputManager::InputManager(        const sp<InputReaderPolicyInterface>& readerPolicy,        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {    //创建InputDispatcher
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);    //创建InputReader
    mReader = createInputReader(readerPolicy, mClassifier);    //创建了两个线程 InputReaderThread和InputDispatcherThread
    initialize();
}void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}//InputReaderFactory.cppsp<InputReaderInterface> createInputReader(        const sp<InputReaderPolicyInterface>& policy,        const sp<InputListenerInterface>& listener) {    //EventHub作为参数 传入InputReader
    return new InputReader(new EventHub(), policy, listener);
}

上述代码,可以看到,InputManager中基本都是创建操作,创建了InputDispatcher、InputClassifier、InputReader、EventHub、InputReaderThread、InputDispatcherThread。

下面会逐步看到他们的作用 以及如何运行的。 这里先简单说明下其中几个主要的部分, 先有个大致了解。

  • EventHub:创建InputReader时 可以看到先创建了EventHub作为参数。

    EventHub 通过Linux内核的INotify与Epoll机制 监听设备,可直接访问 设备节点。通过 getEvents() 方法 读取设备节点的原始输入事件 数据。

    关于 EventHub的创建 这里不讨论了,这里只需简单了解它上面一点就可以了。它涉及内核和一些机制,暂时我也还不熟悉,哈哈。

  • InputReader:负责输入事件的获取。在独立线程(InputReaderThread)中 循环执行,有以下几个功能:

    功能1-通过 EventHub 不断 获取设备节点的 原始输入数据 。

    功能2-然后 进行加工处理后 交由 InputDispatcher分派 。

    功能3-它还有 管理 输入设备列表和配置 。

  • InputDispatcher:负责输入事件的派发。在独立线程(InputDispatcherThread)中运行,其保存有WMS的所有窗口信息。

    在接收到 InputReader 的输入事件后,会在窗口信息中找到合适的 窗口 并 派发消息。

  • InputReaderThread、InputDispatcherThread:因为InputReader 和 InputDispatcher都是耗时操作,因此创建 单独线程 来运行他们。这就是他们运行的线程。

创建完成后,他们是如何联系 并 运行的?

这个下面从 InputReaderThread和InputDispatcherThread两个线程的运行起来 理一下就可以大致了解。

1.2 InputReaderThread的运行:InputReader读取和处理事件

这里从InputReaderThread的运行开始介绍。

关于InputReaderThread和InputDispatcherThread 是如何运行起来,如何执行的threadLoop() ,后面也介绍了,请参考1.5

1.2.1 InputReader 获取输入事件

这里从 InputReaderThread::threadLoop() 开始跟踪:

//InputReaderBase.cppbool InputReaderThread::threadLoop() {
    mReader->loopOnce();    return true;
}//InputReader.cppvoid InputReader::loopOnce() {
    ...    //获取事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();        if (count) {            //处理输入事件,参考1.2.2
            processEventsLocked(mEventBuffer, count);
        }
        ...
    } // 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.
    //将事件 推送给 InputDispatcher 进行处理。参考1.2.2.3
    mQueuedListener->flush();
    ...
}

线程 运行起来后,会执行threadLoop,这里返回true,会循环执行该threadLoop方法。

threadLoop中调用 loopOnce,通过3步将消息 发送到InputDispatcher:

  • 通过 mEventHub->getEvents()获取所有 输入事件的原始数据。 这部分该篇不讨论

  • 通过 processEventsLocked() 处理输入事件。 参考1.2.2

  • 通过 mQueuedListener->flush() 推送到InputDispatcher。 参考1.2.2.3

1.2.2 InputReader 处理输入事件

看下处理输入事件的方法:processEventsLocked()

//InputReader.cppvoid InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;            while (batchSize < count) {                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId) {                    break;
                }
                batchSize += 1;
            }            //处理 真正的输入事件,参考 1.2.2.2
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {            //处理 设备增加、删除、扫描更新。参考1.2.2.1
            switch (rawEvent->type) {            case EventHubInterface::DEVICE_ADDED:
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);                break;
            ...
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

前面讲了InputReader有3个功能,这里可以看到功能2和3:对输入事件进行加工处理后 交由InputDispatcher;对输入设备 列表的管理和配置。先看功能3,在看功能2。

1.2.2.1 了解输入设备的管理

先看下功能3:对输入设备 列表的管理和配置。

这里以增加设备 为例,看下addDeviceLocked():

//InputReader.cppvoid 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
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);
    ...	//加入mDevices
    mDevices.add(deviceId, device);
    ...
}//mDevices 和 InputDevice定义(截取部分)//InputReader.hclass InputReader : public InputReaderInterface {
	KeyedVector<int32_t, InputDevice*> mDevices;
}class InputDevice {
	int32_t mId;//通过mId从EventHub中找到对应的输入设备
	std::vector<InputMapper*> mMappers;//处理上报的事件}

这里创建了一个InputDevice,然后将其加入到mDevices。mDevices 中保存了 设备的id 以及 对应的InputDevice。

EventHub 中也有个 mDevices,保存了 设备的id 和 对应的Device信息。 如下(截取部分):

//EventHub.hclass EventHub : public EventHubInterface {
    KeyedVector<int32_t, Device*> mDevices;
    
    struct Device {
        Device* next;        int fd; // may be -1 if device is closed  //设备节点 的文件句柄
        const int32_t id;        const std::string path;        const InputDeviceIdentifier identifier; //记录设备信息,设备的名称、供应商、型号等等
        std::unique_ptr<TouchVideoDevice> videoDevice;
        uint32_t classes;
		std::unique_ptr<VirtualKeyMap> virtualKeyMap;
        KeyMap keyMap;
	}
}

看下 创建InputDevice的过程,createDeviceLocked():

//InputReader.cppInputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,        const InputDeviceIdentifier& identifier, uint32_t classes) {    //创建InputDevice
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);    // External devices.
    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
        device->setExternal(true);
    }    // Keyboard-like devices.
    ...    if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }    // 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));
    }    return device;
}void InputDevice::addMapper(InputMapper* mapper) {
    mMappers.push_back(mapper);
}

创建了InputDevice后,进行一些设置。值得关注的是 一个InputDevice保存了多个 InputMapper,这些InputMapper保存在mMappers。

简单理一下:InputReader添加设备,首先创建了一个InputDevice,然后加入到mDevices中。而根据设备类型,可以创建多个InputMapper,这多个InputMapper保存在InputDevice中的mMappers中。

1.2.2.2 输入事件处理

接着看功能2,对输入事件进行处理 processEventsForDeviceLocked() :

//InputReader.cppvoid InputReader::processEventsForDeviceLocked(int32_t deviceId,        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);    //最终根据deviceId获得设备对应的InputDevice
    InputDevice* device = mDevices.valueAt(deviceIndex);    //事件交由 对应InputDevice处理。rawEvents是一组事件,可以注意下来源。                       
    device->process(rawEvents, count);
}//InputReader.cppvoid 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.
    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().c_str());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {			//将InputDvices对象中的mMappers依次取出来,调用process()进行处理
            for (InputMapper* mapper : mMappers) {
                mapper->process(rawEvent);
            }
        }
        --count;
    }
}

InputReader 获得某设备相关一组事件,然后找到对应InputDevice进行处理,执行 InputDevice::process() 。

InputDevice则将InputDvices对象中的mMappers依次取出来,调用process()进行处理。各个 InputMapper 对事件进行判断,若是属于自己处理的类型 再进行不同的处理。

下面 以键盘事件 为例说明,则InputMapper是KeyboardInputMapper:

//InputReader.cppvoid 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;
        }
        ...
    }
}//InputReader.cpp//内核上报的扫描码(scanCode),转换成Android系统使用的按键码(keyCode),重构NotifyArgs,加入 mArgsQueue队列void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;
    ...    //重构args
    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);    //插入到 mArgsQueue 队列中
    getListener()->notifyKey(&args);
}

这个处理过程 主要是 封装各个参数,重新构造成 NotifyKeyArgs ,然后 将构造的 NotifyKeyArgs对象加入 mArgsQueue队列。

加入到 mArgsQueue的过程, getListener()->notifyKey(&args):

//InputReader.cppInputListenerInterface* InputReader::ContextImpl::getListener() {    return mReader->mQueuedListener.get();
}
InputReader::InputReader(const sp<EventHubInterface>& eventHub,        const sp<InputReaderPolicyInterface>& policy,        const sp<InputListenerInterface>& listener) : ... {
    mQueuedListener = new QueuedInputListener(listener);
}//InputListener.cppQueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
        mInnerListener(innerListener) {
}void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push_back(new NotifyKeyArgs(*args));//push_back() 在Vector尾部插入}//InputListener.hclass QueuedInputListener : public InputListenerInterface {
    std::vector<NotifyArgs*> mArgsQueue;
};

在1.1中 创建InputReader时已经知道(可以回去看下),InputReader中的lister是InputClassifier对象,所以 QueuedInputListener中的innerListener 也就是 InputClassifier。

到这里再理一下:事件先交由了对应的InputDevice,然后找对处理该事件类型的InputMapper 进行处理。InputMapper 将事件等信息 构造了NotifyArgs,然后加入到了mArgsQueue中。

1.2.2.3 输入事件传入到 InputDispatcher

看 InputReader::loopOnce() 的最后一句:mQueuedListener->flush();

//InputListener.cppvoid QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();    //依次从mArgsQueue中取出NotifyArgs
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];        //mInnerListener是InputClassifier,上面(1.2.2.2最后)已经特意指出
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

如注释所说。接着看 args->notif(),接下来都是以键盘事件为例:

//NotifyArgs是所有args的超类。 以键盘为例,args即NotifyKeyArgsvoid NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}//InputManager.cppInputManager::InputManager(        const sp<InputReaderPolicyInterface>& readerPolicy,        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
}//InputClassifier.cpp//mListener是InputDispatcherInputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
      : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}void InputClassifier::notifyKey(const NotifyKeyArgs* args) {    // pass through
    mListener->notifyKey(args);
}

很清楚列出了,这里的mListener即InputDispatcher。所以最终走到了 InputDispatcher::notifyKey():

//InputDispatcher.cppvoid InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ...
    int32_t keyCode = args->keyCode;    //Meta + Backspace -> generate BACK; Meta + Enter -> generate HOME
    accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);

    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->displayId, args->action,
            flags, keyCode, args->scanCode, metaState, repeatCount,
            args->downTime, args->eventTime);

    android::base::Timer t;    //mPolicy是NativeInputManager,最终回调到PhoneWindowManager的同名方法。参考1.2.2.4
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    ...
    bool needWake;
    { // acquire lock
        mLock.lock();
        ...
        KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime,
                args->deviceId, args->source, args->displayId, 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();    //entry加入到mInboundQueue
    mInboundQueue.enqueueAtTail(entry);
    ...    return needWake;
}

最终,输入事件 由InputReader 获取处理后,传递到InputDispatcher,封装成EventEntry并加入mInboundQueue 队列了。

1.2.2.4 事件入队前的拦截:interceptKeyBeforeQueueing()

注意上面 InputDispatcher::notifyKey 中有 mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); 这句话,对PhoneWindowManager有过了解的,应该比较清楚。

这里就是 policy拦截的 比较常见的一处,从这最终回调 的是 PhoneWindowManager中的方法。

这个大致看下,这里mPolicy即 NativeInputManager ,所以直接看NativeInputManager::interceptKeyBeforeQueueing():

//com_android_server_input_InputManagerService.cppvoid NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
    ...
	wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
    ...
}//InputManagerService.javaprivate int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {    return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}

这个跟踪 就是执行了IMS中的interceptKeyBeforeQueueing()方法。

最终是如何调用到 PhoneWindowManager 中的方法的?

这里的mWindowManagerCallbacks是 wms中创建的InputManagerCallback对象。这个如何来的 参考1.4。

所以 mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags); 即:

//InputManagerCallback.javapublic InputManagerCallback(WindowManagerService service) {
    mService = service;
}@Overridepublic int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {    return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}

这里的mService.mPolicy就是PhoneWindowManager对象,在WMS创建时设置的。所以最终 回调的 PhoneWindowManager 中的 interceptKeyBeforeQueueing() 方法。 PhoneWindowManager 是 WindowManagerPolicy 的实现类。

1.3 InputDispatcherThread运行:InputDispatcher派发事件

前面讲到,输入事件 在InputDispatcher中 封装成EventEntry并加入mInboundQueue 队列了。接着看 InputDispatcher是如何继续处理 派发的。
如同InputReaderThread中介绍,这里直接看threadLoop()。

//frameworks/native/services/inputflinger/InputDispatcher.cppbool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();    return true;
}void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        //若mCommandQueue为空
        if (!haveCommandsLocked()) {            //参考1.3.1
            dispatchOnceInnerLocked(&nextWakeupTime);
        }        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        //参考1.3.4,运行 mCommandQueue 中命令
        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);
}//InputDispatcher.hEventEntry* mPendingEvent GUARDED_BY(mLock);
Queue<EventEntry> mInboundQueue GUARDED_BY(mLock);
Queue<EventEntry> mRecentQueue GUARDED_BY(mLock);
Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock);//InputDispatcher.cppbool InputDispatcher::haveCommandsLocked() const {    return !mCommandQueue.isEmpty();
}

1.3.1 InputDispatcher 取得输入事件

1.2.2.3讲到:输入事件 由InputReader 获取处理后,加入到了InputDispatcher中的 mInboundQueue 队列了。

事件派发 首先从 mInboundQueue队列中 取出输入事件,然后进行处理。

//InputDispatcher.cppvoid InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    ...    // Ready to start a new event.
    // If we don't already have a pending event, go grab one.
    if (! mPendingEvent) {        if (mInboundQueue.isEmpty()) {
            ...
        } else {            // Inbound queue has at least one entry.
            //从mInboundQueue中 出队 一个元素
            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.
        //这里注意下,ANR相关。这里先mark下,这篇不说明
        resetANRTimeoutsLocked();
    }    // Now we have an event to dispatch.
    // All events are eventually dequeued and processed this way, even if we intend to drop them.
    ...    switch (mPendingEvent->type) {
    ...    case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        ...        //分派事件
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);        break;
    }
    ...
}

取出事件后,然后分派,这里同样以 键盘按键事件为例,直接看 dispatchKeyLocked():

1.3.2 分派事件前处理

直接看 dispatchKeyLocked():

//InputDispatcher.cppbool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...    // Give the policy a chance to intercept the key.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {        //派发给用户,参考1.3.2.1
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {            //将InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible函数指针作为参数
            //执行postCommandLocked(),执行后 该函数被封装到CommandEntry 加入到 mCommandQueue队列
            CommandEntry* commandEntry = postCommandLocked(
                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);            //InputWindowHandle保存了窗口相关信息,由java层而来
            //获取焦点窗口的InputWindowHandle
            sp<InputWindowHandle> focusedWindowHandle =
                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));            if (focusedWindowHandle != nullptr) {                //InputChannel也是一种跨进程
                commandEntry->inputChannel =
                    getInputChannelLocked(focusedWindowHandle->getToken());
            }
            commandEntry->keyEntry = entry;
            entry->refCount += 1;            return false; // wait for the command to run
        } else {
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    } 
    ...    
    // Identify targets.
    //参考 1.3.2.3 
    std::vector<InputTarget> inputTargets;
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    ...    // Add monitor channels from event's or focused display.
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));    // Dispatch the key.
    //参考1.3.3
    dispatchEvenfentLocked(currentTime, entry, inputTargets);    return true;
}

InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    CommandEntry* commandEntry = new CommandEntry(command);
    mCommandQueue.enqueueAtTail(commandEntry);    return commandEntry;
}

1.3.2.1 事件分派前的拦截:interceptKeyBeforeDispatching()

通过postCommandLocked() 将 doInterceptKeyBeforeDispatchingLockedInterruptible 函数作为参数,封装到CommandEntry 最后加入到 mCommandQueue队列。这个函数并没有马上运行。

这个doInterceptKeyBeforeDispatchingLockedInterruptible():

//InputDispatcher.cppvoid InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
        CommandEntry* commandEntry) {
    KeyEntry* entry = commandEntry->keyEntry;
    ...
    sp<IBinder> token = commandEntry->inputChannel != nullptr ?
        commandEntry->inputChannel->getToken() : nullptr;
    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token,
            &event, entry->policyFlags);
    ...
    entry->release();
}

interceptKeyBeforeDispatching() 类似1.2.2.4。最终也是 调用到PhoneWindowManager 中的同名方法。

1.3.2.2 了解InputWindowHandle和InputChannel

InputWindowHandle:

InputWindowHandle保存了窗口相关信息,由java层而来。

关于InputWindowHandle 知道这大概是什么,不影响此篇理解,就没有完全跟踪下去。下面个人查看的路径开始,内容挺多,也没跟踪完全。先记录下,后续再看。

//WindowManagerService.javapublic int addWindow(Session session, IWindow client, int seq, ...) {
    displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
}//InputMonitor.java/* Updates the cached window information provided to the input dispatcher. */void updateInputWindowsLw(boolean force) {    if (!force && !mUpdateInputWindowsNeeded) {        return;
    }
    scheduleUpdateInputWindows();
}

InputChannel:

InputChannel也是一种跨进程, 本质也是socket。是一对创建的。

WindowState创建了一对InputChannel。server端注册到InputDispatcher,建立了Connect。client端返回给应用进程的窗口,ViewRootImpl.setView()时传入的参数mInputChannel。

InputDispatcher向其InputChannel中写入事件,窗口就可以从InputChannel中读取了。

简单列出下相关代码:

//WindowManagerService.javapublic int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState) {    final boolean openInputChannels = (outInputChannel != null
            && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);    if  (openInputChannels) {        //outInputChannel来自 ViewRootImpl.setView()时创建的
        win.openInputChannel(outInputChannel);
    }
}//WindowState.javavoid openInputChannel(InputChannel outInputChannel) {    if (mInputChannel != null) {        throw new IllegalStateException("Window already has an input channel.");
    }
    String name = getName();    //创建一对InputChannel,创建过程该篇不说明。      &                
                
                        
					
                
                

相关推荐

android下vulkan与opengles纹理互通

talkchan · 1175浏览 · 2020-11-23 10:37:39
Android 使用RecyclerView实现轮播图

奔跑的男人 · 2175浏览 · 2019-05-09 17:11:13
微软发布新命令行工具 Windows Terminal

吴振华 · 869浏览 · 2019-05-09 17:15:04
Facebook 停止屏蔽部分区块链广告

· 754浏览 · 2019-05-09 17:20:08
加载中

0评论

评论
大家好,我是一名专注技术开发的技术屌丝,有什么问题可以互相交流,一起学习进步,谢谢。
分类专栏
小鸟云服务器
扫码进入手机网页