網站首頁 編程語言 正文
前言
之前寫過幾篇關于輸入系統的文章,但是還沒有寫完,后來由于工作的變動,這個事情就一直耽擱了。而現在,在工作中,遇到輸入系統相關的事情也越來越多,其中有一個非常有意思的需求,因此是時候繼續分析 InputManagerService。
InputManagerService 系統文章,基于 Android 12 進行分析。
本文將以 IMS 簡稱 InputManagerService。
啟動流程
InputManagerService 是一個系統服務,啟動流程如下
// SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
// ..
// 1. 創建
inputManager = new InputManagerService(context);
// 注冊服務
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
// 保存 wms 的回調
inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
// 2. 啟動
inputManager.start();
try {
// 3. 就緒
if (inputManagerF != null) {
inputManagerF.systemRunning();
}
} catch (Throwable e) {
reportWtf("Notifying InputManagerService running", e);
}
// ...
}
IMS 的啟動流程分為三步
- 創建輸入系統,建立上層與底層的映射關系。
- 啟動輸入系統,其實就是啟動底層輸入系統的幾個模塊。
- 輸入系統就緒,上層會同步一些配置給底層輸入系統。
下面分三個模塊,分別講解這三步。
創建輸入系統
// InputManagerService.java
public InputManagerService(Context context) {
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
// 配置為空
mStaticAssociations = loadStaticInputPortAssociations();
// 默認 false
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
// 1. 底層進行初始化
// mPtr 指向底層創建的 NativeInputManager 對象
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
// 空
String doubleTouchGestureEnablePath = context.getResources().getString(
R.string.config_doubleTouchGestureEnableFile);
// null
mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
new File(doubleTouchGestureEnablePath);
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
IMS 構造函數,主要就是調用 nativeInit() 來初始化底層輸入系統。
// com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
// 從Java層的MessageQueue中獲取底層映射的MessageQueue
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
// 創建 NativeInputManager
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
// 返回指向 NativeInputManager 對象的指針
return reinterpret_cast<jlong>(im);
}
原來底層創建了 NativeInputManager 對象,然后返回給上層。
但是 NativeInputManager 并不是底層輸入系統的服務,它只是一個連接上層輸入系統和底層輸入系統的橋梁而已。來看下它的創建過程
// com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
// 1.保存上層的InputManagerService對象
mServiceObj = env->NewGlobalRef(serviceObj);
// 2. 初始化一些參數
{
AutoMutex _l(mLock);
// mLocked 的類型是 struct Locked,這里初始化了一些參數
// 這些參數會被上層改變
mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
mLocked.pointerCapture = false;
mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
}
mInteractive = true;
// 3.創建并注冊服務 InputManager
mInputManager = new InputManager(this, this);
defaultServiceManager()->addService(String16("inputflinger"),
mInputManager, false);
}
NativeInputManager 構造過程如下
- 創建一個全局引用,并通過 mServiceObj 指向上層的 InputManagerService 對象。
- 初始化參數。這里要注意一個結構體變量 mLocked,它的一些參數都是由上層控制的。例如,mLocked.showTouches 是由開發者選項中 "Show taps" 決定的,它的功能是在屏幕上顯示一個觸摸點。
- 創建并注冊服務 InputManager。
原來,InputManager 才是底層輸入系統的服務,而 NativeInputManagerService 通過 mServiceObj 保存了上層 InputManagerService 引用,并且上層 InputManagerService 通過 mPtr 指向底層的 NativeInputManager。因此,我們可以判定 NativeInputManager 就是一個連接上層與底層的橋梁。
我們注意到創建 InputManager 使用了兩個 this 參數,這里介紹下 NativeInputManager 和 InputManager 的結構圖
InputManager 構造函數需要的兩個接口正好是由 NativeInputManager 實現的,然而,具體使用這兩個接口的不是 InputManager,而是它的子模塊。這些子模塊都是在 InputManager 的構造函數中創建的
// InputManager.cpp
InputManager::InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
// 1. 創建InputDispatcher對象,使用 InputDispatcherPolicyInterface 接口
mDispatcher = createInputDispatcher(dispatcherPolicy);
// 2. 創建InputClassifier對象,使用 InputListenerInterface
mClassifier = new InputClassifier(mDispatcher);
// 3. 創建InputReader對象,使用 InputReaderPolicyInterface 和 InputListenerInterface
mReader = createInputReader(readerPolicy, mClassifier);
}
// InputDispatcherFactory.cpp
sp<InputDispatcherInterface> createInputDispatcher(
const sp<InputDispatcherPolicyInterface>& policy) {
return new android::inputdispatcher::InputDispatcher(policy);
}
// InputReaderFactory.cpp
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) {
return new InputReader(std::make_unique<EventHub>(), policy, listener);
}
InputManager 構造函數所使用的兩個接口,分別由 InputDispatcher 和 InputReader 所使用。因此 InputManager 向上通信的能力是由子模塊 InputDispatcher 和 InputReader 實現的。
InputManager 創建了三個模塊,InputReader、InputClassifier、InputDispatcher。 InputReader 負責從 EventHub 中獲取事件,然后把事件加工后,發送給 InputClassfier。InputClassifer 會把事件發送給 InputDispatcher,但是它會對觸摸事件進行一個分類工作。最后 InputDispatcher 對進行事件分發。
那么現在我們可以大致推算下輸入系統的關系圖,如下
這個關系圖很好的體現了設計模式的單一職責原則。
EventHub 其實只屬于 InputReader,因此要想解剖整個輸入系統,我們得逐一解剖 InputReader、InputClassifier、InputDispatcher。后面的一系列的文章將逐個來剖析。
啟動輸入系統
// InputManagerService.java
public void start() {
Slog.i(TAG, "Starting input manager");
// 1.啟動native層
nativeStart(mPtr);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
// 2.監聽數據庫,當值發生改變時,通過 native 層
// 監聽Settings.System.POINTER_SPEED,這個表示手指的速度
registerPointerSpeedSettingObserver();
// 監聽Settings.System.SHOW_TOUCHES,這個表示是否在屏幕上顯示觸摸坐標
registerShowTouchesSettingObserver();
// 監聽Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON
registerAccessibilityLargePointerSettingObserver();
// 監聽Settings.Secure.LONG_PRESS_TIMEOUT,這個多少毫秒觸發長按事件
registerLongPressTimeoutObserver();
// 監聽用戶切換
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
updateDeepPressStatusFromSettings("user switched");
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
// 3. 從數據庫獲取值,并傳遞給 native 層
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
updateDeepPressStatusFromSettings("just booted");
}
輸入系統的啟動過程如下
- 啟動底層輸入系統。其實就是啟動剛剛說到的 InputReader, InputDispatcher。
- 監聽一些廣播。因為這些廣播與輸入系統的配置有關,當接收到這些廣播,會更新配置到底層。
- 直接讀取配置,更新到底層輸入系統。
第2步和第3步,本質上其實都是更新配置到底層,但是需要我們對 InputReader 的運行過程比較熟悉,因此這個配置更新過程,留到后面分析。
現在我們直接看下如何啟動底層的輸入系統
// com_android_server_input_InputManagerService.cpp
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
// 調用InputManager::start()
status_t result = im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
通過 JNI 層的 NativeInputManager 這個橋梁來啟動 InputManager。
前面用一幅圖表明了 NativeInputManager 的橋梁作用,現在感受到了嗎?
status_t InputManager::start() {
// 啟動 Dispatcher
status_t result = mDispatcher->start();
if (result) {
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
// 啟動 InputReader
result = mReader->start();
if (result) {
ALOGE("Could not start InputReader due to error %d.", result);
mDispatcher->stop();
return result;
}
return OK;
}
InputManager 的啟動過程很簡單,就是直接啟動它的子模塊 InputDispatcher 和 InputReader。
InputDispatcher 和 InputReader 的啟動,都是通過 InputThread 創建一個線程來執行任務。
//InputThread.cpp
InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
: mName(name), mThreadWake(wake) {
mThread = new InputThreadImpl(loop);
mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}
注意 InputThread 可不是一個線程,InputThreadImpl 才是一個線程,如下
//InputThread.cpp
class InputThreadImpl : public Thread {
public:
explicit InputThreadImpl(std::function<void()> loop)
: Thread(/* canCallJava */ true), mThreadLoop(loop) {}
~InputThreadImpl() {}
private:
std::function<void()> mThreadLoop;
bool threadLoop() override {
mThreadLoop();
return true;
}
};
當線程啟動后,會循環調用 threadLoop(),直到這個函數返回 false。從 InputThreadImpl 的定義可以看出,threadLoop() 會一直保持循環,并且每一次循環,會調用一次 mThreadLoop(),而函數 mThreadLoop 是由 InputReader 和 InputDispacher 在啟動時傳入
// InputReader.cpp
status_t InputReader::start() {
if (mThread) {
return ALREADY_EXISTS;
}
// 線程啟動后,循環調用 loopOnce()
mThread = std::make_unique<InputThread>(
"InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
return OK;
}
// InputDispatcher.cpp
status_t InputDispatcher::start() {
if (mThread) {
return ALREADY_EXISTS;
}
// 線程啟動后,循環調用 dispatchOnce()
mThread = std::make_unique<InputThread>(
"InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
return OK;
}
現在,我們可以明白,InputReader 啟動時,會創建一個線程,然后循環調用 loopOnce() 函數,而 InputDispatcher 啟動時,也會創建一個線程,然后循環調用 dispatchOnce()。
輸入系統就緒
// InputManagerService.java
public void systemRunning() {
mNotificationManager = (NotificationManager)mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
synchronized (mLidSwitchLock) {
mSystemReady = true;
// Send the initial lid switch state to any callback registered before the system was
// ready.
int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID);
for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
LidSwitchCallback callback = mLidSwitchCallbacks.get(i);
callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP);
}
}
// 監聽廣播,通知底層加載鍵盤布局
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateKeyboardLayouts();
}
}, filter, null, mHandler);
// 監聽廣播,通知底層加載設備別名
filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
reloadDeviceAliases();
}
}, filter, null, mHandler);
// 直接通知一次底層加載鍵盤布局和加載設備別名
mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);
mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);
if (mWiredAccessoryCallbacks != null) {
mWiredAccessoryCallbacks.systemReady();
}
}
private void reloadKeyboardLayouts() {
nativeReloadKeyboardLayouts(mPtr);
}
private void reloadDeviceAliases() {
nativeReloadDeviceAliases(mPtr);
}
無論是通知底層加載鍵盤布局,還是加載設備別名,其實都是讓底層更新配置。與前面一樣,更新配置的過程,留到后面分析。
結束
通過本文,我們能大致掌握輸入系統的輪廓。后面,我們將逐步分析子模塊 InputReader 和 InputDispatcher 的功能。
原文鏈接:https://juejin.cn/post/7161376731096432653
相關推薦
- 2022-07-24 docker容器使用GPU方法實現_docker
- 2022-06-07 如何在Python中妥善使用進度條詳解_python
- 2022-11-13 MacOS下C++使用WebRTC注意事項及問題解決_C 語言
- 2022-09-27 React?函數式組件和類式組件詳情_React
- 2022-08-28 linux應用參數保存與配置
- 2022-06-16 Golang項目搭配nginx部署反向代理負載均衡講解_Golang
- 2022-04-09 C++實現簡易計算器功能_C 語言
- 2023-01-19 C++深度探索虛函數指針示例_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支