目录
1、system_server进程如何引用libinputflinger.so?
2)processEventsForDeviceLocked
1、InputReader如何通过notify通知到InputDispatcher?
1)InputReader的监听器InputListener
1)dispatchConfigurationChangedLocked
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
- 设备节点的读取:EventHub.cpp做的事情就是从这个节点里面读取数据,并进行封装整理成为events向量:如下代码,这里我直接引用https://xiaoxu.blog.csdn.net/article/details/146344278
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
对象,通过InputDispatcher
的mInboundQueue
队列进入分发流程,最终经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断言,输出错误日志并终止进程。该进程为核心进程被终止,就会触发系统重启。