InputManagerService按键接收过程

本文详细解析了Android系统中InputManagerService的创建与启动流程,包括与WindowManagerService的交互、核心组件InputManager的运作机制,以及输入事件从捕获到分发的全过程。

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

InputManagerService的创建过程

由SystemServer启动并将其注册到系统服务中。

frameworks/base/services/java/com/android/server/SystemServer.java
    /**
     * Starts a miscellaneous grab bag of stuff that has yet to be refactored
     * and organized.
     */
    private void startOtherServices() {
        WindowManagerService wm = null;
        .......
        InputManagerService inputManager = null;
        .......
        inputManager = new InputManagerService(context);//1
        wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode !=                             FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore); //2
        ServiceManager.addService(Context.WINDOW_SERVICE, wm);
        ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
        inputManager.setWindowManagerCallbacks(wm.getInputMonitor());//4
        inputManager.start();//5
  1. 创建了一个InputManagerService(为了方便,以后简称IMS)的实例,也是整个系统中的唯一实例;
  2. 将唯一的IMS传给WindowManagerService(简称WMS),这样WMS可以调用IMS所有的公有方法;
  3. 将IMS注册到系统服务中,这样可以通过Binder机制进行调用IMS;
  4. 将WMS的InputMonitor实例给IMS引用,InputMonitor负责输入事件的监控和焦点的管理等;
  5. 启动输入监控。

先简单总结下WMS和IMS的关系,以后一点一点完善:

这里写图片描述

实例化的过程

IMS的构造函数代码如下:

    public InputManagerService(Context context) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());//1

.......
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//2
......
    }
  1. 创建一个Handler,可以注意到,其handler使用的Looper为DisplayThread的Looper,而此Looper同样被DisplayManagerService使用,另外WindowManagerService的实例化也是通过这个Looper运行的,其保证了他们之间不会并发。
  2. 调用nativeInit完成native的初始化。

IMS对应的native层的代码位置为:frameworks/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); //1
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper()); //2
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);//3
}

sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) {
    jlong ptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
    return reinterpret_cast<NativeMessageQueue*>(ptr);
}
  1. 将IMS的MessageQueue转化成native成的MessageQueue,并且java成的对象被保存到gMessageQueueClassInfo.mPtr引用中;
  2. 实例化一个NativeInputManager,以后IMS将会和这个实例打交道;
  3. 将第2步实例的指针传给IMS的mPtr全局变量保存。

再来看下NativeInputManager的构造函数:

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj);
    mServiceObj = env->NewGlobalRef(serviceObj);//1
.......
    sp<EventHub> eventHub = new EventHub();//2
    mInputManager = new InputManager(eventHub, this, this);//3
}
  1. 将IMS的Context及IMS的实例以及IMS的Looper都保存了起来,这时,NativeInputManager和IMS的关系已变成生死与共了,IMS拥有NativeInputManager的指针地址,NativeInputManager又拥有IMS的唯一实例,同时也拥有其Looper;
  2. 实例化EventHub,EventHub监控,包装,分发具体的输入设备(主要是/dev/input下的节点),其代码位于frameworks/native/services/inputflinger/EventHub.cpp,先不看它。
  3. 实例化InputManager,又是一个重要的实例,下面看下它的构造函数。
frameworks/native/services/inputflinger/InputManager.cpp
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();
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputManager的构造函数创建了4个实例,mDispatcher及其mDispatcherThread,mReader和mReaderThread。另外InputManager构造函数中后两位形参InputReaderPolicyInterface和InputDispatcherPolicyInterface均指向的是同一个也是唯一的NativeInputManager实例。

总结下这些对象的关系:

这里写图片描述

运行IMS

在SystemServer中,inputManager.start():

    public void start() {
        nativeStart(mPtr);
        .......
    }

mPtr就是NativeInputManager实例。

static void nativeStart(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

im->getInputManager()获得InputManager的实例:

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);//1
    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;
}

将InputDispatcherThread和InputReaderThread这两个线程启动了起来。

  1. 启动InputDispatcherThread线程
bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

mDispatcher就是InputDispatcher。

  1. 启动InputReaderThread线程。
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

mReader为InputReader。

从名字上看,InputReader应该是负责读取输入事件,InputDispatcher应该是负责分发输入的事件,下面着重看下这两个类。另外InputDispatcherThread和InputReaderThread都是C++11的Thread子类,当Thread被start后,其threadLoop将会被调用,而且如果threadLoop返回的是true,threadLoop将会被一致循环调用,直到其返回false。目前它们一致返回的是ture,所以InputDispatcher的dispatchOnce和InputReader的loopOnce将会被一次一次的调用。

下面我们先看下输入事件的处理。

InputReader::loopOnce

void InputReader::loopOnce() {
    ......
    // 1 从EventHub中获得输入事件的信息,将读取到的数据保存在mEventBuffer,返回值是读到的个数,该函数是阻塞的,如果没有事件,一致到超时才返回。
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    {
        .......
        if (count) {
            // 2 对读到到的数据进行处理
            processEventsLocked(mEventBuffer, count);
        }
        ...
    }

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        // 如果设备发生改变,通知。mPolicy实际上就是NativeInputManager。
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    ......
    // 分发数据。
    mQueuedListener->flush();
}
  1. EventHub通过linux的iNotify和EPoll机制查询输入设备的节点的事件,详细说这个东西需要较大的篇幅,先不看它;
  2. 将会对读取到的数据进行加工;
  3. 会将事件交给InputDispatcher进行分发。

看下数据的加工过程

InputReader::processEventsLocked

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {  //处理一般的输入事件
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %d Count: %d", batchSize, count);
#endif
            // 将来自于同一个输入设备的事件一块处理。
            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;
    }
}

看下增加设备的代码:

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    //如果该设备已经添加,忽略。
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {
        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
        return;
    }

    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);

    // 创建一个InputDevice的实例。
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);
    ......
    //将InputDevice增加到容器中。
    mDevices.add(deviceId, device);
}

再看下对输入事件的处理:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ......
    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }

    device->process(rawEvents, count);
}

调用InputDevice的process进行处理:

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
        ......
        if (mDropUntilNextSync) {
            ......
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ......
        } else {
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
        }
    }
}

mapper->process(rawEvent),这个maper是在运行增加设备的代码时,调用createDeviceLocked函数时为每种设备设置的mapper。

看下常见的KeyboardInputMapper:

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;

        if (isKeyboardOrGamepadKey(scanCode)) {
            int32_t keyCode;
            uint32_t flags;
            if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
                keyCode = AKEYCODE_UNKNOWN;
                flags = 0;
            }
            // 处理按键值
            processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
        }
        break;
    }
    ......
    }
}

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
        int32_t scanCode, uint32_t policyFlags) {
    ......
    // 通知按键值
    getListener()->notifyKey(&args);
}

getListener()的值就是InputReader中的mQueuedListener,调用它的notifyKey实际上只是把键值保存到mQueuedListener的容器中。

mQueuedListener的类型是QueuedInputListener,它是在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);

    { // acquire lock
        ......
    } // release lock
}

listener实际上是InputManager中InputDispatcher类型的mDispatcher。

QueuedInputListener的构造函数是:

QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
        mInnerListener(innerListener) {
}

最后来到了InputReader::loopOnce节中的最后一步:

mQueuedListener->flush();
void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

在通知按键值的时候:

getListener()->notifyKey(&args);
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

所以现在NotifyKeyArgs的notify函数将被调用:

void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}

到这时,InputDispatcher的notifyKey将被调用:

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ......
    // 验证是否是键按下或抬起事件
    if (!validateKeyEvent(args->action)) {
        return;
    }
    ......
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    ......
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

mPolicy就是NativeInputManager实例,而NativeInputManager的interceptKeyBeforeQueueing又会调用InputManagerService.java的interceptKeyBeforeQueueing,这样就又回到了Java层。

### Android 中通过应用检测或处理虚拟按键通知的方法 在 Android 开发中,可以利用特定 API 和机制来检测和处理虚拟按键的通知行为。以下是实现这一目标的具体方式: #### 1. **监听虚拟按键的状态变化** 为了捕获虚拟按键的行为,开发者可以在 `View` 或者 `Activity` 的生命周期方法中拦截这些事件。例如,当用户按下返回键时,可以通过重写 `onBackPressed()` 方法来进行干预。 ```java @Override public void onBackPressed() { // 自定义逻辑,比如提示确认对话框或者忽略操作 if (shouldInterceptBackPress()) { showConfirmationDialog(); } else { super.onBackPressed(); // 执行默认的返回操作 } } ``` 这种方法适用于防止误触返回键的情况[^4]。 --- #### 2. **全局监控输入事件** 如果需要更深层次地控制虚拟按键的行为,则可能涉及底层框架中的 `InputManagerService` 和 `WindowManagerPolicy` 组件。具体来说,Android 系统会将所有的输入事件(包括触摸屏、键盘以及导航栏上的虚拟按键)转换为标准化的 `input_event` 数据结构,并传递给相应的处理器[^5]。 对于第三方应用程序而言,虽然无法直接访问系统的 InputPipeline 流程,但仍可通过某些间接手段捕捉部分交互动作。例如,借助 Accessibility Service 来跟踪用户的焦点切换与手势触发路径。 ##### 使用 Accessibility Service 实现虚拟按键监测 创建一个继承自 `AccessibilityService` 的类,并在其回调函数中解析接收到的动作类型。 ```java public class VirtualKeyMonitor extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { int eventType = event.getEventType(); switch (eventType) { case AccessibilityEvent.TYPE_VIEW_CLICKED: Log.d("VirtualKey", "A view was clicked."); break; default: break; } } @Override public void onInterrupt() {} } ``` 配置文件声明服务权限及过滤条件: ```xml <service android:name=".VirtualKeyMonitor" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService"/> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service> ``` 上述代码允许程序感知屏幕上的点击活动,尽管其精度有限且主要面向辅助功能场景,但它提供了一种可行的技术路线[^3]。 --- #### 3. **动态调整虚拟导航栏可见性** 除了被动接收反馈外,还可以主动修改虚拟按键的表现形式以减少干扰风险。例如,在全屏模式下隐藏底部工具条即可有效降低意外触动概率。 调用以下方法可临时关闭软控件显示效果: ```java if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } else { requestWindowFeature(Window.FEATURE_NO_TITLE); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } ``` 注意该策略仅影响当前 Activity 的呈现样式而不改变实际硬件状态[^1]。 --- ### 总结 综上所述,针对 Android 平台下的虚拟按键管理需求,既可以从高层级入手定制 UI 行动序列,也可以深入挖掘操作系统内部运作原理寻求解决方案。然而需要注意的是,出于安全性和隐私保护方面的考量,许多敏感区域已被官方严格封锁,因此建议优先考虑合法合规的方式达成目的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值