android /inputmanager-jni,Linux/Android——Input系统之frameworks层InputManagerService (六)【转】...

上一篇Linux/Android——input系统之 kernel层 与 frameworks层交互 (五)中有介绍kernel层一下以及与Android这边frameworks层之间的联系,算是打通android 应用层与 kernel驱动层,对整个input系统的学习是至关重要的,其中frameworks层只是简单记录了几个接入点,这里开始分析frameworks层的细节部分。

input服务的启动:

android启动的时候会启动很多个service,这个可以参考SystemServer.Java ,会启动InputManagerService这个服务:

Slog.i(TAG, "Input Manager");

inputManager = new InputManagerService(context, wmHandler);

...

ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

直接看InputManagerService.java中的start函数:

public void start() {

Slog.i(TAG, "Starting input manager");

nativeStart(mPtr);   //调用了本地方法,JNI对应的cpp 在server下的jni目录下

...

}

这个牵扯到android的server的jni,最开始是在SystemServer中加载android_server这个动态库,

至于这个动态库的编译可参考/frameworks/base/services/jni/Android.mk中的内容

所以在调用这个nativeStart方法时,相关的动态库已经加载到SystemServer的进程中。

先看下这个start函数在jni文件中的实现,frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp中:

static void nativeStart(JNIEnv* env, jclass clazz, jint ptr) {

NativeInputManager* im = reinterpret_cast(ptr);  //这个NativeInputManager 是在上面InputMangerService构造的时候调用nativeInit时new出来的

status_t result = im->getInputManager()->start(); //这里是调用了NativeInputManager中的InputManager中的start方法,同样这个InputManager是NativeInputManager构造的时候new出来的

if (result) {

jniThrowRuntimeException(env, "Input manager could not be started.");

}

}

其实熟悉JNI的话,我分析到这里,就应该差不多了。。对于JNI 不是很了解的话可以参考我之前的博客:Andorid——ubuntu下的 NDK / JNI

看下NativeInputManager构造函数中的:

sp eventHub = new EventHub();

mInputManager = new InputManager(eventHub, this, this);

这里的JNI部分就不多说了,现在就看这个InputManager的start方法,上面提到到Android.mk,可以看到include了一个libinput的动态库,

而这个动态库的路径是在/frameworks/base/services/input下,这就明了啦.此目录下有InputManager.cpp . EventHub.cpp等

直接看start:

status_t InputManager::start() {

status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);  //这个mDispatcherThread是在InputManager构造函数里调用initialize初始化,这里很明显启动了这个名为InputDispatcher的线程

if (result) {

ALOGE("Could not start InputDispatcher thread due to error %d.", result);

return result;

}

result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); //同上,开启线程

if (result) {

ALOGE("Could not start InputReader thread due to error %d.", result);

mDispatcherThread->requestExit();

return result;

}

return OK;

}

到这里算是看到真面目的感觉,看这两个线程,字面意思,一个是分发给input事件给当前的activity的,一个是读取从下层发上来的input事件的

InputDispatcher分发:

从上面的线程启动分析:

bool InputDispatcherThread::threadLoop() {

mDispatcher->dispatchOnce();

return true;

}

调用分发一次的函数:

void InputDispatcher::dispatchOnce() {

nsecs_t nextWakeupTime = LONG_LONG_MAX;

{ // acquire lock

AutoMutex _l(mLock);

mDispatcherIsAliveCondition.broadcast();

// Run a dispatch loop if there are no pending commands.

// The dispatch loop might enqueue commands to run afterwards.

if (!haveCommandsLocked()) {           //如果有缓存的命令就调用下面的runCommand去执行,没有的话这里去检查是否有新的input事件,这里定义一个唤醒时间控制

dispatchOnceInnerLocked(&nextWakeupTime);

}

// Run all pending commands if there are any.

// If any commands were run then force the next poll to wake up immediately.

if (runCommandsLockedInterruptible()) {

nextWakeupTime = LONG_LONG_MIN;

}

} // release lock

// Wait for callback or timeout or wake.  (make sure we round up, not down)

nsecs_t currentTime = now();

int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);

mLooper->pollOnce(timeoutMillis);  //执行上面一次分发之后,就进入了loop,这个loop会持续的检测对应的管道中是否有内容可读,而另外一个线程InputReader 读取到input事件之后就会往这个管道写入

}

这个是处理input事件的,后续分析,先看怎么读取事件.

InputReader读取:

源码位于frameworks/base/libs/ui/InputReader.cpp ,开启线程如下:

bool InputReaderThread::threadLoop() {

mReader->loopOnce();

return true;

}

这里看这个loopOnce:

void InputReader::loopOnce() {

...

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

...

if (count) {

processEventsLocked(mEventBuffer, count); //如果获取到事件,就处理

}

...

}

可以看到这里就到了获取event事件了。上一篇中有提到!

getEvent中会一直read,直到get到event之后,通过precessevent处理,最终会唤醒上面介绍到的InputDispatcherThread,通知它有新的事件来了

<think>好的,用户之前已经询问过Android输入流程的梳理文档,现在他进一步要求更深入的源码分析。我需要先回顾一下之前的对话,确保这次的回答能够衔接上,并且满足他更深次的需求。 首先,用户可能是一位Android开发者,或者是对系统感兴趣的学习者。他可能在开发过程中遇到了输入相关的问题,或者想深入理解机制以便优化应用。之前的回答已经覆盖了流程概览,现在他需要源码级别的分析,说明他希望了解具体实现细节,可能用于调试或自定义功能。 接下来,我需要确定用户的具体需求。是希望了解整个流程中各个模块的源码结构,还是某个特定部分的详细分析?比如InputReader、InputDispatcher,或者应用的事件分发。根据他之前的问题,可能需要覆盖整个流程,但重点放在系统服务和应用的交互。 我需要整理源码分析的步骤,从系统服务开始,包括InputManagerServiceInputReader和InputDispatcher。这部分需要解释关键类的作用和交互流程,比如InputReader如何从设备读取事件,InputDispatcher如何分发事件。同时,要提到线程模型,比如InputReader和InputDispatcher运行在独立的线程中,使用EventHub监听设备节点。 然后,应用方面,需要涉及InputChannel的注册过程,ViewRootImpl如何与WMS交互,以及事件如何传递到View树。这部分可能需要详细说明Binder通信和SocketPair的使用,以及事件在应用中的处理流程,比如通过InputEventReceiver接收事件,再分发到DecorView和Activity。 另外,用户可能对某些关键方法感兴趣,比如dispatchKeyEvent或onTouchEvent,需要指出这些方法在源码中的位置和调用链。同时,可以提到一些常见的Hook点,比如修改InputFilter来监控或修改事件,或者通过重写View的方法来改变事件处理逻辑。 还要考虑用户可能的进阶需求,比如如何调试输入事件,或者如何处理事件冲突。这时候需要推荐一些工具,如getevent、sendevent,或者adb命令来查看系统状态。此外,可以提到查看Android官方文档和源码中的测试用例,帮助用户进一步学习。 需要注意的是,源码分析可能会涉及复杂的代码结构,用户可能需要具体的代码路径和关键类的位置。因此,在回答中要提供清晰的代码路径,比如frameworks/base/services/core/java/com/android/server/input/,以及关键方法的签名和调用关系。 同时,用户可能对事件分发的细节,如ANR机制、焦点管理感兴趣,因此需要解释InputDispatcher如何检测应用响应超时,以及WMS如何维护焦点窗口。这部分内容需要结合源码中的超时处理和焦点更新逻辑。 最后,确保回答结构清晰,分模块讲解,并给出示例和调试方法,帮助用户将理论应用到实际开发中。可能还需要提醒用户注意线程和同步机制,避免在分析源码时产生混淆。</think>以下是对 Android 输入系统核心流程的 **源码级分析**,结合关键代码路径(基于 Android 13 源码)和核心逻辑: --- ### **1. 系统服务源码分析** #### **1.1 InputManagerService 初始化** - **代码路径**: `frameworks/base/services/core/java/com/android/server/input/InputManagerService.java` `frameworks/native/services/inputflinger/InputManager.cpp` - **关键流程**: 1. **SystemServer 启动 InputManagerService**: ```java // SystemServer.java inputManager = new InputManagerService(context); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); ``` 2. **Native 初始化**: 通过 JNI 调用 `NativeInputManager`,创建 `InputReader` 和 `InputDispatcher` 线程。 #### **1.2 InputReader 读取事件** - **代码路径**: `frameworks/native/services/inputflinger/InputReader.cpp` - **核心逻辑**: - **EventHub 监听设备节点**: ```cpp void EventHub::wake() { // 通过 epoll 监听 /dev/input 下的设备节点 struct epoll_event event; event.events = EPOLLIN; event.data.fd = mINotifyFd; epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &event); } ``` - **循环读取原始事件**: ```cpp void InputReader::loopOnce() { // 从 EventHub 获取原始事件(RawEvent) size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); processEventsLocked(mEventBuffer, count); // 处理事件 } ``` - **事件换**: 将 `RawEvent` 换为 `KeyEvent` 或 `MotionEvent`(如 `processKey` 和 `processMotion` 方法)。 #### **1.3 InputDispatcher 分发事件** - **代码路径**: `frameworks/native/services/inputflinger/InputDispatcher.cpp` - **关键逻辑**: - **确定目标窗口**: ```cpp // 通过 WMS 查询窗口级和焦点 sp<InputWindowHandle> windowHandle = findTouchedWindowAtLocked(displayId, x, y); ``` - **分发事件到 Socket**: ```cpp // 通过 InputChannel 发送事件到应用进程 status_t status = connection->inputPublisher.publishKeyEvent(...); ``` - **ANR 检测**: ```cpp // 超时未收到应用确认(5秒) nsecs_t timeout = connection->inputChannel->getTimeout(); if (currentTime > timeout) { onAnrLocked(connection); } ``` --- ### **2. 应用源码分析** #### **2.1 InputChannel 注册** - **代码路径**: `frameworks/base/core/java/android/view/ViewRootImpl.java` - **流程**: - **窗口创建时注册 InputChannel**: ```java // ViewRootImpl.java mInputChannel = new InputChannel(); mWindowSession.addToDisplay(mWindow, ..., mInputChannel); ``` - **关联 InputEventReceiver**: ```java mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); ``` #### **2.2 事件传递到 View 树** - **代码路径**: `frameworks/base/core/java/android/view/ViewRootImpl.java` `frameworks/base/core/java/android/view/View.java` - **关键方法**: 1. **InputEventReceiver.onInputEvent()**: ```java // ViewRootImpl.WindowInputEventReceiver public void onInputEvent(InputEvent event) { enqueueInputEvent(event, this, 0, true); } ``` 2. **ViewRootImpl.deliverInputEvent()**: 调用 `mView.dispatchPointerEvent(event)`(`mView` 是 `DecorView`)。 3. **Activity.dispatchTouchEvent()**: ```java // Activity.java public boolean dispatchTouchEvent(MotionEvent ev) { return getWindow().superDispatchTouchEvent(ev); } ``` 4. **ViewGroup.dispatchTouchEvent()**: 通过 `onInterceptTouchEvent` 决定是否拦截,最终调用子 `View` 的 `onTouchEvent`。 --- ### **3. 核心机制源码解析** #### **3.1 焦点窗口更新** - **代码路径**: `frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java` - **逻辑**: ```java // WMS 更新焦点窗口 void setFocusedWindowLocked(WindowState newFocus) { mCurrentFocus = newFocus; InputManagerService.updateFocusedWindow(newFocus); // 通知 InputDispatcher } ``` #### **3.2 事件拦截(ViewGroup)** - **代码路径**: `frameworks/base/core/java/android/view/ViewGroup.java` - **关键代码**: ```java public boolean dispatchTouchEvent(MotionEvent ev) { if (onInterceptTouchEvent(ev)) { // 检查是否拦截 // 拦截后为自身处理 return super.dispatchTouchEvent(ev); } // 继续分发给子 View return child.dispatchTouchEvent(ev); } ``` --- ### **4. 调试与 Hook 技巧** #### **4.1 事件监控** - **InputFilter 拦截事件**: 实现 `InputFilter` 并注册到 `InputManagerService`,可修改或拦截全局输入事件。 - **代码路径**: `frameworks/base/core/java/android/view/IInputFilter.aidl` #### **4.2 关键日志** - **InputDispatcher 日志**: ```bash adb logcat -b events | grep "InputDispatcher" ``` - **View 事件日志**: 在 `View.dispatchTouchEvent` 中插入日志,观察事件传递路径。 --- ### **5. 流程图(源码视角)** ```plaintext 1. InputReader 线程: EventHub.read() → RawEvent → processEventsLocked() → KeyEvent/MotionEvent 2. InputDispatcher 线程: findTouchedWindowLocked() → publishKeyEvent() → Socket 发送 → App InputChannel 3. 应用进程: InputEventReceiver.onInputEvent() → ViewRootImpl → DecorView → Activity → View.dispatchTouchEvent() ``` --- ### **6. 深入建议** 1. **源码调试**: 使用 Android Studio 导入 `frameworks/base` 代码,断点调试 `InputDispatcher.cpp` 或 `ViewGroup.java`。 2. **官方文档**: 参考 [Android Input System Overview](https://source.android.com/docs/core/interaction/input)。 3. **实战案例**: 修改 `InputReader` 或 `InputDispatcher` 逻辑(如强制拦截返回键),需编译自定义 ROM。 如果需要分析特定模块(如多点触控或手势识别),可以进一步细化问题!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值