最近追蹤了一下 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. ArcGIS在Android的应用
  2. "android sdk Content Loader's has encountered a problem"的解
  3. 使用MediaPlayer播放音乐文件
  4. InputFilter方法filter 解释
  5. Android之--电话归属地的悬浮框的…
  6. android 7.0 手机android.content.res.XmlResourceParser androi
  7. Android(安卓)多线程下载
  8. Android(安卓)UI开发第六篇——仿QQ的滑动Tab
  9. Android手机定位案例代码

随机推荐

  1. android收藏
  2. Android闹钟服务AlarmManager
  3. android传送照片到FTP服务器
  4. Android之使用Activity与Fragment通信
  5. android 抽奖盘动画 自定义View
  6. android dpi换算以及常用分辨率列表
  7. Android邮件地址正则表达式
  8. Android之水平ProgressBar多彩背景颜色
  9. android 获取农历日期和天干、地支
  10. Android显示网络图片相关实现方法浅谈