Android 的 KeyEvent : 從 EventHub 到 PhoneWindowManager


最近追蹤了一下 Android 4.3 的 source,並且追蹤了 KeyEvent 一路從 EventHub.cpp 到 PhoneWondowManager.java 的流程
這邊順便記錄一下。

Android 在 Linux kernal 的部份,所有的 Input Event 都會使用 /dev/input/event0~X 的 device node
要 Monitoring Input Event,基本上可以去 pulling 這幾個 node (但是拿到的會是 RawEvent Data,而且應該會是顯示亂碼)

或是可以利用 adb shell getevent 的指令,一來他會列出這個 node 現在使用的 device name 是什麼 (例如鍵盤、gpio-key或是 touch screen等)也會把 Raw Event 轉換成比較容易了解的格式,以及數字,在 Debug 上也蠻好用的。

完整的 call flow 有興趣的可以點開

切入正題,目前看到 Android 4.3 KeyEvent 的流程大致上為:

EventHub.cpp 基本上去監控 /dev/input/eventX 的 node,但是只是提供 function 來讓外部存取。
一開始是由 frameworks/base/service/input/InputManager.cpp 中會建立 InputReaderThread
並且在初始化完成之後開始 run。

接下來的 code 就會跑到 frameworks/base/service/input/InputReader.cpp 裡面
這個 Thread 基本上就是去做 pulling 的動作,並且實作在 loopOnce 這個 function

InputReader.cpp
void InputReader::loopOnce() {
    ...
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    ...
        processEventsLocked(mEventBuffer, count);
    ...
}

在這個 function 裡面會去呼叫到 mEventHub->getEvents() 並取得最原始的 RawEvent
接著會丟到 processEventsLocked() 然後 processEventsForDeviceLocked()

InputReader.cpp
void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    ...
    InputDevice* device = mDevices.valueAt(deviceIndex);
    ...
    device->process(rawEvents, count);
}

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    ...
    for (size_t i = 0; i < numMappers; i++) {
        InputMapper* mapper = mMappers[i];
        mapper->process(rawEvent);
    }
}  

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, 
            int32_t scanCode, uint32_t policyFlags) {
    ...
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
    getListener()->notifyKey(&args);  
}

接著會根據目前 Event 的 device 種類,丟到 InputDevice 的 process()
再來會把這個 RawEvent 丟給所有的 InputMapper 的 process() 看誰要處理
以 KeyEvent 來說,會處理的會是 KeyboardInputMapper (一樣位於 InputReader.cpp 內)
所以由 KeyboardInputMapper->process() 然後送到 processKey() function

InputListener.cpp
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

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();
}

接著會跳到 frameworks/base/service/input/InputListener.cpp 中,剛剛 processKey() 中會把這些 Event 的資訊丟到 QueuedInputListener::notifyKey() 然後 push 到  mArgqueue 之中,每次flush的時候交給 mInnerListener 去處理

InputDispatcher.cpp
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ...
    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->action,
            flags, args->keyCode, args->scanCode, metaState, 0,
            args->downTime, args->eventTime);
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    ...
}

再來剛剛的 mListener 其實就是 InputDispatcher 所以接下來會接到 frameworks/base/service/input/InputDispatcher.cpp 之中的 notifyKey function,並在此第一次打包成 KeyEvent 的格式 (還不是 Java 物件)
然後送到 mPolicy->interceptKeyBeforeQueueing()

com_android_server_input_ImputManagerService.cpp
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags){
    ...
  JNIEnv* env = jniEnv();
    jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
    if (keyEventObj) {
        wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags, isScreenOn);
        ...
    }
}

而這個 mPolict 就是 InputManagerService 的 native 物件,位於 frameworks/base/service/jni/com_android_server_input_ImputManagerService.cpp
然後再這裡會透過 JNI 的 function android_view_KeyEvent_fromNative() 把底層的 KeyEvent 轉成 Java 的 KeyEvent 物件
在下來透過 native callback 的方式,把做好的 KeyEvent 物件(Java版) 丟回 Java層的 InputManagerService,位置在 frameworks/base/service/java/com/android/server/input/InputManagerService.java 裡面的 interceptKeyBeforeQueueing() 接著裡面也只做一件事,把這個 Event 繼續 pass 給 mWindowManagerCallbacks 也就是 Android 上層最早接受到 KeyEvent 的 PhoneWindowManager 
其位置在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java

所有 Activity 會收到的 KeyEvent,或是系統會前置處理的 KeyEvent (比方說 HOME鍵,一些實體鍵盤的快捷鍵) 都是由這個位置開始的,藉由這次工作上剛好有機會去 trace 這一串 call flow,也順便記錄一下,未來有需要的時候可以參考。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值