Android InputFlinger简单分析(主要分析Touch)

本文主要分析Android系统中InputFlinger服务在处理Touch事件的过程。从InputManagerService的启动,到InputDispatcher和InputReader的协同工作,特别是对MotionEvent的处理。通过开机加载Touch设备的日志和单点touch事件的日志,揭示了InputReader如何读取并通知InputDispatcher,以及事件在不同Mapper之间的传递,最后到达Java层的View系统。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android InputFlinger简单分析(主要分析Touch)

首先,它有个服务,InputManagerService.

InputManagerService启动

startOtherServices@SystemServer

...
inputManager = new InputManagerService(context);
wm = WindowManagerService.main(context, inputManager,
     mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
...
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
...
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
            inputManager.start();
...

看看构造函数:

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

这个mPrt之后作为了其他native函数的第一个参数。先看看nativeInit

(位置base/services/core/jni/com_android_server_input_InputManagerService.cpp)

static jlong nativeInit(JNIEnv* env, jclass clazz,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
...
    NativeInputManager* im = new NativeInputManager(contextObj,         serviceObj,messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

简单来理解就是获取java层的消息队列,然后构造了一个NativeInputManager对象,然后返回java层,赋值给mPtr。

前文,startOtherServices@SystemServer中还调用了

inputManager.start();

start()@InputManagerService

...
nativeStart(mPtr);
...

nativeStart:

static void nativeStart(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();
...
}

即用nativeInit中new的NativeInputManager对象调用start函数。

那NativeInputManager是什么呢?

inline sp<InputManager> getInputManager() const { return mInputManager; }

直接去看InputManager调用start函数就好了。

接下来,我们的工作目录就集中到

frameworks/native/services/inputflinger

从InputManager::start开始

...
  status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
...
  result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
...

mDispatcherThread和mReaderThread的run函数,都会去调用threadLoop函数(就当是常识了)。

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

从名字上来看,一个专门去读取input,一个专门去分发input.可是为什么只运行一次(loopOnce)呢?

我们这次主要关注的是touch事件,即MotionEvent.

InputDispatcher.h

struct MotionEntry : EventEntry {

Motion是Event的一种!

virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
virtual void notifyKey(const NotifyKeyArgs* args);
virtual void notifyMotion(const NotifyMotionArgs* args);
virtual void notifySwitch(const NotifySwitchArgs* args);
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);

这几个函数功能应该类似,只不过Event种类不同。

在InputDispatcher.cpp中竟然找不到调用。嗯!yes!搜索结果如下:

InputReader.cpp:2551:        getListener()->notifyMotion(&args);
...

getListener@InputReader:

class InputReader : public InputReaderInterface {
...
virtual InputListenerInterface* getListener();

InputDispatcher.h:

class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface
...
class InputDispatcher : public InputDispatcherInterface

回头过去看一下InputManager的构造函数,就都明白了

InputManager::InputManager

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    //注意第三个参数
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

InputReader的构造函数:

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        //这个
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);

InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}

总的来说就是,notifyMotion函数的调用来自InputReader.读到input之后notify!

InputReader分析

InputReader::loopOnce

//引出一个EventHub,getEvents也非常类似平时在adb shell中使用的命令
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
...
processEventsLocked(mEventBuffer, count);
...

InputReader::processEventsLocked

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
      ...
        //type不是
        //DEVICE_ADDED,DEVICE_REMOVED,FINISHED_DEVICE_SCAN这三个
        if(type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
      ...
        processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        }
    }
}

我们来看看一个开机加载Touch device的log

V/Ev
<think>我们正在处理一个Android系统开发问题:用户希望保持触摸屏方向与显示屏方向一致。根据引用[1]中的信息,我们可以了解到系统属性中关于屏幕方向的设置:ro.same.orientation=false//主屏与副屏方向是否相同ro.rotation.external=false//副屏是否随主屏旋转ro.sf.hwrotation=0//主屏初始方向ro.orientation.einit=90//副屏初始方向但是,用户的需求是触摸屏方向与显示屏方向一致。在Android系统中,触摸屏的方向通常由输入子系统处理,而显示屏方向由显示子系统处理。我们可能需要考虑以下方面:1.显示屏的方向设置:通常由WindowManagerService管理,通过设置屏幕旋转角度来实现。2.触摸屏的方向:触摸事件的方向需要与显示屏方向一致,这样用户触摸的坐标才能正确映射。在Android中,触摸事件的方向调整通常由InputReader处理,它会根据当前屏幕的旋转状态对触摸事件进行旋转。然而,用户可能遇到了在某些定制设备上(比如有多个屏幕)触摸屏方向与显示屏方向不一致的问题。根据引用[2]和[3],我们了解到Surface和WindowStateAnimator相关的内容,但似乎与触摸事件关系不大。引用[4]是关于PowerManagerService,与电源管理相关,可能不直接相关。因此,我们可以考虑以下解决方案:方案一:通过系统属性或系统设置强制触摸屏方向与显示屏方向一致-在Android系统中,触摸屏的方向调整通常由输入设备配置(InputDeviceConfiguration)文件完成。这些文件位于/system/usr/idc/目录下,以触摸屏设备名命名(如Generic.kl等)。-我们可以通过配置触摸屏的坐标变换矩阵(touch.display.orientation)来使其与显示方向同步。方案二:在框架层修改,确保当显示屏方向改变时,触摸屏的方向也相应调整-在WindowManagerService中,当屏幕方向改变时,会通知输入系统重新配置输入设备。-我们可以查看InputManagerService中关于屏幕旋转的处理。具体步骤:1.确定当前设备的触摸屏设备名称。可以通过`getevent -p`命令查看。2.在/system/usr/idc/目录下找到对应的触摸屏配置文件(如Vendor_XXXX_Product_XXXX.idc),如果没有则创建一个。3.在该配置文件中设置触摸屏的方向变换矩阵。例如,如果显示屏方向为0度(自然方向),则矩阵为单位矩阵;如果显示屏旋转90度,则矩阵需要相应调整。但是,这种方法需要针对不同的旋转角度设置不同的矩阵,而且需要动态调整。实际上,Android系统本身已经支持根据屏幕旋转自动调整触摸事件的方向。所以,如果用户遇到不一致的情况,可能是由于以下原因:-触摸屏驱动报告的方向不正确。-输入配置文件中没有正确配置。因此,我们可以尝试以下步骤:步骤1:确保显示屏方向设置正确-在设置中允许自动旋转,或者固定为某个方向,看是否能够同步。步骤2:检查触摸屏的输入配置文件-确保触摸屏的输入配置文件中有正确的配置,特别是关于旋转的配置。例如,可以尝试添加:touch.orientationAware =1这表示触摸屏是方向感知的,系统会根据屏幕旋转自动调整触摸事件。步骤3:如果问题仍然存在,可能需要修改框架层代码-在InputReader中,当屏幕方向改变时,会重新计算触摸事件的变换矩阵。我们可以跟踪这一过程,确保触摸屏设备被正确通知到屏幕方向的变化。代码层面,我们可以参考InputReader.cpp中的处理:-在`InputReader::configureDisplay`函数中,会设置DisplayViewport,然后通知每个输入设备(包括触摸屏)重新配置。-在触摸屏设备(TouchInputMapper)中,会使用当前的DisplayViewport来更新变换矩阵。因此,如果触摸屏没有正确更新,可能是由于DisplayViewport没有正确设置,或者触摸屏设备没有收到配置改变的通知。但是,由于用户没有提供具体的问题现象(比如是哪个方向不一致,是否在旋转后不一致等),我们只能给出一般性建议。结论:-通常,Android系统会自动处理触摸屏方向与显示屏方向一致的问题。如果出现不一致,首先检查输入配置文件和驱动。-如果问题发生在定制设备上(比如有多个显示屏),则需要确保每个显示屏的Viewport信息正确,并且触摸屏被关联到正确的显示屏上。根据引用[1]中关于双屏属性的设置,如果用户设备有多个显示屏,那么需要确保触摸屏被关联到主显示屏(或者用户正在交互的显示屏),并且该显示屏的方向变化能够正确传递到输入子系统。具体到代码,我们可以查看InputManagerService中关于设置显示视图(setDisplayViewports)的调用,确保当屏幕方向改变时,新的Viewport被设置到InputReader。在PowerManagerService(引用[4])中,当屏幕唤醒时可能会触发屏幕旋转,但这不是直接原因。因此,建议用户:1.检查输入配置文件,确保触摸屏设备配置了`touch.orientationAware =1`。2.如果问题在定制设备上,检查是否在屏幕旋转时正确调用了InputManagerService的setDisplayViewports。如果用户需要修改代码,可以查看以下路径:-frameworks/base/services/core/java/com/android/server/input/InputManagerService.java-frameworks/native/services/inputflinger/InputReader.cpp在InputManagerService中,设置显示视图的方法:```javapublicvoid setDisplayViewports(List<DisplayViewport> viewports){...nativeSetDisplayViewports(mPtr,viewports);}```在WindowManagerService中,当显示信息发生变化时(比如屏幕旋转),会调用InputManagerService的setDisplayViewports。因此,如果用户发现屏幕旋转后触摸方向不变,可能是由于没有正确更新DisplayViewports。但是,标准Android系统已经处理了这一点。所以,问题可能出现在设备制造商的定制层。由于问题描述较为简略,我们只能提供这些方向性的指导。相关问题:1.如何配置Android触摸屏的输入配置文件?2.Android系统中屏幕旋转时,触摸事件是如何变换的?3.如何调试Android输入子系统中的触摸事件?4.在双屏设备上如何确保触摸屏与主显示屏方向一致?</think>### 保持Android触摸屏方向与显示屏方向一致 在Android系统中,触摸屏方向与显示屏方向的同步主要通过以下机制实现: #### 核心实现原理 1. **方向感知配置** 在输入设备配置文件中添加方向感知标记: `/system/usr/idc/<touchscreen>.idc` ```bash touch.orientationAware = 1 # 关键配置 ``` 此配置使触摸屏自动跟随屏幕旋转方向[^1][^3]。 2. **显示系统同步** 当屏幕旋转时,`WindowManagerService` 会触发: ```java // frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java void updateRotationUnchecked() { mDisplayRotation.updateRotationAndSendNewConfig(); mInputMonitor.setDisplayOrientation(...); // 通知输入系统 } ``` 输入系统收到方向变更后,自动调整触摸坐标映射[^2][^3]。 3. **Surface 方向同步** 通过 `Surface` 对象实现显示缓冲区的方向同步: ```java // frameworks/native/libs/gui/Surface.cpp void Surface::setTransform(uint32_t transform) { mTransform = transform; // 存储旋转状态 mGraphicBufferProducer->setTransform(transform); } ``` 触摸事件会根据此变换矩阵自动调整[^3]。 #### 故障排查步骤 若方向不一致: 1. **检查输入配置** ```bash adb shell getevent -p | grep 'orientation aware' ``` 确认输出包含 `0001 00000001` 2. **验证设备属性** 检查系统属性: ```bash adb shell getprop | grep orientation # 应包含 ro.sf.hwrotation=0 等有效配置[^1] ``` 3. **调试窗口方向** 在 `WindowStateAnimator` 中添加调试日志: ```java // frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java void setSurfaceTransform(SurfaceControl.Transaction t) { if (DEBUG_ORIENTATION) Slog.d(TAG, "Rotating surface: " + mTransform); t.setTransform(mSurfaceControl, mTransform); } ``` #### 特殊设备处理 **双屏设备**需额外配置: ```properties # 在设备makefile中 ro.same.orientation=true # 强制主副屏同向 ro.rotation.external=true # 副屏随主屏旋转 ``` 需同步修改 `InputReader` 中的多屏映射逻辑[^1][^4]。 > **关键点**:Android默认支持方向同步,异常通常由错误的设备配置或定制ROM缺失方向感知实现导致。优先检查 `touch.orientationAware` 配置值[^3][^4]。 --- ### 相关问题 1. **如何检测Android设备的当前屏幕旋转方向?** 2. **触摸屏方向翻转(180°)时如何调试?** 3. **Android多显示屏系统中如何指定触摸屏映射关系?** 4. **系统属性 `ro.sf.hwrotation` 的不同取值会产生什么影响?** 5. **在自定义ROM中如何实现强制横屏模式下的触摸方向同步?** [^1]: 双屏属性配置参考 [^2]: WindowManagerService方向处理逻辑 [^3]: Android Surface方向同步机制 [^4]: 系统级方向配置实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值