Android 从问题出发探索KeyEvent分发流程(1) PhoneWindowManager是如何收到事件的

源码环境是Android13,所有非必要代码都做了省略处理,最大程度简化主干分析过程

1. 代码分析小目标

按照3个小问题来看下KeyEvent的传递过程

  1. PhoneWindowManager是如何收到事件的
  2. Activity是如何收到KeyEvent的
  3. View的keyListener是如何收到事件的

Step 1: SystemServermain 方法创建InputManagerServiceWindowManagerService 对象,并将WMS内部的InputManagerCallback传入IMS方便接收新数据

base/services/java/com/android/server/SystemServer.java

java 复制代码
    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }
    
    private void run() {
         try {
            startOtherServices(t);
        } catch (Throwable ex) {

        } finally {
        }
    }
    
    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
            // WMS needs sensor service ready
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
                    /.../
           inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
           inputManager.start();
    }
    
    // InputManagerService.start内部其实是调用了他的mNative实现
public void start() {
    Slog.i(TAG, "Starting input manager");
    mNative.start();
}

Step 2: KeyEvent是从InputManager转发过来,真正的处理过程实际是Native去做的,所以我们先找到IMS的Native实现,众所周知,java转到Native肯定有jni定义,我们如何找到jni文件呢,jni文件一般是包名加类名 ,也就是com_android_server_input_InputManagerService.cpp ,C层最终业务的实际的实现类是InputManager.cpp,内部调用start创建并启动InputReader和InputDispatcher

/base/services/core/jni/com_android_server_input_InputManagerService.cpp

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

    mServiceObj = env->NewGlobalRef(serviceObj);

    {
        AutoMutex _l(mLock);
        mLocked.systemUiLightsOut = false;
        mLocked.pointerSpeed = 0;
        mLocked.pointerAcceleration = android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
        mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
    }
    mInteractive = true;
    mTouchWakeup = property_get_bool("ro.duershow.touchwakeup.enable", 0);
    InputManager* im = new InputManager(this, this);
    mInputManager = im;
    defaultServiceManager()->addService(String16("inputflinger"), im);
}

/native/services/inputflinger/InputManager.cpp

c++ 复制代码
InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = std::make_unique<InputClassifier>(*mDispatcher);
    mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mClassifier);
    // Dispatcher类被一层层包装后混进了Inputeader获取情报
    mReader = createInputReader(readerPolicy, *mBlocker);
}

status_t InputManager::start() {
    status_t result = mDispatcher->start();
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }

    result = mReader->start();
    if (result) {
        ALOGE("Could not start InputReader due to error %d.", result);

        mDispatcher->stop();
        return result;
    }

    return OK;
}

Step 3: InputReader 借助EventHub 获取到设备事件,最终调用getListener()->notifyKey 发送事件,这个流程稍微长一点,涉及到InputReaderInputDevice , 从Step 2最后一段代码可以知道,InputReader创建时候将InputDispatcher作为listener,所以实际调用的是InputDispatcher的notifyKey.

/native/services/inputflinger/reader/InputReader.cpp

InputReader::loopOnce() -> InputReader::processEventsLocked -> InputReader::processEventsForDeviceLocked

cpp 复制代码
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
    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;
    }

    device->process(rawEvents, count);
}

逻辑转到InputDevice里面,调用process

/native/services/inputflinger/reader/InputDevice.cpp

cpp 复制代码
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
    // We cannot simply ask each mapper to process them in bulk because mappers may
    // have side-effects that must be interleaved.  For example, joystick movement events and
    // gamepad button presses are handled by different mappers but they should be dispatched
    // in the order received.
    for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
                mapper.process(rawEvent);
            });
        --count;
    }
}

遍历map这个map保存的是不同设备的数据几何,比如我们现在探索的就是KeyboardInputMapper ,代表键盘设备,android有很多的InputMapper但是最终都是通过getListener 调用不同的函数将事件交给InputDispatcher

cpp 复制代码
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)) {
                processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
                           usageCode);
            }
            break;
        }
    }
}
cpp 复制代码
void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
                                     int32_t usageCode) {

    NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
                       getDisplayId(), policyFlags,
                       down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
                       AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener().notifyKey(&args);
}

Step 4: InputDispatcher收到事件是需要检查然后加入队列,在加入队列之前会先调用mPolicy->interceptKeyBeforeQueueing入队列前给一次处理机会

cpp 复制代码
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {

    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);


        needWake = enqueueInboundEventLocked(std::move(newEntry));
  
    } // release lock

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

mPolicy的赋值是在InputDispatcher构造函数 处理,所以需要我们往回找他的实现,从Step 2 的代码可以知道InputDispatcher 是在InputManager 构造里创建的,且是透传的参数,InputManager 又是在com_android_server_input_InputManagerService.cpp 内创建,且参数传的都是this ,也就是NativeInputManager ,所以实际调用NativeInputManagerinterceptKeyBeforeQueueing

Step 5: NativeInputManager调用java层的同名函数

/base/services/core/jni/com_android_server_input_InputManagerService.cpp

cpp 复制代码
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {

    if ((policyFlags & POLICY_FLAG_TRUSTED)) {
        nsecs_t when = keyEvent->getEventTime();
        JNIEnv* env = jniEnv();
        jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
        jint wmActions;
        if (keyEventObj) {
        // C反向调用java代码,也就是InputManagerService.java的interceptKeyBeforeQueueing
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
         
    } else {
        if (interactive) {
            policyFlags |= POLICY_FLAG_PASS_TO_USER;
        }
    }
}

Step 6: InputManagerServcie继续透传到WMS

/base/services/core/java/com/android/server/input/InputManagerService.java

java 复制代码
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    // 还记得step 1提到的WMS安插的间谍
    return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}

/base/services/core/java/com/android/server/wm/InputManagerCallback.java

java 复制代码
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}

mService 就是WindowManagerServicemPolicy 是在WindowManagerServicemain 函数调用构造时候传入,在SystemServer 调用main 时候传入的PhoneWindowManager

Step 7: WMS回调PhoneWindowManagerinterceptKeyBeforeQueueing,至此,PhoneWindowManager如何收到的KeyEvent就结束了,这个时候KeyEvent还没有进入分发流程

相关推荐
m0_548514772 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php
凤邪摩羯3 小时前
Android-性能优化-03-启动优化-启动耗时
android
凤邪摩羯3 小时前
Android-性能优化-02-内存优化-LeakCanary原理解析
android
喀什酱豆腐3 小时前
Handle
android
m0_748232925 小时前
Android Https和WebView
android·网络协议·https
m0_748251725 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
m0_748254667 小时前
go官方日志库带色彩格式化
android·开发语言·golang
zhangphil7 小时前
Android使用PorterDuffXfermode模式PorterDuff.Mode.SRC_OUT橡皮擦实现“刮刮乐”效果,Kotlin(2)
android·kotlin
爱学测试的李木子7 小时前
从0到1搭建 Android 自动化 python+appium 环境
android·软件测试·python·测试工具·自动化
咸芝麻鱼7 小时前
Android Studio | 连接手机设备后,启动App时出现:Waiting For DebuggerApplication (App名)...
android·adb·智能手机·android studio