最近接觸到了一個問題:耳機插入事件的由來,走讀了下IMS輸入系統服務的源碼。同時,IMS輸入系統服務在Android的開發過程中,也經常出現,有必要瞭解下相關原理。 ...
目錄
- 一、目的
- 二、環境
- 三、相關概念
- 四、詳細設計
- 五、Input設備節點介紹
- 六、參考資料
一、目的
最近接觸到了一個問題:耳機插入事件的由來,走讀了下IMS輸入系統服務的源碼。同時,IMS輸入系統服務在Android的開發過程中,也經常出現,有必要瞭解下相關原理。
- 學習下IMS輸入系統的源碼設計,瞭解該模塊承擔的業務職責,熟悉Android結構
- 瞭解Android屏幕點擊事件、物理按鍵事件的分發規則
二、環境
- 版本:Android 11
- 平臺:高通 QCM2290
三、相關概念
3.1 輸入設備
常見的輸入設備有滑鼠、鍵盤、觸摸屏等,用戶通過輸入設備與系統進行交互。
3.2 UEVENT機制
"uevent" 是 Linux 系統中的一種事件通知機制,用於向用戶空間發送有關內核和設備狀態變化的通知。這種機制通常用於設備驅動程式、熱插拔事件以及設備狀態變化等場景,以便用戶空間應用程式能夠在這些事件發生時做出相應的響應。
3.3 JNI
JNI,全稱Java Native Interface,是Java編程語言的一種編程框架,用於實現Java代碼與其他編程語言(如C、C++)進行交互的介面。JNI允許Java程式調用原生代碼(native code),即由其他編程語言編寫的代碼,並且允許原生代碼調用Java代碼。通過JNI,Java程式可以訪問底層系統功能、使用硬體設備、調用第三方庫等。
3.4 EPOLL機制
監聽多個描述符的可讀/可寫狀態。等待返回時攜帶了可讀的描述符
3.5 INotify
Linux 內核所提供的一種文件系統變化通知機制。可以監控文件系統的變化,如文件新建、刪除、讀寫等
四、詳細設計
通過屏幕的觸摸事件,來分析IMS系統,相關如下
4.1 結構圖
4.2 代碼結構
層級 | 模塊 | 描述 | 源碼 | 編譯產物 |
---|---|---|---|---|
Framework | InputManagerService | xxx | frameworks/base/services/core/java/ | out/target/product/qssi/system/framework/services.jar |
Native | NativeInputManager | xxx | frameworks/base/services/core/jni/ | out/target/product/qssi/system/lib64/libandroid_servers.so |
Native | Inputflinger | xxx | frameworks/native/services/inputflinger/ | out/target/product/qssi/system/lib64/libinputflinger.so |
Native | Inputreader | xxx | frameworks/native/services/inputflinger/reader | out/target/product/qssi/system/lib64/libinputreader.so |
Native | Inputdispatcher | xxx | frameworks/native/services/inputflinger/dispatcher/ | (靜態庫)out/soong/.intermediates/frameworks/native/services/inputflinger/dispatcher/libinputdispatcher/android_arm64_armv8-a_static/libinputdispatcher.a |
Native | NativeInputEventReceiver | xxx | frameworks/base/core/jni/ | out/target/product/qssi/system/lib64/libandroid_runtime |
Native | InputChannel | xxx | frameworks/native/libs/input/ | out/target/product/qssi/system/lib64/libinput.so |
4.3 InputManagerService模塊
InputManagerService是Android框架層一個非核心服務,主要是提供一個IMS輸入系統啟動的入口,同時對應用層提供業務相關介面。
4.3.1 IMS服務入口
Android設備開機後,會啟動system_server進程,InputManagerService服務(以下簡稱IMS)在該進程被喚起。
@frameworks\base\services\java\com\android\server\SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
t.traceBegin("StartInputManagerService");
inputManager = new InputManagerService(context);//新建IMS實例
t.traceEnd();
...
t.traceBegin("StartInputManager");
inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());//設置窗體事件監聽
inputManager.start();//啟動IMS服務
t.traceEnd();
...
}
4.3.2 IMS初始化
此處做一些IMS相關的初始化操作,會調用nativeInit方法,獲取一個NativeInputManager對象,類似於一個句柄。
@frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
public InputManagerService(Context context) {
...
mStaticAssociations = loadStaticInputPortAssociations();
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
...
}
4.3.3 IMS啟動
InputManagerService通過start方法啟動,會調用nativeStart方法,該方法為Native方法
@frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
private static native void nativeStart(long ptr);
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
...
}
4.3.4 IMS消息監聽
該方法為Native的回調方法,用於上報IMS事件,如耳機插入事件等。
@frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
// Native callback.
private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
...
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
}
if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
}
if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
switchMask);
}
...
}
4.4 NativeInputManager模塊
該模塊為JNI模塊,主要處理Java方法與c++方法映射關係,即IMS服務與InputFlinger模塊的通信橋梁。
4.4.1 nativeInit初始化
(1)新建一個NativeInputManager對象,並將該對象返回給java層
@\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);
...
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
(2)創建InputManager管理類,主要用於管理Input事件分發、事件讀取行為
@\frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
...
mInputManager = new InputManager(this, this);
defaultServiceManager()->addService(String16("inputflinger"),
mInputManager, false);
}
4.4.2 nativeStart啟動
獲取上一個階段創建NativeInputManager對象,並引用start啟動該模塊
@\frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
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.");
}
}
4.5 Inputflinger模塊
input事件的管理類,數據傳遞類,也是輸入系統native層核心的模塊。
ps: 根據字典里的定義,flinger是指出軌的人。在SurfaceFlinger的例子中,它把可視數據扔給surface AudioFlinger把音頻數據扔給適當的接收者。它們只是“可愛”的詞…