Android Input 系列专题【inputflinger事件的读取与分发】

目录

一、InputManager与IMS的联系

1、system_server进程如何引用libinputflinger.so?

2、InputManagerService

3、NativeInputManagerService

4、JNI如何引用到InputManager.cpp?

5、InputManager的封装

二、InputReader事件读取

1、EventHub

2、InputReader的轮询

1)InputReader开启线程轮询

2)loopOnce

3、InputReader原始事件数据解析

1)合成事件与物理事件的隔离

2)processEventsForDeviceLocked

4、InputDevice如何装饰所有子设备?

1)InputMapper装饰者

2)InputMapper所有派生物理设备

三、InputDispatcher事件分发

1、InputReader如何通过notify通知到InputDispatcher?

1)InputReader的监听器InputListener

2)notify分离不同类型的事件的处理逻辑

2、InputDispatcher的轮询

1)InputDispatcher开启线程轮询

2)dispatchOnce事件类型的处理

3、InputDispatcher事件的分发

1)dispatchConfigurationChangedLocked

2)dispatchKeyLocked

3)dispatchMotionLocked

四、日志与案例

1、日志之触摸屏点击事件

2、日志之触摸移动事件

3、日志之音量+键按压事件

4、案例之点击切换相关控件高概率出现异常飞线


Android输入系统在native中的核心工作就是,从Linux驱动设备节点中读取事件,然后将这个事件进行分发,这两项工作分别交给了InputReader和InputDispatcher来做。

他们的源码都属于native层inputflinger里面的一部分,如下架构:

根据Android.bp和目录结构来看,可以进行如下总结:

  • inputflinger并不是一个独立的native进程,它以库的形式存在,即被FW的IMS进行调用
  • inputflinger的核心代码为InputManager.cpp,因此InputManager充当了JNI的角色与FW的IMS进行交互
  • libinputflinger.so依赖于InputReader.cpp和InputDispatcher.cpp,但进行了分离把他们分别封装成为libinputreader.so和libinputdispatcher.so来进行引用,因此后续调试可以专门针对这三个库进行推送调试

最后用一张网图来梳理他们的关系:

一、InputManager与IMS的联系

前文已经提到了InputManager.cpp主要用来和fw层的InputManagerService来进行联系,这里从源码的角度来解析一下他们之间到底是如何联系起来的。其实还是使用了传统的方式,让InputMnagerService通过JNI的方式来调用InputManager.cpp,因此inputflinger的代码是运行在system_server进程里面的

1、system_server进程如何引用libinputflinger.so?

FrameWork层最重要的两个framework.jar和services.jar,已经SystemServer等一系列系统服务都被定义在aosp/framework/base/目录中,有如下信息:

​frameworks/base/services/core/jni/Android.bp  --->集成libinputflinger
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java  --->Android输入系统FW层IMS服务
frameworks/native/services/inputflinger/Android.bp  --->定义libinputflinger
frameworks/native/services/inputflinger/InputManager.cpp  --->Android输入系统Native层管理类
​

因此他们同属一个模块和进程,libinputflinger以库的方式被引用进去。

2、InputManagerService

//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
    static final String TAG = "InputManager";
    // To enable these logs, run: 'adb shell setprop log.tag.InputManager DEBUG' (requires restart)
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    //定义mNative是一个java类,其内部定义了所有的native方法
    private final NativeInputManagerService mNative;
    private final Context mContext;
    private final InputManagerHandler mHandler;
    private DisplayManagerInternal mDisplayManagerInternal;
    //...省略...
    static class Injector {
        private final Context mContext;
        private final Looper mLooper;
        Injector(Context context, Looper looper) {
            mContext = context;
            mLooper = looper;
        }
        Context getContext() {
            return mContext;
        }
        Looper getLooper() {
            return mLooper;
        }
        //获取NativeInputManagerService实例
        NativeInputManagerService getNativeService(InputManagerService service) {
            return new NativeInputManagerService.NativeImpl(service, mLooper.getQueue());
        }
        void registerLocalService(InputManagerInternal localService) {
            LocalServices.addService(InputManagerInternal.class, localService);
        }
    }
    public InputManagerService(Context context) {
        this(new Injector(context, DisplayThread.get().getLooper()));
    }
    InputManagerService(Injector injector) {
        mContext = injector.getContext();
        mHandler = new InputManagerHandler(injector.getLooper());
        //获取NativeInputManagerService实例
        mNative = injector.getNativeService(this);
        //后续把mNative实例对象添加到各个模块,为了让IMS系统的native层更加方便的与fw各个模块进行交互
        mSettingsObserver = new InputSettingsObserver(mContext, mHandler, this, mNative);
        mKeyboardLayoutManager = new KeyboardLayoutManager(mContext, mNative, mDataStore, injector.getLooper());
        mBatteryController = new BatteryController(mContext, mNative, injector.getLooper());
        mKeyboardBacklightController =  KEYBOARD_BACKLIGHT_CONTROL_ENABLED ? new KeyboardBacklightController(mContext,  mNative, mDataStore, injector.getLooper()) : new KeyboardBacklightControllerInterface() {};
        mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
        mUseDevInputEventForAudioJack = mContext.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="   + mUseDevInputEventForAudioJack);
        injector.registerLocalService(new LocalService());
    }
    public void start() {
        Slog.i(TAG, "Starting input manager");
        //非常重要,初始化native层世界所有的C++类
        mNative.start(); 
        Watchdog.getInstance().addMonitor(this);// Add ourselves to the Watchdog monitors.
    }
    //......省略...
}

根据如上代码可以进行如下总结:

  • IMS通过持有NativeInputManagerService对象来控制native世界
  • IMS在服务启动之后通过mNative.start方法来调用native世界对象的start方法进行启动

3、NativeInputManagerService

NativeInputManagerService就是一个单纯的接口,定义了一堆需要和native世界交互的方法。

最终把这些接口方法转换为native层方法,即这些方法的实现全部都在native层里面的那些C++库里面。

这些native方法的根据包名转换到:com_android_server_input_InputManagerService.cpp

4、JNI如何引用到InputManager.cpp?

  • NativeInputManager对象的构造:改对象直接实例化了InputManager.cpp
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
//NativeInputManager构造函数
NativeInputManager::NativeInputManager(jobject serviceObj, const sp<Looper>& looper)
      : mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();
    //拿到FW的IMS对象实例
    mServiceObj = env->NewGlobalRef(serviceObj);
    //实例化C++世界的核心类InputManager.cpp
    InputManager* im = new InputManager(this, *this);
    mInputManager = im;
    //直接像c++世界的servicemanager注册服务inputflinger,注意该方法只是注册服务实例对象,并不是注册进程,这里的进程还是system_server进程
    defaultServiceManager()->addService(String16("inputflinger"), im);
}
//NativeInputManager析构函数
NativeInputManager::~NativeInputManager() {
    JNIEnv* env = jniEnv();
    env->DeleteGlobalRef(mServiceObj);
}
  • nativeInit和nativeStart的实现
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static NativeInputManager* getNativeInputManager(JNIEnv* env, jobject clazz) {
    return reinterpret_cast<NativeInputManager*>(
            env->GetLongField(clazz, gNativeInputManagerServiceImpl.mPtr));
}
static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == nullptr) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    static std::once_flag nativeInitialize;
    NativeInputManager* im = nullptr;
    std::call_once(nativeInitialize, [&]() {
        // Create the NativeInputManager, which should not be destroyed or deallocated for the lifetime of the process.
        //核心代码:创建NativeInputManager实例化对象,其实就是封装了InputManager.cpp
        im = new NativeInputManager(serviceObj, messageQueue->getLooper());
    });
    LOG_ALWAYS_FATAL_IF(im == nullptr, "NativeInputManager was already initialized.");
    return reinterpret_cast<jlong>(im);
}

static void nativeStart(JNIEnv* env, jobject nativeImplObj) {
    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
    //核心代码:拿到NativeInputManager实例化对象,并调用start,就是调用了InputManager.start方法
    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

    综上,IMS与InputManager的关系如下,system_server进程的IMS通过JNI的方式启动了native世界的InputManager.cpp的start方法。

    5、InputManager的封装

    综上所述,在开机过程中,IMS跟随system_server进程而启动,在启动过程中,通过JNI的方式,调用inputmanager.cpp进行启动,inputmanager.cpp持有inputreader.cpp和inoutdispatcher.cpp,且通知他们启动。

    二、InputReader事件读取

    InputReader事件读取的原理就是直接从驱动设备节点/dev/input/里面读取事件,通常有多个输入设备,在我手上的这台Android平板的该目录如下:

    • 在我触摸屏幕的时候event3节点文件中会打印数据:

    • 在我按音量-的时候event0节点文件中会打印数据
    • 在我按音量+的时候event2节点文件中会打印数据
    • 输入getevent命令可以直接获取/dev/input/里面的事件:

    1、EventHub

    InputReader事件读取的原理就是直接从驱动设备节点/dev/input/里面读取事件。如果对应设备有事件,例如鼠标事件,屏幕触摸事件,驱动会直接向这些节点写入数据。

    • 设备节点的路径:他的日志可以过滤EventHub

    size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
        ……
        RawEvent* event = buffer;
        size_t capacity = bufferSize;
        bool awoken = false;
        for (;;) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            // 如果需要,重新打开输入设备
            if (mNeedToReopenDevices) {
                mNeedToReopenDevices = false;
                closeAllDevicesLocked();
                mNeedToScanDevices = true;
                break; // return to the caller before we actually rescan
            }
            // 报告最近添加/删除的任何设备
            for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
                std::unique_ptr<Device> device = std::move(*it);
                ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
                event->when = now;
                event->deviceId = (device->id == mBuiltInKeyboardId)
                        ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id;
                event->type = DEVICE_REMOVED;
                event += 1;
                it = mClosingDevices.erase(it);
                mNeedToSendFinishedDeviceScan = true;
                if (--capacity == 0) {
                    break;
                }
            }
            // 扫描新的输入设备,第一次为true
            if (mNeedToScanDevices) {
                mNeedToScanDevices = false;
                // 打开 /dev/input/ 目录下的input设备后,将其注册到epoll的监控队列中。
                scanDevicesLocked();
                mNeedToSendFinishedDeviceScan = true;
            }
            // 报告设备添加事件
            while (!mOpeningDevices.empty()) {
                std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
                mOpeningDevices.pop_back();
                ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
                event->when = now;
                event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
                // 对于新开的设备,生成一个 DEVICE_ADDED 类型的 RawEvent 并添加到输出缓冲区。
                event->type = DEVICE_ADDED;
                event += 1;
                // 尝试为设备匹配相应的视频设备(如触摸屏),并将设备信息插入到 mDevices 映射中
                for (auto it = mUnattachedVideoDevices.begin(); 
                        it != mUnattachedVideoDevices.end(); it++) {
                    std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
                    if (tryAddVideoDeviceLocked(*device, videoDevice)) {
                        // videoDevice was transferred to 'device'
                        it = mUnattachedVideoDevices.erase(it);
                        break;
                    }
                }
                auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
                if (!inserted) {
                    ALOGW("Device id %d exists, replaced.", device->id);
                }
                mNeedToSendFinishedDeviceScan = true;
                if (--capacity == 0) {
                    break;
                }
            }
            // 发送设备扫描完成通知
            if (mNeedToSendFinishedDeviceScan) {
                mNeedToSendFinishedDeviceScan = false;
                event->when = now;
                event->type = FINISHED_DEVICE_SCAN;
                event += 1;
                if (--capacity == 0) {
                    break;
                }
            }
            // 处理待处理事件队列中的下一个输入事件
            bool deviceChanged = false;
            while (mPendingEventIndex < mPendingEventCount) {
                const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
                // 处理INotify事件
                if (eventItem.data.fd == mINotifyFd) {
                    if (eventItem.events & EPOLLIN) {
                        mPendingINotify = true;
                    }
                    //...省略....
                    continue;
                }
                // 处理唤醒管道事件
                if (eventItem.data.fd == mWakeReadPipeFd) {
                    if (eventItem.events & EPOLLIN) {
                        ALOGV("awoken after wake()");
                        awoken = true;
                        char wakeReadBuffer[16];
                        ssize_t nRead;
                        do {
                            nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer));
                        } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer));
                    }
                    //...省略....
                    continue;
                }
                // 处理输入设备事件
                Device* device = getDeviceByFdLocked(eventItem.data.fd);
                //...省略....
                // 处理触摸屏输入事件
    			if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) {
    				//...省略....
    				continue;
    			}
                // 处理标准输入设备事件
                // 对于标准输入设备(如键盘、鼠标等),检查是否有可读事件 (EPOLLIN)
                if (eventItem.events & EPOLLIN) {
                    // 从device中得到fd后再去读取设备,获取input事件
                    int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
                    if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                        // 读取失败或设备已被移除,则关闭该设备
                        deviceChanged = true;
                        closeDeviceLocked(*device);
                    } 
                    //...省略....
                    else {
                        // 读取成功,将每个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];
                            event->when = processEventTimestamp(iev);
                            event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
                            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) {
                    // 处理挂起事件 (EPOLLHUP),则关闭该设备
                    deviceChanged = true;
                    closeDeviceLocked(*device);
                }
                //...省略....
            }
            // 如果存在未处理的 INotify 事件并且所有待处理事件都已处理完毕
            if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
                mPendingINotify = false;
                // 处理设备节点的变化
                readNotifyLocked();
                deviceChanged = true;
            }
            // 报告添加或移除的设备
            if (deviceChanged) {
                continue;
            }
            //...省略....
            // 等待更多事件的到来
            int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
            //...省略....
     
        // 所有操作完成后,返回我们读取的事件数
        return event - buffer;
    }
    • events3节点:从下面的代码来看有些怀疑专门针对屏幕的定制,但具体代码有带研究

    2、InputReader的轮询

    接下来就是InputReader的主要逻辑,她封装了事件的读取和事件的分发前阶段。

    1)InputReader开启线程轮询

    InputReader的构造函数和start与stop方法如下,其核心就是通过封装的InputThread类来创建线程,线程名为InputReader,并在EventHub收到事件的时候唤起,调用loopOnce函数。

    2)loopOnce

    //frameworks/native/services/inputflinger/reader/InputReader.cpp
    void InputReader::loopOnce() {
        int32_t oldGeneration;
        int32_t timeoutMillis;
        bool inputDevicesChanged = false;
        std::vector<InputDeviceInfo> inputDevices;
        std::list<NotifyArgs> notifyArgs;
        //流程1:读取驱动设备节点的事件数据,并以RawEvent数组的方式进行封装
        std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);
        { // acquire lock
            std::scoped_lock _l(mLock);
            mReaderIsAliveCondition.notify_all();
            if (!events.empty()) {
                //流程2:成功读取到事件之后,通过processEventsLocked进行解析,并以NotifyArgs数组的方式进行封装
                notifyArgs += processEventsLocked(events.data(), events.size());
            }
            //....省略....
        } // release lock
        //...省略...
        //流程3:遍历mQueuedListener队列中所有的监听器,并发布事件,其实就是进行所有设备的事件分发
        notifyAll(std::move(notifyArgs));
        //流程4:更新队列
        mQueuedListener.flush();
    }
    void InputReader::notifyAll(std::list<NotifyArgs>&& argsList) {
        for (const NotifyArgs& args : argsList) {
            mQueuedListener.notify(args);
        }
    }

    在一次轮询中,核心的任务就两个:

    • 读取原始数据格式的RawEvent事件,然后并封装成NotifyArgs事件
    • 然后遍历所有监听器,进行事件分发,实际上搞了一个装饰者模式,将不同类型的事件分发出去,这块逻辑我们在事件分发的时候详细介绍

    3、InputReader原始事件数据解析

    InputReader通过processEventsLocked函数对从驱动设备节点读取出来的原始数据事件的处理,但是原始数据事件里面不一定全是由物理设备触发上来的数据,还有一些事件是系统发送出来的,所有首先需要对真实物理事件和合成事件的区分

    1)合成事件与物理事件的隔离

    接着上文这里对真实物理事件和系统发出的合成事件进行了区分: 

    • 如果是真实物理事件,查找原始事件里面的设备ID,通过processEventsForDeviceLocked去找到输入子设备InputDevice,然后通过装饰者的方式在各个子设备中处理原始数据并进行封装。
    • 什么是物理事件呢?由硬件物理设备触发的真实事件,例如手指接触屏幕,屏幕设备触发的触摸事件,GPIO电源键被按下触发的真实事件。
    • 如果是合成事件,判断具体类型,进行设备注册和注销,和设备的扫描,这些事件都不是由物理设备触发的,而是由系统触发的,所以把她们叫做合成事件。
    //frameworks/native/services/inputflinger/reader/include/EventHub.h
    class EventHubInterface {
        enum {
            //定义合成事件:当检测到新输入设备连接时触发(如插入USB鼠标或蓝牙键盘),系统会通过该事件通知InputReader加载设备驱动并初始化配置。事件携带设备ID和时间戳,触发addDeviceLocked()调用完成设备注册‌
            DEVICE_ADDED = 0x10000000,
            //定义合成事件:输入设备断开连接时生成(如拔出触摸屏或手柄),触发removeDeviceLocked()清理设备资源。该事件会确保后续输入事件不会分发给已移除设备‌
            DEVICE_REMOVED = 0x20000000,
            //设备扫描周期完成标志事件,每次扫描(包括冷启动时的初始扫描)至少发送一次。用于同步设备状态变更,触发handleConfigurationChangedLocked()更新全局输入配置(如键盘布局切换
            FINISHED_DEVICE_SCAN = 0x30000000,
            //区分合成事件:通常认为小于FIRST_SYNTHETIC_EVENT的事件为真实的物理事件
            //真实物理事件:由硬件物理设备触发的真实事件,例如手指接触屏幕,屏幕设备触发的触摸事件,GPIO电源键被按下触发的真实事件
            FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
        };
    }
    //frameworks/native/services/inputflinger/reader/InputReader.cpp
    std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
        std::list<NotifyArgs> out;
        //遍历原始事件的数组
        for (const RawEvent* rawEvent = rawEvents; count;) {
            int32_t type = rawEvent->type;
            size_t batchSize = 1;
            //如果是真实物理事件:只要小于FIRST_SYNTHETIC_EVENT都被定义为真实事件
            if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
                //获取设备ID:原始事件触发的时候会把物理设备ID带进去,这里直接获取
                int32_t deviceId = rawEvent->deviceId;
                while (batchSize < count) {
                    if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||  rawEvent[batchSize].deviceId != deviceId) {
                        break;
                    }
                    batchSize += 1;
                }
                if (debugRawEvents()) {
                    ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
                }
                //通过设备ID找到输入设备,去进行事件处理
                out += 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;
        }
        return out;
    }

    2)processEventsForDeviceLocked

    前文已经提到了当有物理设备注册或者注销的时候,系统触发几个合成事件:DEVICE_ADDED和DEVICE_REMOVED 进行设备的添加和移除,其实这里的设备管理也使用了一个观察者德模式。如下逻辑

    那么设processEventsForDeviceLocked是如何去找寻原始events数据里面对于的物理设备呢?如/dev/input/events3节点触发的事件就应该找到触摸屏设备,如果是/dev/input/events0节点触发的事件就应该去找到物理按键设备。

    //frameworks/native/services/inputflinger/reader/InputReader.cpp
    std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,  size_t count) {
        //通过设备ID找到对应的设备,这里其实就是从原始的events数据里面找到对应的设备
        auto deviceIt = mDevices.find(eventHubId);
        if (deviceIt == mDevices.end()) {
            ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
            return {};
        }
        std::shared_ptr<InputDevice>& device = deviceIt->second;
        if (device->isIgnored()) {
            // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
            return {};
        }
        //调用设备抽象类的process方法,这个抽象父类其实就是InputDevice
        return device->process(rawEvents, count);
    }

    最后调用inputdevice的process函数来进行数据处理。关键其实就是在这个函数里面,是一种典型的装饰者设计模式。

    4、InputDevice如何装饰所有子设备?

    那么这些输入设备是如何管理的呢?那么我就需要研究一下InputDevice了。

    1)InputMapper装饰者

    她是如何通过InputMapper来进行装饰。分为如下四个流程:

    //frameworks/native/services/inputflinger/reader/InputReader.cpp
    std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
        //....省略....
                    switch (rawEvent->type) {
                    case EventHubInterface::DEVICE_ADDED:
                        //流程1:当收到输入设备注册德时候,调用此方法创建添加关联设备ID的InputDevice
                        addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                        break;
        //....省略....
    }
    void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
        //如果mDevices列表存在关联设备,直接返回
        if (mDevices.find(eventHubId) != mDevices.end()) {
            ALOGW("Ignoring spurious device added event for eventHubId %d.", eventHubId);
            return;
        }
        //流程2:通过createDeviceLocked创建InputDevice,并关联设备ID
        InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
        std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
        //...省略...
    }
    std::shared_ptr<InputDevice> InputReader::createDeviceLocked( int32_t eventHubId, const InputDeviceIdentifier& identifier) {
        //创建InputDevice
        std::shared_ptr<InputDevice> device;
        if (deviceIt != mDevices.end()) {
            device = deviceIt->second;
        } else {
            int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId :  nextInputDeviceIdLocked();
            device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(), identifier);
        }
        //流程3:创建InputDevice,并调用addEventHubDevice来进行关联设备ID,设备ID作为参数传递了进去
        device->addEventHubDevice(eventHubId, mConfig);
        return device;
    }
    //frameworks/native/services/inputflinger/reader/InputDevice.cpp
    void InputDevice::addEventHubDevice(int32_t eventHubId, const InputReaderConfiguration& readerConfig) {
        if (mDevices.find(eventHubId) != mDevices.end()) {
            return;
        }
        //封装设备ID为InputDeviceContext类型
        std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
        //流程4:通过封装设备ID的contextPtr来创建mappers,这个mapper就是一个装饰者
        std::vector<std::unique_ptr<InputMapper>> mappers = createMappers(*contextPtr, readerConfig);
        //InputDevice的主要工作其实就是交给mapper
        mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
        bumpGeneration();
    }
    

    最后在createMappers函数中根据不同的设备ID创建不同德inputMapper,如下代码逻辑:

    2)InputMapper所有派生物理设备

    如上所有的XXXInputMapper,这里分别介绍一下这些Mapper对应什么物理设备:

    • CursorInputMapper:处理鼠标设备输入,包括移动、点击和滚轮事件,通过PointerController控制光标位置
    • KeyboardInputMapper:映射物理键盘输入,处理按键扫描码到Android键值的转换,支持组合键检测
    • TouchInputMapper:触摸屏幕的基类,派生出如下三个触摸屏设备。2025年左右使用的屏幕基本上是MultiTouchInputMapper
    • SingleTouchInputMapper:单点触控设备(早期电阻屏)
    • MultiTouchInputMapper:多点触控设备(现代电容屏)
    • TouchpadInputMapper:笔记本触控板设备
    • JoystickInputMapper:处理游戏手柄/操纵杆的轴运动和按钮事件,支持力反馈特性
    • ExternalStylusInputMapper:管理外接手写笔输入,支持压感、倾斜等高级特性(如Wacom数位板)
    • RotaryEncoderInputMapper:对应旋转编码器设备(如智能手表表冠、旋钮控制器)
    • SensorInputMapper:处理加速度计、陀螺仪等传感器数据,但实际传感器事件通常通过独立子系统传递
    • VibratorInputMapper:控制设备的触觉反馈(震动马达),严格来说属于输出设备
    • SwitchInputMapper:映射物理开关事件(如盖子开关、滑动开关等)

    最后回到processEventsForDeviceLocked函数中的device->process(rawEvents, count)的逻辑中,最终其实就是调用了对应XXXInputMapper的process函数:

    //frameworks/native/services/inputflinger/reader/InputReader.cpp
    std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,  size_t count) {
        //通过设备ID找到对应的设备,这里其实就是从原始的events数据里面找到对应的设备
        auto deviceIt = mDevices.find(eventHubId);
        if (deviceIt == mDevices.end()) {
            ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
            return {};
        }
        std::shared_ptr<InputDevice>& device = deviceIt->second;
        if (device->isIgnored()) {
            // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
            return {};
        }
        //流程1:调用设备抽象类的process方法,这个抽象父类其实就是InputDevice
        return device->process(rawEvents, count);
    }
    ///frameworks/native/services/inputflinger/reader/InputDevice.cpp
    std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t count) {
        std::list<NotifyArgs> out;
        for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
            if (mDropUntilNextSync) {
                if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                    mDropUntilNextSync = false;
                    ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun.");
                } else {
                    ALOGD_IF(debugRawEvents(), "Dropped input event while waiting for next input sync.");
                }
            } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
                ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
                mDropUntilNextSync = true;
                out += reset(rawEvent->when);
            } else {
                //流程2:根据设备ID遍历mapper,调用mapper的process函数
                for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
                    out += mapper.process(rawEvent);
                });
            }
            --count;
        }
        return out;
    }

    三、InputDispatcher事件分发

    InputDispatcher作为native层的事件分发,他起到的作用,就是把InputReader封装好的事件分发到FW层的窗口或者控件之中。这个过程中主要涉及到

    • 如何接收来自InputReader过来的事件
    • 查询此事件关联的窗口或者控件
    • 最后把事件传递到ViewRootImpl中

    1、InputReader如何通过notify通知到InputDispatcher?

    这里重点针对InputReader如何把第二章提到的封装好之后的事件给到InputDispatcher,即具体再来解析一下下面这段代码中的notfiyAll函数。

    值得注意的是,这段代码逻辑修改的有些频繁,如上图截取的高通A14项目的代码,但是从AOSP A14的代码来看,这里其实有一些改动,包括AOSP A15/A13都不一致,虽然细节实现有些差异,但是大体逻辑一致。这里就以为高通A14的代码来解读一下。

    1)InputReader的监听器InputListener

    notifyAll代码如下,遍历所有NotifyArgs事件,然后通过监听器的notify函数是进行事件分发:

    进一步来看看mQueuedListener监听器队列到底是怎么赋值的呢?

    如上代码,这个监听器队列mQueuedListener在构造函数的第三个参数给予过来,所以我们不得不回到inputreader是如何创建的,第一章就解释了是inputmanager来创建他的:

    从这里的注释就可以看出来InputReader持有InputDispatcher的引用,这个关系如下:

    //frameworks/native/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
    //第一步:InputManager.cpp根据dispatcherPolicy实例化InputDispatcher对象
    std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
            InputDispatcherPolicyInterface& policy) {
        return std::make_unique<android::inputdispatcher::InputDispatcher>(policy);
    }
    //frameworks/native/services/inputflinger/InputProcessor.cpp
    //第二步:InputManager.cpp把InputDispatcher对象作为参数构造InputProcessor实例对象
    InputProcessor::InputProcessor(InputListenerInterface& listener) : mQueuedListener(listener) {}
    //frameworks/native/services/inputflinger/UnwantedInteractionBlocker.cpp
    //第三步:InputManager.cpp把InputProcessor对象作为参数构造UnwantedInteractionBlocker实例对象
    UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener)
          : UnwantedInteractionBlocker(listener, isPalmRejectionEnabled()){};
    //frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp
    //第四步:InputManager.cpp把UnwantedInteractionBlocker作为listener构造InputReader
    std::unique_ptr<InputReaderInterface> createInputReader(
            const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener) {
        return std::make_unique<InputReader>(std::make_unique<EventHub>(), policy, listener);
    }
    所以listener的处理逻辑InputListener.cpp,这个监听器最后还是封装InputDispatcher。

    2)notify分离不同类型的事件的处理逻辑

    总结一下,InputReader最后调用的notify其实就是向后面InputDispatcher中提到的队列mInboundQueue插入事件数据。

    2、InputDispatcher的轮询

    InputDispatcher的设计与InputReader基本一致,其代码逻辑也基本相同:

    1)InputDispatcher开启线程轮询

    InputDispatcher的构造函数和start与stop方法如下,其核心就是通过封装的InputThread类来创建线程,线程名为InputDispatcher,这里是通过mLooper的唤起来调用dispatchOnce函数

    2)dispatchOnce事件类型的处理

    dispatchOnceInnerLocked是处理实际事件分发过来的,源码如下:

    //frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp 
    void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
        // 如果mPendingEvent是一个null,需要去读取事件
        if (!mPendingEvent) {
            if (mInboundQueue.empty()) {
                //如果队列为空,
                mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
                if (!mPendingEvent)  return;
            }else{
                //否则直接从队列中读出一个事件,存放在mPendingEvent变量中
                mPendingEvent = mInboundQueue.front();
                mInboundQueue.pop_front();
            }
        }
        //定义事件废弃的原因
        DropReason dropReason = DropReason::NOT_DROPPED;
        if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
            dropReason = DropReason::POLICY;
        } else if (!mDispatchEnabled) {
            dropReason = DropReason::DISABLED;
        }
        //处理不同类型的事件
        switch (mPendingEvent->type) {
            //CONFIGURATION_CHANGED用于标识‌设备全局配置变更‌的事件类型:属于底层输入事件派发流程的关键环节。当设备配置(如屏幕方向、语言、键盘状态等)发生动态变化时,例如:横竖屏切换时,系统会生成该事件并携带新的 orientation 值;物理键盘连接/断开或软键盘弹出/隐藏时触发;用户切换系统语言或时区时,事件会包含更新的 locale 配置
            case EventEntry::Type::CONFIGURATION_CHANGED: {
                const ConfigurationChangedEntry& typedEntry =
                        static_cast<const ConfigurationChangedEntry&>(*mPendingEvent);
                done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
                dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
                break;
            }
            //DEVICE_RESET输入设备被重置‌:这类事件通常由底层硬件或驱动触发,用于通知系统输入设备的连接状态或配置发生了根本性变化‌。例如:当输入设备(如触摸屏、键盘)因异常断电、驱动崩溃或物理断开连接后重新初始化时,系统会生成该事件,标志设备需要重新配置‌;设备驱动检测到硬件异常(如固件崩溃、通信超时)时,主动触发重置事件,要求上层重新建立输入通道‌;在 Android 系统升级或输入服务(如 InputManagerService)重启时,可能强制重置所有输入设备以同步状态‌
            case EventEntry::Type::DEVICE_RESET: {
                const DeviceResetEntry& typedEntry = static_cast<const DeviceResetEntry&>(*mPendingEvent);
                done = dispatchDeviceResetLocked(currentTime, typedEntry);
                dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
                break;
            }
            //FOCUS表示焦点变化的事件类型:用于标记窗口或视图的焦点状态变更(如获焦/失焦),由InputDispatcher通过InputChannel传递给应用进程
            case EventEntry::Type::FOCUS: {
                std::shared_ptr<FocusEntry> typedEntry = std::static_pointer_cast<FocusEntry>(mPendingEvent);
                dispatchFocusLocked(currentTime, typedEntry);
                dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
                break;
            }
            //TOUCH_MODE_CHANGED表示设备触摸模式状态变化的事件类型:当设备从非触摸交互(如键盘/轨迹球)切换到触摸屏交互时触发
            case EventEntry::Type::TOUCH_MODE_CHANGED: {
                const auto typedEntry = std::static_pointer_cast<TouchModeEntry>(mPendingEvent);
                dispatchTouchModeChangeLocked(currentTime, typedEntry);
                dropReason = DropReason::NOT_DROPPED; // touch mode events are never dropped
                break;
            }
            //POINTER_CAPTURE_CHANGED处理指针设备(如鼠标/触控笔)捕获状态变化的事件类型:包括拖拽操作中需要持续跟踪指针位置,绘图应用保持触控笔输入连续性,游戏控制需要处理鼠标移出窗口的情况
            case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
                const auto typedEntry = std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent);
                dispatchPointerCaptureChangedLocked(currentTime, typedEntry, dropReason);
                done = true;
                break;
            }
            //DRAG处理拖拽操作的核心事件类型
            case EventEntry::Type::DRAG: {
                std::shared_ptr<DragEntry> typedEntry = std::static_pointer_cast<DragEntry>(mPendingEvent);
                dispatchDragLocked(currentTime, typedEntry);
                done = true;
                break;
            }
            //KEY表示物理/虚拟键盘按键事件的核心类型:专用于键盘类输入设备(包括物理键盘、虚拟键盘、游戏手柄等),通过EventHub从/dev/input节点读取原始扫描码后转换生成。含三种基础动作类型KEY_DOWN‌、KEY_UP‌、KEY_REPEAT‌长按按键时的重复输入
            case EventEntry::Type::KEY: {
                std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
                if (isAppSwitchDue) {
                    if (isAppSwitchKeyEvent(*keyEntry)) {
                        resetPendingAppSwitchLocked(true);
                        isAppSwitchDue = false;
                    } else if (dropReason == DropReason::NOT_DROPPED) {
                        dropReason = DropReason::APP_SWITCH;
                    }
                }
                if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
                    dropReason = DropReason::STALE;
                }
                if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
                    dropReason = DropReason::BLOCKED;
                }
                done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
                break;
            }
            //MOTION处理触摸/指针移动事件的核心类型:该事件对应触摸屏、鼠标、手写笔等指针设备的输入动作。包含以下基础动作常量:AMOTION_EVENT_ACTION_DOWN、AMOTION_EVENT_ACTION_MOVE、AMOTION_EVENT_ACTION_UP、AMOTION_EVENT_ACTION_HOVER_MOVE鼠标悬停移
            case EventEntry::Type::MOTION: {
                std::shared_ptr<MotionEntry> motionEntry = std::static_pointer_cast<MotionEntry>(mPendingEvent);
                if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
                    dropReason = DropReason::APP_SWITCH;
                }
                if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
                    dropReason = DropReason::STALE;
                }
                if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
                    dropReason = DropReason::BLOCKED;
                }
                done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
                break;
            }
            //SENSOR传递硬件传感器数据的核心事件类型:该事件对应加速度计、陀螺仪、光感等物理传感器的数据输入,通过Sensor HAL层从内核驱动获取原始数据,注意我们可能支持多种sensor
            case EventEntry::Type::SENSOR: {
                std::shared_ptr<SensorEntry> sensorEntry = std::static_pointer_cast<SensorEntry>(mPendingEvent);
                if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
                    dropReason = DropReason::APP_SWITCH;
                }
                nsecs_t bootTime = systemTime(SYSTEM_TIME_BOOTTIME);
                if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(bootTime, *sensorEntry)) {
                    dropReason = DropReason::STALE;
                }
                dispatchSensorLocked(currentTime, sensorEntry, &dropReason, nextWakeupTime);
                done = true;
                break;
            }
        }
        if (done) {
            if (dropReason != DropReason::NOT_DROPPED) {
                dropInboundEventLocked(*mPendingEvent, dropReason);
            }
            mLastDropReason = dropReason;
            releasePendingEventLocked();
            *nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately
        }
    }

    3、InputDispatcher事件的分发

    在介绍几个常用的事件类型分发之前,我们先看看事件到底是怎么发送的?

    几乎所有不同类型的dispatchXXXLocked函数最后都调用了dispatchEventLocked函数,源码如下:

    //frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp 
    //函数功能:输入事件分发的核心方法,负责将事件派发到目标窗口
    //函数参数currentTime:当前系统时间戳(纳秒级)
    //函数参数eventEntry:待分发的事件对象(如MotionEntry或KeyEntry)
    //函数参数inputTargets:目标窗口列表,包含InputChannel等连接信息
    void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> eventEntry, const std::vector<InputTarget>& inputTargets) {
        if (DEBUG_DISPATCH_CYCLE)  ALOGD("dispatchEventToCurrentInputTargets");
        //更新与窗口交互的令牌(Token),用于焦点管理和防篡改验证
        updateInteractionTokensLocked(*eventEntry, inputTargets);
        //触发系统用户活动状态更新(如防止屏幕休眠)
        pokeUserActivityLocked(*eventEntry);
        //遍历目标窗口‌:逐个处理inputTargets中的目标窗口
        for (const InputTarget& inputTarget : inputTargets) {
            //获取连接对象‌:通过InputChannel的Token获取对应Connection(跨进程通信管道)
            std::shared_ptr<Connection> connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
            if (connection != nullptr) {
                //有效连接‌:调用prepareDispatchCycleLocked准备事件分发周期
                prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
            } else {
                //无效连接‌:打印调试日志并丢弃事件(通常因窗口已销毁)
                if (DEBUG_FOCUS) ALOGD("Dropping event delivery to target with channel '%s' because it is no longer registered with the input dispatcher.", inputTarget.inputChannel->getName().c_str());
            }
        }
    }

    1)dispatchConfigurationChangedLocked

    此函数用于设备全局配置变更的时候触发的事件,例如屏幕旋转、键盘插入

    //函数功能:处理系统配置变更事件(如屏幕旋转、键盘连接状态变化等)
    //函数参数:currentTime/当前系统时间戳(纳秒级) entry/包含配置变更事件的具体信息(如触发时间戳)
    bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime, const ConfigurationChangedEntry& entry) {
        if (DEBUG_OUTBOUND_EVENT_DETAILS)  ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
        //当键盘设备状态变化(如外接键盘插拔)时,需中断当前可能的按键长按重复事件
        resetKeyRepeatLocked();
        //封装命令:mPolicy.notifyConfigurationChanged,通过InputDispatcherPolicyInterface通知上层(如WindowManagerService)设备全局配置出现了变更
        auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) {
            scoped_unlock unlock(mLock);
            mPolicy.notifyConfigurationChanged(eventTime);
        };
        //执行命令
        postCommandLocked(std::move(command));
        return true;
    }

    2)dispatchKeyLocked

    此函数用于处理键盘类输入设备(包括物理键盘、虚拟键盘、游戏手柄等),通过EventHub从/dev/input节点读取原始扫描码后转换生成。包含三种基础动作类型:

    • KEY_DOWN‌:按键按下时触发
    • KEY_UP‌:按键释放时触发
    • KEY_REPEAT‌:长按按键时的重复输入

    InputReader将原始输入数据封装为KeyEvent对象,通过InputDispatchermInboundQueue队列进入分发流程,最终经WindowInputEventReceiver传递到应用层。KeyEvent对象包含以下核心字段:

    • 按键码(keyCode,如KEYCODE_ENTER
    • 元状态(metaState,如CTRL组合键)
    • 设备ID(deviceId
    • 时间戳(eventTime

    3)dispatchMotionLocked

    此函数用于处理触摸/指针移动事件的核心类型,我们通常在手机设备上接触更多还是触摸事件。该事件对应触摸屏、鼠标、手写笔等指针设备的输入动作,通过Linux内核/dev/input节点上报原始坐标数据。包含以下基础动作常量(定义于frameworks/native/include/android/input.h):

    • AMOTION_EVENT_ACTION_DOWN:触控点按下
    • AMOTION_EVENT_ACTION_MOVE:触控点移动
    • AMOTION_EVENT_ACTION_UP:触控点抬起
    • AMOTION_EVENT_ACTION_HOVER_MOVE:鼠标悬停移动

    InputReader将原始数据转换为MotionEntry对象,关键字段包括:

    • 触控点坐标数组(pointerCoords
    • 触控点ID(pointerProperties.id
    • 事件时间戳(eventTime
    • 设备ID(deviceId

    通过InputDispatcher::dispatchMotionLocked()方法实现:

    • 调用findTouchedWindowTargetsLocked()确定目标窗口
    • WindowInputEventReceiver传递到应用进程

    如上代码核心还是找到窗口添加到inputTargets里面,然后遍历所有窗口在分发到ViewRootImpl之中

    //frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp 
    std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions, InputEventInjectionResult& outInjectionResult) {
        //定义目标窗口列表
        std::vector<InputTarget> targets;
        //记录当前事件相关信息
        // event injection will be allowed.
        const int32_t displayId = entry.displayId;
        const int32_t action = entry.action;
        const int32_t maskedAction = MotionEvent::getActionMasked(action);
        //记录上一次事件相关信息
        const TouchState* oldState = nullptr;
        TouchState tempTouchState;
        if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
            oldState = &(it->second);
            tempTouchState = *oldState;
        }
        //isSplit 表示当前触摸事件是否需要分裂处理,通过shouldSplitTouch()函数判断窗口是否支持分裂触摸(如分屏模式下的多窗口交互
        bool isSplit = shouldSplitTouch(tempTouchState, entry);
        //switchedDevice 标识输入设备是否切换,当满足以下任一条件时为true:旧状态存在(oldState != nullptr);设备ID或事件来源(source)发生变化
        const bool switchedDevice = (oldState != nullptr) && (oldState->deviceId != entry.deviceId || oldState->source != entry.source);
        //isHoverAction 判断是否为悬浮事件(非接触式触摸),包括:HOVER_MOVE(悬浮移动);HOVER_ENTER(悬浮进入);HOVER_EXIT(悬浮离开)
        const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||  maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
        //wasDown 表示旧状态是否存在且处于按下状态(isDown()),用于追踪前一个事件的按压状态
        const bool wasDown = oldState != nullptr && oldState->isDown();
        //isDown 标识当前是否为按下事件,包括:标准按下动作(ACTION_DOWN);多点触控按下(ACTION_POINTER_DOWN)且此前无按下状态(!wasDown)
        const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) || (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
        //newGesture 标记是否为新手势的开始,触发条件包括:按下事件(isDown);滚动动作(ACTION_SCROLL);悬浮进入/移动动作(HOVER_ENTER/MOVE)
        const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||  maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;
        //isFromMouse 通过isFromSource()检测事件来源是否为鼠标设备(AINPUT_SOURCE_MOUSE)
        const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
        // 之前处于已经按下但是设备已经切换当前不是按下状态就忽略事件
        if (switchedDevice && wasDown && !isDown) {
            LOG(INFO) << "Dropping event because a pointer for device " << oldState->deviceId  << " is already down in display " << displayId << ": " << entry.getDescription();
            outInjectionResult = InputEventInjectionResult::FAILED;
            return {}; // wrong device
        }
        //如果是一次新的手势操作,当手指再次按下屏幕就可以认为是一次新的手势
        if (newGesture) {
            tempTouchState.reset();
            tempTouchState.deviceId = entry.deviceId;
            tempTouchState.source = entry.source;
            isSplit = false;
        } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
            //异常情况下打印日志废弃事件
            ALOGI("Dropping move event because a pointer for a different device is already active " "in display %" PRId32,  displayId);
            outInjectionResult = InputEventInjectionResult::FAILED;
            return {}; // wrong device
        }
        //新手势开始的
        if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
            /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
            // Handle the case where we did not find a window.
            if (newTouchedWindowHandle == nullptr) {
                ALOGD("No new touched window at (%.1f, %.1f) in display %" PRId32, x, y, displayId);
                // Try to assign the pointer to the first foreground window we find, if there is one.
                newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
            }
            // Verify targeted injection.
            if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
                ALOGW("Dropping injected touch event: %s", (*err).c_str());
                outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
                newTouchedWindowHandle = nullptr;
                return {};
            }
            //.....寻找目标窗口....
        } else {
            /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
            // 非按下事件过滤进行事件废弃:当指针未按下(!isDown())且非悬浮退出动作(HOVER_EXIT)时触发
            if (!tempTouchState.isDown() && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
                LOG(INFO) << "Dropping event because the pointer is not down or we previously  dropped the pointer down event in display " << displayId << ": " << entry.getDescription();
                outInjectionResult = InputEventInjectionResult::FAILED;
                return {};
            }
            // 悬浮退出检查进行事件废弃:检查旧状态是否存在且当前指针在任意窗口悬浮,若验证失败则记录日志并返回错误,通过removeHoveringPointer移除该指针的悬浮状态
            if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
                const int32_t pointerId = entry.pointerProperties[0].id;
                if (oldState == nullptr ||
                    oldState->getWindowsWithHoveringPointer(entry.deviceId, pointerId).empty()) {
                    LOG(INFO) << "Dropping event because the hovering pointer is not in any windows in display " << displayId << ": " << entry.getDescription();
                    outInjectionResult = InputEventInjectionResult::FAILED;
                    return {};
                }
                tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
            }
            //....寻找目标窗口
        }
        //.....省略...
    }

    四、日志与案例

    最后总结一下IMS的流程图如下:

    1、日志之触摸屏点击事件

    重要关键日志如下:

    #搜索InputDispatcher,找寻action类型,事件来源source
    Line 13635: 07-03 14:50:12.667  2179  2439 D InputDispatcher: dispatchMotion - eventTime=1194046934000, deviceId=5, source=TOUCHSCREEN, displayId=0, policyFlags=0x62000000, action=DOWN, actionButton=0x0, flags=0x0, metaState=0x0, buttonState=0x0,edgeFlags=0x0, xPrecision=1.000000, yPrecision=1.000000, downTime=1194046934000
    #搜索InputDispatcher,找到X和Y坐标
    Line 13636: 07-03 14:50:12.668  2179  2439 D InputDispatcher:   Pointer 0: id=0, toolType=FINGER, x=665.000000, y=257.000000, pressure=1.000000, size=0.000000, touchMajor=0.000000, touchMinor=0.000000, toolMajor=0.000000, toolMinor=0.000000, orientation=0.000000
    #开启日志宏控才能输出,表示正在向目标窗口分发事件
    Line 13643: 07-03 14:50:12.668  2179  2439 D InputDispatcher: dispatchEventToCurrentInputTargets
    Line 13649: 07-03 14:50:12.668  2179  2439 D InputDispatcher: channel 'bda9ab5 com.android.gallery3d/com.android.gallery3d.app.GalleryActivity (server)' ~ startDispatchCycle
    Line 13657: 07-03 14:50:12.669  5427  5427 D InputEventReceiver: channel 'bda9ab5 com.android.gallery3d/com.android.gallery3d.app.GalleryActivity (client)' ~ Received motion event.
    #ViewRootImpl的enqueueInputEvent方法能打印所有事件日志,但是日志需要自己添加
    Line 13670: 07-03 14:50:12.675  5427  5427 W ViewRootImpl: SHEN---->enqueueInputEvent event = MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=257.0, y[0]=135.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=1194046, downTime=1194046, deviceId=5, source=0x1002, displayId=0, eventId=194245677 }
    	

    总结:点击一个按钮会依次触发如下流程,屏幕驱动驱动传感器检查到事件节点,InputReader先读取事件给到InputDispatcher,InputDispatcher查找目标窗口,查找失败废弃事件并打印日志,查找成功通过channel的跨进程方式去分发到fw侧,最后在app的ViewRootImpl进行响应和事件分发到各个子View。总共会收到两个事件:DOWN表示按下,UP表示抬起。

    2、日志之触摸移动事件

    同样在第一小节的基础上先收到DOWN表示按下,然后批量收到MOVE表示移动,最后收到UP表示抬起。这里截一下MOVE:

    3、日志之音量+键按压事件

    4、案例之点击切换相关控件高概率出现异常飞线

    问题描述:在横屏模式下,点击能够触发窗口或者控件切换的时候,高概率出现这种飞线。只有在开发者模式打开了”指针位置”开关,并且在横屏模式下才能概率性复现,经过多次复现和排查,只有在具有窗口或者View控件切换的过程中进行点击,才会概率性出现。目前找到的最高复现概率是进入aosp原生图库应用,左上角的

    分析过程:手动添加日志进行对比正常现象和异常现象

    对比日志差异点如下:

    分析结论:我们这个案例有如下关键日志,因此可以判断带有焦点的窗口查找失败,结果并不是InputEventInjectionResult::SUCCEEDED,进入事件注入失败后的补偿逻辑,新创建CANCEL事件并发送出去。

    针对如上结论,我们做了如下实验:

    • 在竖屏模式下,点击aosp图库的这个poupueView进行高概率触发,发现也会高概率出现事件焦点窗口没有找到,但是坐标是OK
    • 在多个A14项目横屏模式均发现此现象,均发现此现象和次日志。但是在A15平台上面没有发现,因此认为是A14平台BUG

    5、案例之两个手指先后触碰屏幕导致系统重启

    问题描述:一只手指按住屏幕,另一个手指快速点击屏幕,突然出现重启现象

    原因分析:Abort message: 'Split motion event has mismatching downTime and eventTime for ACTION_DOWN, motionEntry=MotionEvent

    如上日志当检测到ACTION_DOWN事件时,强制验证splitDownTime(事件分割时间)必须等于originalMotionEntry.eventTime(原始事件时间)。如果时间戳不匹配,会触发LOG_ALWAYS_FATAL_IF断言,输出错误日志并终止进程。该进程为核心进程被终止,就会触发系统重启。

     参考文档

    Android IMS原理解析 - 简书

    Android 输入事件分发全流程梳理(一)_android input事件分发流程-CSDN博客

    Android 输入事件分发全流程梳理(二)_android输入事件流程图-CSDN博客

    <think>嗯,用户想要深入了解Android 9源码中的Input流程,我需要详细梳理这部分内容。首先,我应该回忆Android输入系统的基本结构,可能涉及到的模块包括InputManagerService、WindowManagerService、InputReader和InputDispatcher等。 接下来,用户可能需要知道整个流程是如何从硬件事件传递到应用的。我得从底层开始,比如Linux内核的输入设备驱动,然后到HAL层,再到Native层的InputReader线程读取事件,再转交给InputDispatcher分发。 要注意Android 9和之前版本的区别,比如可能有新的API或者架构调整。需要确认Android 9中是否有重大变更,比如Project Treble相关的内容是否影响HAL层,但Input子系统可能变化不大。 用户可能对事件分发机制感兴趣,比如如何确定目标窗口,如何处理触摸事件分发顺序,以及ANR的触发机制。这部分需要详细说明InputDispatcher的作用,如何WindowManagerService交互,通过Socket通信传递事件。 另外,需要考虑代码的具体位置,比如frameworks/base/services/core/java/com/android/server/input/,以及Native部分的代码路径,比如frameworks/native/services/inputflinger/。可能需要提到关键的类和方法,比如InputManagerService的启动过程,EventHub如何监听设备节点。 还要注意事件分发流程,包括添加到队列、寻找目标窗口、处理焦点切换、权限检查等步骤。可能需要解释InputChannel和WindowInputEventReceiver的作用,以及应用侧如何通过ViewRootImpl接收事件。 最后,用户可能需要一些调试方法,比如查看日志或者使用adb命令获取输入事件信息。这部分可以简要提及,帮助用户实际验证流程。 总之,回答需要结构清晰,从底层到上层逐步展开,结合关键代码和流程,确保用户能理解整个Input处理机制。同时,要使用中文,避免专业术语过多,适当解释概念,保持逻辑连贯。</think>好的,我将为您梳理Android 9(Pie)源码中Input事件处理的核心流程。这个涉及Linux内核到应用层的完整处理链路,以下是关键步骤分析: --- ### 一、硬件事件产生 1. **内核层**:输入设备(触摸屏/按键)通过`evdev`驱动生成`input_event` 2. **HAL层**:`EventHub`(位于`frameworks/native/services/inputflinger/EventHub.cpp`)通过`epoll`监听`/dev/input`节点 --- ### 二、InputReader处理阶段 ```cpp // frameworks/native/services/inputflinger/InputReader.cpp void InputReader::loopOnce() { size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer...); processEventsLocked(mEventBuffer, count); // 原始事件解析 } ``` 1. 将原始事件转换为`RawEvent` 2. 根据设备类型分类处理: - 触摸事件:生成`TouchInputMapper` - 物理按键:生成`KeyboardInputMapper` - 传感器事件:生成`SensorInputMapper` --- ### 三、InputDispatcher分发阶段 ```java // frameworks/base/services/core/java/com/android/server/input/InputManagerService.java class NativeInputManager { void notifyKey(int32_t eventType, int32_t deviceId...) { mService.mHandler.obtainMessage(...).sendToTarget(); } } ``` 1. **事件入队**:通过`InputDispatcher::enqueueInboundEventLocked`写入队列 2. **目标窗口查找**: - 通过`WindowManagerService`获取当前焦点窗口 - 检查窗口的`InputChannel`有效性 3. **分发逻辑**: - 按键事件:直接发送到焦点窗口 - 触摸事件:通过`findTouchedWindowTargetsLocked`计算触点所在的窗口 --- ### 四、跨进程通信机制 ```cpp // frameworks/base/core/java/android/view/InputChannel.java public final class InputChannel implements Parcelable { // 使用socket pair进行IPC通信 private static native void nativeSendSignal(...); } ``` 1. **InputChannel**:每个窗口通过`ViewRootImpl.addToDisplay`创建 2. **WindowInputEventReceiver**:应用层通过`Choreographer`接收事件 --- ### 五、应用层处理 ```java // frameworks/base/core/java/android/view/ViewRootImpl.java final class ViewPostImeInputStage extends InputStage { protected int onProcess(...) { if (event instanceof KeyEvent) { deliverKeyEvent(postEvent); } else { processPointerEvent(event); } } } ``` 1. `ViewRootImpl`通过`InputEventReceiver.dispatchInputEvent`接收事件 2. 事件经过`InputStage`责任链处理(`ViewPostImeInputStage`->`NativePostImeInputStage`) --- ### 六、ANR触发机制 1. **超时检测**:`InputDispatcher`设置5秒超时(按键事件) 2. 当事件未在`InputConfig.TIMEOUT`内完成处理: ```cpp // frameworks/native/services/inputflinger/InputDispatcher.cpp void doDispatchCycleLockedInterruptible(...) { if (timeoutTime <= currentTime) { onANRLocked(currentTime, ...); } } ``` --- ### 关键调试方法 1. 查看事件流: ```bash adb shell getevent -lt ``` 2. 监控Window焦点: ```bash adb shell dumpsys input ``` 3. 查看ANR日志: ```bash adb logcat | grep "Input event injection timed out" ``` 建议结合源码查看以下关键类: - `InputManagerService`(Java层入口) - `InputDispatcher.cpp`(核心分发逻辑) - `InputTransport.cpp`(跨进程通信实现) - `InputEventReceiver.java`(应用层接收入口) 需要更深入的代码分析可指定具体模块(如多点触控处理、手势识别等)。
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    诸神黄昏EX

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

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

    余额充值