西昌網(wǎng)站建設(shè)我想在百度上發(fā)布廣告怎么發(fā)
文章目錄
- 前言
- 一、輸入系統(tǒng)的基本組成部分
- 二、輸入系統(tǒng)相關(guān)源碼分析
- 1、IMS 構(gòu)建
- 1.1、SystemServer # startOtherServices()
- 1.2、InputManagerService
- 1.3、NativeInputManager # nativeInit()
- 1.4、NativeInputManager
- 1.5、InputManager
- 1.6、InputDispatcher
- 1.7、InputReader
- 1.8、EventHub
- 1.9、小結(jié)
- 2、IMS 啟動
- 2.1、IMS # start()
- 2.2、NativeInputManager # nativeStart()
- 2.3、InputManager # start()
- 2.4、InputDispatcher # start()
- 2.5、InputReader # start()
- 2.6、InputThread
- 3、IMS 系統(tǒng)就緒
- 三、總結(jié)
- 1、IMS 啟動時序圖
- 2、IMS 成員關(guān)系圖
- 參考
前言
Android 輸入系統(tǒng)(Input System) 的工作原理,包括:輸入設(shè)備的管理、輸入事件的加工方式及派發(fā)流程。首先輸入設(shè)備包括:觸摸屏,鍵盤,鼠標(biāo)和手柄等,其中觸摸屏與鍵盤是 Android 最普遍也是最標(biāo)準(zhǔn)的輸入設(shè)備。當(dāng)用戶操作輸入設(shè)備時,Linux 內(nèi)核接收到相應(yīng)的硬件中斷,然后將中斷加工成原始的輸入事件數(shù)據(jù)并寫入其對應(yīng)的設(shè)備節(jié)點中,在用戶空間可以通過輸入系統(tǒng)內(nèi)部的讀取函數(shù)將原始事件數(shù)據(jù)讀出,并進(jìn)行一系列的翻譯加工成 Android 輸入事件,然后在所有的窗口中尋找合適的事件接收者,并派發(fā)給它來消費(fèi)該輸入事件??梢?#xff0c;輸入系統(tǒng)在整個輸入事件處理過程中起到了承上啟下的銜接作用。
一、輸入系統(tǒng)的基本組成部分
上圖展示了輸入事件的處理流程以及輸入系統(tǒng)中最基本的參與者,下面簡要介紹一下各個參與者:
- Linux 內(nèi)核:接受輸入設(shè)備的中斷,并將原始輸入事件的數(shù)據(jù)寫入設(shè)備節(jié)點中;
- 設(shè)備節(jié)點:內(nèi)核與 InputManagerService 的橋梁,它將原始事件的數(shù)據(jù)暴露給用戶空間,以便 InputManagerService 可以從中讀取事件;
- InputManagerService:Android 系統(tǒng)服務(wù),以后簡稱 IMS,其分為 Java 層和 Native 層兩部分。Java 層負(fù)責(zé)與 WindowManagerService 通信。而 Native 層則是InputReader 和 InputDispatcher 兩個輸入系統(tǒng)關(guān)鍵組件的運(yùn)行容器;
- EventHub:直接訪問所有的設(shè)備節(jié)點。并且正如其名字所描述的,它通過一個名為 getEvents( ) 的函數(shù)將所有輸入系統(tǒng)相關(guān)的待處理的底層事件返回給使用者,包括原始輸入事件、設(shè)備節(jié)點的增刪等。
- InputReader:IMS 中的關(guān)鍵組件之一,運(yùn)行于一個獨立的線程中,負(fù)責(zé)管理輸入設(shè)備的列表與配置,以及進(jìn)行輸入事件的加工處理。通過其線程循環(huán)不斷地通過 getEvent( ) 函數(shù)從 EventHub 中將事件取出并進(jìn)行處理。對于設(shè)備節(jié)點的增刪事件,將會更新輸入設(shè)備列表與配置。對于原始輸入事件,InputReader 對其進(jìn)行翻譯、組裝、封裝為包含更多信息、更具可讀性的輸入事件,然后交給 InputDispatcher 進(jìn)行派發(fā);
- InputReaderPolicy:為 InputReader 的事件加工處理提供一些策略配置,例如鍵盤布局信息等;
- InputDispatcher:IMS 中的另一個關(guān)鍵組件,也運(yùn)行于一個獨立的線程中。InputDispatcher 中保管了來自 WindowManagerService 的所有窗口的信息,其收到來自 InputReader 的輸入事件后,會在其保管的窗口中尋找合適的窗口,并將事件派發(fā)給此窗口;
- InputDispatcherPolicy:為 InputDispatcher 的派發(fā)過程提供策略控制。例如截取某些特定的輸入事件用作特殊用途,或者阻止將某些事件派發(fā)給目標(biāo)窗口。一個典型的例子就是 HOME 鍵被 InputDispatcherPolicy 截取到 PhoneWindowManager 中進(jìn)行處理,并阻止窗口收到 HOME 鍵按下的事件;
- WindowManagerService:雖不是輸入系統(tǒng)中的成員,但卻對 InputDispatcher 的正常工作起到了至關(guān)重要的作用。當(dāng)新建窗口時,WMS 為新窗口和 IMS 之間創(chuàng)建了事件傳遞所用的通道。另外,WMS 還將所有窗口的信息,包括窗口的可點擊區(qū)域、焦點窗口等信息,實時地更新到 IMS 的 InputDispatcher 中,使得 InputDispatcher 可以正確地將事件派發(fā)到指定的窗口;
- ViewRootImpl:對某些窗口,如壁紙窗口、SurfaceView 的窗口來說,窗口就是輸入事件派發(fā)的終點。而對其他的如 Activity、對話框等使用了 Android 控件系統(tǒng)的窗口來說,輸入事件的終點是控件 View。ViewRootImpl 將窗口所接收的輸入事件沿著控件樹將事件派發(fā)給感興趣的控件 View;
二、輸入系統(tǒng)相關(guān)源碼分析
我們知道,Zygote 進(jìn)程創(chuàng)建并啟動后,在 fork 出的子進(jìn)程 SystemServer 的初始化過程中啟動 Android 系統(tǒng)所有的 Service 服務(wù),這些系統(tǒng)服務(wù)分為三大類:引導(dǎo)服務(wù)、核心服務(wù)及其他服務(wù),具體的啟動流程可參考探索Framework之SystemServer進(jìn)程的啟動詳解。
輸入系統(tǒng)服務(wù) IMS 是在啟動其他服務(wù)里面啟動的,接下來從源碼的角度來繼續(xù)探索分析。
1、IMS 構(gòu)建
在 SystemServer 類中找到啟動其他服務(wù) startOtherServices() 方法的代碼,提取主要的邏輯代碼進(jìn)行分析,源碼如下:
1.1、SystemServer # startOtherServices()
xref: /frameworks/base/services/java/com/android/server/SystemServer.java
public final class SystemServer implements Dumpable {......private void startOtherServices(@NonNull TimingsTraceAndSlog t) {final Context context = mSystemContext;WindowManagerService wm = null;......InputManagerService inputManager = null;......try {......// 啟動 InputManagerService 服務(wù)t.traceBegin("StartInputManagerService"); // 新建 InputManagerService 對象inputManager = new InputManagerService(context);......t.traceBegin("StartWindowManagerService"); // 啟動 WindowManagerService 服務(wù)mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);// 使用新建的 IMS 對象來構(gòu)建 WMS 對象wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);......// 將 InputManagerService 發(fā)布到 ServiceManager 以便調(diào)用者可以訪問 IMS 提供的接口ServiceManager.addService(Context.INPUT_SERVICE, inputManager,/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);t.traceBegin("SetWindowManagerService"); // ActivityManagerService 設(shè)置 WindowManagerServicemActivityManagerService.setWindowManager(wm);t.traceBegin("StartInputManager"); // 設(shè)置向 WMS 發(fā)起回調(diào)的 callback 對象inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());inputManager.start(); // 啟動 InputManagerService,具體見......} catch (Throwable e) {......// 日志輸出并拋出異常}......final InputManagerService inputManagerF = inputManager;t.traceBegin("MakeInputManagerServiceReady");try {if (inputManagerF != null) {// 輸入系統(tǒng) IMS 準(zhǔn)備就緒inputManagerF.systemRunning();}} catch (Throwable e) {reportWtf("Notifying InputManagerService running", e);}......}......
}
IMS 的啟動流程可以分為以下三個步驟:
- 構(gòu)建 IMS 實例對象,并建立上層與底層的映射關(guān)系。
- 啟動 IMS,其內(nèi)部就是啟動 native 層輸入系統(tǒng)的幾個重要參與者(后續(xù)會分析)。
- IMS 系統(tǒng)就緒,此時 Java 層會同步一些配置給 native 層輸入系統(tǒng)。
首先是 IMS 實例對象的構(gòu)建,分析查看 IMS 類的源碼
1.2、InputManagerService
xref: /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stubimplements Watchdog.Monitor {......private final Context mContext;private final InputManagerHandler mHandler;......private static native long nativeInit(InputManagerService service,Context context, MessageQueue messageQueue);......public InputManagerService(Context context) {this.mContext = context;// 獲取 DisplayThread 的 Looper 創(chuàng)建 IMS 內(nèi)部的 InputManagerHandler 對象this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());mStaticAssociations = loadStaticInputPortAssociations();mUseDevInputEventForAudioJack =context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="+ mUseDevInputEventForAudioJack);// 每一個分為 Java 和 Native 兩部分的對象在創(chuàng)建時都會有一個 native 函數(shù)// 在創(chuàng)建 Java 層對象的同時 native 層也創(chuàng)建一個,注意:使用的是同一個 Looper 對象// mPtr 指向底層創(chuàng)建的 NativeInputManager 對象mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());String doubleTouchGestureEnablePath = context.getResources().getString(R.string.config_doubleTouchGestureEnableFile);mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :new File(doubleTouchGestureEnablePath);// 新建 IMS 的本地系統(tǒng)服務(wù) LocalService,其繼承自 InputManagerInternal 抽象接口,并加入到 LocalServices 中LocalServices.addService(InputManagerInternal.class, new LocalService());}
}
方法中,獲取 DisplayThread 的 Looper,新建 InputManagerHandler 對象,然后調(diào)用 native 層的 nativeInit() 函數(shù),創(chuàng)建NativeInputManager 對象,最后新建 IMS 的本地系統(tǒng)服務(wù) LocalService,其繼承自 InputManagerInternal 抽象接口,并加入到 LocalServices 中。
DisplayThread 在 system_server 進(jìn)程中是單例的,且只能被 WindowManager、DisplayManager、InputManager 使用。
1.3、NativeInputManager # nativeInit()
xref: /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static const JNINativeMethod gInputManagerMethods[] = { // JNI 注冊的映射關(guān)系/* name, signature, funcPtr */{"nativeInit","(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/""MessageQueue;)J",(void*)nativeInit},
};static jlong nativeInit(JNIEnv*env, jclass /* clazz */,jobject serviceObj, jobject contextObj, jobject messageQueueObj) {// 由傳入的 Java 層的 MessageQueue 轉(zhuǎn)換獲取 native 層的 MessageQueuesp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);if (messageQueue == nullptr) {jniThrowRuntimeException(env, "MessageQueue is not initialized.");return 0;}// 新建 NativeInputManager 對象,此對象將是 native 層組件與 Java 層 IMS 進(jìn)行通信的橋梁NativeInputManager * im = new NativeInputManager(contextObj, serviceObj,messageQueue -> getLooper());im -> incStrong(0);// 返回指向 NativeInputManager 對象的指針給 Java 層的 IMS,IMS 將其保存在 mPtr 成員變量中return reinterpret_cast < jlong > (im);
}
通過 JNI 注冊的映射關(guān)系,找到 native 層的 nativeInit() 函數(shù),首先由傳入的 Java 層的 MessageQueue 轉(zhuǎn)換獲取 native 層的 NativeMessageQueue 對象,然后新建 NativeInputManager 對象。
1.4、NativeInputManager
xref: /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
class NativeInputManager : public virtual RefBase,public virtual InputReaderPolicyInterface,public virtual InputDispatcherPolicyInterface,public virtual PointerControllerPolicyInterface {public:NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);inline sp<InputManagerInterface> getInputManager() const { return mInputManager; }private:sp<InputManagerInterface> mInputManager; jobject mServiceObj; // IMS 對象sp<Looper> mLooper; // Looper 對象Mutex mLock;struct Locked {......例如,mLocked.showTouches 是// 如果為 True,則啟用指針手勢bool pointerGesturesEnabled;// 由開發(fā)者選項中 "Show taps" 決定的,其功能是在屏幕上顯示一個觸摸點bool showTouches;......} mLocked GUARDED_BY(mLock);std::atomic<bool> mInteractive;......static inline JNIEnv* jniEnv() {return AndroidRuntime::getJNIEnv();}
};NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const sp<Looper>&looper) :mLooper(looper),mInteractive(true) {JNIEnv * env = jniEnv();// 保存 Java 層的 InputManagerService 對象mServiceObj = env -> NewGlobalRef(serviceObj);{ // mLocked 的類型是 struct Locked,這里初始化了一些參數(shù),這些參數(shù)會被 Java 層改變AutoMutex _l (mLock);mLocked.systemUiLightsOut = false;mLocked.pointerSpeed = 0;mLocked.pointerGesturesEnabled = true;mLocked.showTouches = false;mLocked.pointerCapture = false;mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;}mInteractive = true;// 創(chuàng)建了 native 層的 InputManager,它才是底層輸入系統(tǒng)的服務(wù)InputManager * im = new InputManager(this, this);mInputManager = im;// 將 InputManager 注冊到 ServiceManager 中defaultServiceManager()->addService(String16("inputflinger"), im);
}
- 在 NativeInputManager 的構(gòu)造函數(shù)中,創(chuàng)建一個全局引用,并通過 mServiceObj 指向 Java 層的 IMS 對象,便于后續(xù)可以通過 mServiceObj 調(diào)用 Java 層 IMS 對象的方法。
- 初始化參數(shù),這里要注意一個結(jié)構(gòu)體變量 mLocked,它的一些參數(shù)都是由 Java 層控制的。
- 然后將自己作為參數(shù)來新建 InputManager 對象,并將 InputManager 注冊到 ServiceManager 中,InputManager 才是 native 層輸入系統(tǒng)的服務(wù)。
注意:由 NativeInputManager 類的聲明可以看到,其實現(xiàn)了 InputReaderPolicyInterface 與 InputDispatcherPolicyInterface 兩個接口。
1.5、InputManager
xref: /frameworks/native/services/inputflinger/InputManager.h
class InputManager : public InputManagerInterface, public BnInputFlinger {
protected:~InputManager() override;public:InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);......
private:sp<InputReaderInterface> mReader;sp<InputClassifierInterface> mClassifier;sp<InputDispatcherInterface> mDispatcher;
};
xref: /frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {// 創(chuàng)建 InputDispatcher 對象,使用 InputDispatcherPolicyInterface 接口,用于對事件進(jìn)行分發(fā)mDispatcher = createInputDispatcher(dispatcherPolicy);// 創(chuàng)建 InputClassifier 對象,使用 InputListenerInterface,用于對事件分類mClassifier = new InputClassifier(mDispatcher);// 創(chuàng)建 InputReader 對象,使用 InputReaderPolicyInterface 和 InputListenerInterface// 其通過 EventHub 監(jiān)聽"/dev/input"事件,獲取事件,然后把事件加工后,發(fā)送給 InputClassfiermReader = createInputReader(readerPolicy, mClassifier);
}
在 InputManager 內(nèi)部創(chuàng)建了三個子模塊:InputReader、InputClassifier、InputDispatcher,其作用如下:
- InputReader:負(fù)責(zé)從 EventHub 中獲取事件,然后把事件加工后,發(fā)送給 InputClassfier。
- InputClassifer:負(fù)責(zé)把事件發(fā)送給 InputDispatcher,但是它會對觸摸事件進(jìn)行一個分類工作。
- InputDispatcher:對進(jìn)行事件分發(fā)。
此外,在上一小節(jié)的分析中,我們知道在構(gòu)建 InputManager 實例對象時使用了兩個 this 參數(shù),而 InputManager 構(gòu)造函數(shù)需要的兩個接口參數(shù)正是由 NativeInputManager 實現(xiàn)的,而具體使用這兩個接口的不是 InputManager 自身,而是它內(nèi)部的子模塊 InputDispatcher 和 InputReader。
InputDispatcher 和 InputReader 在構(gòu)建時都傳遞了 NativeInputManager 對象參數(shù),并賦值到各自的 mPolicy 變量,后續(xù)可直接通過 mPolicy 調(diào)用 Java 層 IMS 對象方法,因此 InputManager 向 Java 層通信的能力是由子模塊 InputDispatcher 和 InputReader 實現(xiàn)的。
接下來首先來看看 InputDispatcher 是如何通過 createInputDispatcher() 函數(shù)創(chuàng)建的,詳見接下來兩節(jié)的源碼分析。
1.6、InputDispatcher
xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
sp<InputDispatcherInterface> createInputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) {return new android::inputdispatcher::InputDispatcher(policy);
}
方法很簡單,內(nèi)部直接新建 InputDispatcher 對象,再繼續(xù)查看 InputDispatcher 的構(gòu)造函數(shù):
xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.h
class InputDispatcher : public android::InputDispatcherInterface {
protected:~InputDispatcher() override;public:explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
......
private:std::unique_ptr<InputThread> mThread;sp<InputDispatcherPolicyInterface> mPolicy;sp<Looper> mLooper;sp<InputReporterInterface> mReporter;
};
xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy): mPolicy(policy),mPendingEvent(nullptr),mLastDropReason(DropReason::NOT_DROPPED),mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),mAppSwitchSawKeyDown(false),mAppSwitchDueTime(LONG_LONG_MAX),mNextUnblockedEvent(nullptr),mDispatchEnabled(false),mDispatchFrozen(false),mInputFilterEnabled(false),// mInTouchMode will be initialized by the WindowManager to the default device config.// To avoid leaking stack in case that call never comes, and for tests,// initialize it here anyways.mInTouchMode(true),mMaximumObscuringOpacityForTouch(1.0f),mFocusedDisplayId(ADISPLAY_ID_DEFAULT),mFocusedWindowRequestedPointerCapture(false),mWindowTokenWithPointerCapture(nullptr),mLatencyAggregator(),mLatencyTracker(&mLatencyAggregator),mCompatService(getCompatService()) {mLooper = new Looper(false); // 新建自己的 Looper 對象mReporter = createInputReporter(); // 新建 InputReporter 對象mKeyRepeatState.lastKeyEntry = nullptr;policy->getDispatcherConfiguration(&mConfig);
}
在調(diào)用 InputDispatcher 的構(gòu)造函數(shù)構(gòu)建實例對象的同時將入?yún)?policy 賦值給 mPolicy 進(jìn)行保存 (這里入?yún)?policy 即是 NativeInputManager 對象)。其次新建自己的 Looper 對象,然后使用類似創(chuàng)建 InputDispatcher 的 createInputReporter() 函數(shù)新建 InputReporter 對象,代碼比較簡單,不再深入追蹤,感興趣的可自行查看。
1.7、InputReader
接著來看看 InputReader 是如何通過 createInputReader() 函數(shù)創(chuàng)建的,一起跟著源碼來學(xué)習(xí)。
xref: /frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener) {// 創(chuàng)建 EventHub 對象傳入到 InputReader 的構(gòu)造函數(shù)中來新建 InputReader 對象return new InputReader(std::make_unique<EventHub>(), policy, listener);
}
該方法里面,在新建 InputReader 對象時,結(jié)合 InputReader 類的構(gòu)造函數(shù)可知,第一個參數(shù)是 EventHub 的實例對象,那么 EventHub 對象是怎么創(chuàng)建的呢?
這里需要知道一些 C++ 有關(guān)的知識,std::make_unique 的語法如下:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);
std::make_unique:是 C++11 標(biāo)準(zhǔn)引入的一個模版函數(shù),用于動態(tài)分配指定類型的內(nèi)存,并返回一個指向分配內(nèi)存的唯一指針 (即 std::unique_ptr)。語法中,T 是指定的類型,Args 是可變長模板參數(shù)包,用于傳遞給指定類型的構(gòu)造函數(shù)的參數(shù)。在調(diào)用 std::make_unique 時,通過 Args 包傳入構(gòu)造函數(shù)的參數(shù)會被轉(zhuǎn)發(fā)給類型 T 的構(gòu)造函數(shù),以生成相應(yīng)的對象實例。該函數(shù)返回的指針是一個 std::unique_ptr 類型,表示一個擁有指向動態(tài)內(nèi)存的所有權(quán)的對象。
xref: /frameworks/native/services/inputflinger/reader/include/InputReader.h
class InputReader : public InputReaderInterface {
public:InputReader(std::shared_ptr<EventHubInterface> eventHub,const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener);virtual ~InputReader();
protected:// 在循環(huán)過程的每次迭代中,InputReader 讀取并處理一條來自 EventHub 的傳入消息void loopOnce();private:std::unique_ptr<InputThread> mThread;std::shared_ptr<EventHubInterface> mEventHub;sp<InputReaderPolicyInterface> mPolicy;sp<QueuedInputListener> mQueuedListener;
};
xref: /frameworks/native/services/inputflinger/reader/InputReader.cpp
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener): mContext(this),mEventHub(eventHub),mPolicy(policy),mGlobalMetaState(0),mLedMetaState(AMETA_NUM_LOCK_ON),mGeneration(1),mNextInputDeviceId(END_RESERVED_ID),mDisableVirtualKeysTimeout(LLONG_MIN),mNextTimeout(LLONG_MAX),mConfigurationChangesToRefresh(0) {mQueuedListener = new QueuedInputListener(listener);{ // acquire lockstd::scoped_lock _l(mLock);refreshConfigurationLocked(0);updateGlobalMetaStateLocked();} // release lock
}
與構(gòu)建 InputDispatcher 對象類似,在調(diào)用 InputReader 的構(gòu)造函數(shù)構(gòu)建實例對象的同時將入?yún)?policy 賦值給 mPolicy,eventHub 對象賦值給 mEventHub 保存,同時新建 QueuedInputListener 監(jiān)聽對象。
通過前一小節(jié)的分析可知,EventHub 實例對象是通過調(diào)用 std::make_unique() 函數(shù)來創(chuàng)建的,那接下來一起去看看 EventHub 具體都做了些什么?
1.8、EventHub
xref: /frameworks/native/services/inputflinger/reader/include/EventHub.h
class EventHub : public EventHubInterface {
public:EventHub();
private:int32_t mNextDeviceId;BitSet32 mControllerNumbers;std::unordered_map<int32_t, std::unique_ptr<Device>> mDevices;std::vector<std::unique_ptr<Device>> mOpeningDevices;std::vector<std::unique_ptr<Device>> mClosingDevices;int mEpollFd;int mINotifyFd;int mWakeReadPipeFd;int mWakeWritePipeFd;int mInputWd;int mVideoWd;// 一次最多可處理的信號fd的數(shù)量static const int EPOLL_MAX_EVENTS = 16;// 掛起的 epoll 事件數(shù)組和下一個要處理的事件的索引struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];size_t mPendingEventCount;size_t mPendingEventIndex;bool mPendingINotify;
};
xref: /frameworks/native/services/inputflinger/reader/EventHub.cpp
EventHub::EventHub(void): mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),mNextDeviceId(1),mControllerNumbers(),mOpeningDevices(nullptr),mClosingDevices(nullptr),mNeedToSendFinishedDeviceScan(false),mNeedToReopenDevices(false),mNeedToScanDevices(true),mPendingEventCount(0),mPendingEventIndex(0),mPendingINotify(false) {ensureProcessCanBlockSuspend();// 創(chuàng)建 Epoll 對象的描述符,監(jiān)聽設(shè)備節(jié)點是否有數(shù)據(jù)可讀(有無事件發(fā)生)mEpollFd = epoll_create1(EPOLL_CLOEXEC);LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));// 創(chuàng)建 INotify 對象,用于監(jiān)聽設(shè)備節(jié)點的路徑 /dev/input,是否有變化,如有設(shè)備增刪則對應(yīng)的設(shè)備節(jié)點的文件也會增刪mINotifyFd = inotify_init();// 添加 watch 監(jiān)聽存儲設(shè)備節(jié)點的路徑 DEVICE_PATH 的創(chuàng)建與刪除,當(dāng)有設(shè)備節(jié)點發(fā)生變化時,通過 INotify 對象可以讀取事件的詳細(xì)信息mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,strerror(errno));if (isV4lScanningEnabled()) {mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",VIDEO_DEVICE_PATH, strerror(errno));} else {mVideoWd = -1;ALOGI("Video device scanning disabled");}// 構(gòu)建 epoll_event 結(jié)構(gòu)體,并為每一個需要監(jiān)控的描述符填充該結(jié)構(gòu)體,以描述監(jiān)控事件struct epoll_event eventItem = {};eventItem.events = EPOLLIN | EPOLLWAKEUP; // 事件掩碼,指明需要監(jiān)聽的事件類型,可讀eventItem.data.fd = mINotifyFd; // 數(shù)據(jù)字段,設(shè)置需要監(jiān)聽的描述符,這里是 mINotifyFd,即監(jiān)聽設(shè)備節(jié)點的路徑// 調(diào)用 epoll_ctl() 函數(shù)將 INotify 對象注冊到 Epoll 中,監(jiān)聽其文件描述符對應(yīng)的文件夾下是否有設(shè)備節(jié)點的增刪信息// 第一個參數(shù)即前面創(chuàng)建的 Epoll 對象的描述符,第二個參數(shù)表示具體操作,這里 ADD 表示增加注冊事件// 第三個參數(shù)表示需要監(jiān)聽的描述符,第四個參數(shù)是描述監(jiān)聽事件的詳細(xì)信息的 epoll_event 結(jié)構(gòu)體int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, & eventItem);LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);// 創(chuàng)建匿名管道 wakeFds,并將讀端交給 Epoll,寫端交給 InputReader,用于喚醒 Epoll,避免其阻塞在 epoll_wait()int wakeFds[ 2];result = pipe(wakeFds);LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);// mWakeReaderFD 和 mWakeWriterFD 對應(yīng)管道的兩端mWakeReadPipeFd = wakeFds[0];mWakeWritePipeFd = wakeFds[1];result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", errno);result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", errno);eventItem.data.fd = mWakeReadPipeFd;// epoll_ctl() 函數(shù)可重復(fù)調(diào)用,將多個文件描述符的多種事件監(jiān)聽注冊到 Epoll 對象中// 將匿名管道的讀取端的描述符也注冊到 Epoll 中,用于監(jiān)聽讀取端的可讀事件,當(dāng)寫入端有數(shù)據(jù)寫入時// 管道的讀取端就有數(shù)據(jù)可讀,使得 epoll_wait() 得以返回,從而達(dá)到喚醒 InputReader 線程的目的,避免其一直阻塞result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", errno);
}
EventHub 的構(gòu)造函數(shù)主要工作有:
- 新建并初始化 Epoll、INotify 對象等;
- 調(diào)用 inotify_add_watch 函數(shù),監(jiān)聽 “/dev/input” 目錄下的設(shè)備節(jié)點創(chuàng)建與刪除操作,然后通過 read() 函數(shù)讀取事件;
- 將 INotify 添加到 Epoll 中,作為一個監(jiān)控對象;
- 創(chuàng)建管道,將管道讀取端的可讀事件添加到 Epoll 中,使 epoll_wait() 函數(shù)返回,喚醒 InputReader 線程來處理事件。
至此,IMS 在 Java 層和 native 層的實例對象都已創(chuàng)建完成,并且在這個過程中,輸入系統(tǒng)的重要參與者也均創(chuàng)建完成。
1.9、小結(jié)
Java 層的 IMS 的主要工作是為 ReaderPolicy 與 DispatcherPolicy 提供實現(xiàn),以及與 Android 其他系統(tǒng)服務(wù)進(jìn)行協(xié)作,其中最主要的協(xié)作者是 WMS。
NativeInputManager 位于 IMS 的 JNI 層,負(fù)責(zé) Native 層的組件與 Java 層的 IMS 之間的相互通信。同時為 InputReader 及 InputDispatcher 提供了策略請求的接口。策略請求被他轉(zhuǎn)發(fā)給 Java 層的 IMS,由 IMS 進(jìn)行最終的決策定奪。
InputManager 是 InputReader 與 InputDispatcher 的運(yùn)行容器,在啟動 InputReader 與 InputDispatcher 時,分別新建自己的運(yùn)行線程 InputThreadImpl 并啟動運(yùn)行。
2、IMS 啟動
在上一節(jié)的 SystemServer # startOtherServices() 方法中,在構(gòu)建完 IMS 后,IMS 系統(tǒng)中的各個重要參與者仍處于待命狀態(tài),需調(diào)用 IMS # start() 函數(shù)來啟動 IMS,繼續(xù)追蹤源碼分析:
2.1、IMS # start()
xref: /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stubimplements Watchdog.Monitor {......private static native void nativeStart(long ptr);......public void start() {Slog.i(TAG, "Starting input manager");// 啟動 native 層的 IMSnativeStart(mPtr);Watchdog.getInstance().addMonitor(this);// 監(jiān)聽Settings.System.POINTER_SPEED,這個表示手指的速度registerPointerSpeedSettingObserver();// 監(jiān)聽Settings.System.SHOW_TOUCHES,這個表示是否在屏幕上顯示觸摸坐標(biāo)registerShowTouchesSettingObserver();registerAccessibilityLargePointerSettingObserver();registerLongPressTimeoutObserver();registerMaximumObscuringOpacityForTouchSettingObserver();registerBlockUntrustedTouchesModeSettingObserver();// 監(jiān)聽用戶切換mContext.registerReceiver(new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {updatePointerSpeedFromSettings();updateShowTouchesFromSettings();updateAccessibilityLargePointerFromSettings();updateDeepPressStatusFromSettings("user switched");}}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);// 從數(shù)據(jù)庫獲取值,并傳遞給 native 層updatePointerSpeedFromSettings();updateShowTouchesFromSettings();updateAccessibilityLargePointerFromSettings();updateDeepPressStatusFromSettings("just booted");updateMaximumObscuringOpacityForTouchFromSettings();updateBlockUntrustedTouchesModeFromSettings();}......
}
IMS 的啟動過程如下:
- 啟動 native 層輸入系統(tǒng),其實就是啟動剛剛說到的 InputReader 和 InputDispatcher。
- 注冊監(jiān)聽廣播,因為這些廣播與輸入系統(tǒng)的配置有關(guān),當(dāng)接收到這些廣播,會更新配置到 native 層。
- 直接讀取配置,更新到 native 層輸入系統(tǒng)。
2.2、NativeInputManager # nativeStart()
xref: /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static const JNINativeMethod gInputManagerMethods[] = { // JNI 注冊的映射關(guān)系/* name, signature, funcPtr */{"nativeStart", "(J)V", (void*)nativeStart},
};static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {// 將 Java 層保存的 NativeInputManager 對象的指針轉(zhuǎn)換成 NativeInputManager 對象NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);// 查看 1.4 NativeInputManager 的源碼可知,獲取到 InputManager 對象,然后調(diào)用其 start() 函數(shù)status_t result = im->getInputManager()->start();if (result) {jniThrowRuntimeException(env, "Input manager could not be started.");}
}
首先將 Java 層保存 NativeInputManager 對象的指針 mPtr 轉(zhuǎn)換成 NativeInputManager 對象,然后調(diào)用 NativeInputManager # getInputManager() 函數(shù)獲取到 InputManager 對象,接著調(diào)用 InputManager # start() 函數(shù)繼續(xù)啟動流程。
reinterpret_cast 的功能可以分為兩類:1、指針和整數(shù)之間的轉(zhuǎn)換;2、不同類型的指針/成員指針/引用之間的轉(zhuǎn)換。
2.3、InputManager # start()
xref: /frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() {// 啟動承載 InputDispatcher 的線程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 的啟動過程很簡單,調(diào)用 InputDispatcher 和 InputReader 的 start() 函數(shù),啟動承載它們運(yùn)行的線程,來看一下它們是如何啟動線程的。
2.4、InputDispatcher # start()
xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
status_t InputDispatcher::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });return OK;
}
在方法內(nèi),首先判斷 mThread 是否已存在,存在則直接返回,不存在則通過 std::make_unique 函數(shù)來構(gòu)建 InputThread 的實例對象,但沒有看到啟動線程的代碼邏輯,帶著這個疑問,我們再去看看 InputReader 的 start() 方法。
2.5、InputReader # start()
xref: /frameworks/native/services/inputflinger/reader/InputReader.cpp
status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;
}
方法的代碼邏輯跟 InputDispatcher 的差不多,也是沒有看到啟動線程的代碼邏輯,既然都是新建 InputThread 對象,那就具體來看一下 InputThread 類。
2.6、InputThread
xref: /frameworks/native/services/inputflinger/include/InputThread.h
class InputThread {
public:explicit InputThread(std::string name, std::function<void()> loop,std::function<void()> wake = nullptr);virtual ~InputThread();bool isCallingThread();
private:std::string mName; // 線程名std::function<void()> mThreadWake;sp<Thread> mThread; // 承載 InputDispatcher\InputReader 運(yùn)行的線程
};
xref: /frameworks/native/services/inputflinger/InputThread.cpp
class InputThreadImpl : public Thread {
public: // explicit 關(guān)鍵字的作用就是防止類構(gòu)造函數(shù)的隱式自動轉(zhuǎn)換,且只對有一個參數(shù)的類構(gòu)造函數(shù)有效explicit InputThreadImpl(std::function<void()> loop): Thread(/* canCallJava */ true), mThreadLoop(loop) {}~InputThreadImpl() {}private:std::function<void()> mThreadLoop; // 存儲一個可調(diào)用對象,這里指的是 lambda 表達(dá)式bool threadLoop() override {mThreadLoop();return true;}
};InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake): mName(name), mThreadWake(wake) {// 使用封裝的可調(diào)用對象 loop 新建 InputThreadImpl 對象mThread = new InputThreadImpl(loop);// 啟動 InputThreadImpl 線程mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}
std::function:是一個通用的函數(shù)封裝類,它可以存儲、復(fù)制和調(diào)用任意可調(diào)用對象,包括函數(shù)指針、函數(shù)對象、成員函數(shù)指針和lambda 表達(dá)式等。通過使用 std::function 作為函數(shù)參數(shù),我們可以實現(xiàn)更加靈活的函數(shù)調(diào)用方式,提高代碼的可讀性和可維護(hù)性。
由代碼可知 InputThread 類其本身不是一個線程,其內(nèi)部是 InputThreadImpl 類來實現(xiàn)線程的具體功能。使用封裝的可調(diào)用對象 loop 構(gòu)建 InputThreadImpl 對象,然后調(diào)用其 run() 函數(shù)來啟動線程。
InputThreadImpl 類繼承自 Thread 類,而 C++ 的 Thread 類提供了一個名為 threadLoop() 的純虛函數(shù),當(dāng)線程開始運(yùn)行后,將會在內(nèi)建的線程循環(huán)中不斷地調(diào)用 threadLoop() 函數(shù),直到此函數(shù)返回 false,則退出線程循環(huán)結(jié)束線程。但從 InputThreadImpl 類的定義可以看出,threadLoop() 函數(shù)會一直保持循環(huán)(返回值始終為 true),并且每一次循環(huán),會調(diào)用一次 mThreadLoop() 函數(shù),而 mThreadLoop() 函數(shù)是由 InputDispacher 和 InputReader 在啟動時封裝好傳入的可調(diào)用對象。
到這里,終于搞明白了,在 InputDispatcher 啟動時,會創(chuàng)建一個線程,然后循環(huán)調(diào)用 dispatchOnce() 函數(shù),同樣 InputReader 啟動時,也會創(chuàng)建一個線程,然后循環(huán)調(diào)用 loopOnce() 函數(shù)。
3、IMS 系統(tǒng)就緒
上面兩節(jié)已完成 IMS 及其重要參與者的構(gòu)建,并啟動了 IMS 系統(tǒng),接下來就是通知系統(tǒng) IMS 系統(tǒng)已完成啟動并準(zhǔn)備就緒,具體看一下源碼
xref: /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stubimplements Watchdog.Monitor {......// IMS 系統(tǒng)內(nèi)部的 Handler,用來處理鍵盤等輸入設(shè)備有關(guān)的消息private final InputManagerHandler mHandler;private WiredAccessoryCallbacks mWiredAccessoryCallbacks; // 有線連接的設(shè)備回調(diào)private boolean mSystemReady; // 標(biāo)志系統(tǒng)是否準(zhǔn)備完畢private NotificationManager mNotificationManager; // 通知管理......public void systemRunning() {......mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);synchronized (mLidSwitchLock) {mSystemReady = true;......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);}}// 監(jiān)聽廣播,通知 native 層加載鍵盤布局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() {@Overridepublic void onReceive(Context context, Intent intent) {// 其內(nèi)部繼續(xù)調(diào)用 reloadKeyboardLayouts() 函數(shù)updateKeyboardLayouts();}}, filter, null, mHandler);// 監(jiān)聽廣播,通知 native 層加載設(shè)備別名filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);mContext.registerReceiver(new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {reloadDeviceAliases();}}, filter, null, mHandler);// 通過 InputManagerHandler 發(fā)送消息來通知 native 層加載鍵盤布局和加載設(shè)備別名mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);// 如果與系統(tǒng)連接的有線設(shè)備的回調(diào)不為空,則須回調(diào)通知其輸入系統(tǒng) IMS 已準(zhǔn)備完畢if (mWiredAccessoryCallbacks != null) { mWiredAccessoryCallbacks.systemReady();}}private void reloadKeyboardLayouts() {if (DEBUG) {Slog.d(TAG, "Reloading keyboard layouts.");}// 調(diào)用 native 層函數(shù)來加載鍵盤布局nativeReloadKeyboardLayouts(mPtr);}private void reloadDeviceAliases() {if (DEBUG) {Slog.d(TAG, "Reloading device names.");}// 調(diào)用 native 層函數(shù)來加載設(shè)備別名nativeReloadDeviceAliases(mPtr);}......
}
注冊監(jiān)聽廣播,通知 native 層加載鍵盤布局、設(shè)備別名,最后通過 JNI 調(diào)用 native 層的函數(shù)來加載鍵盤布局、設(shè)備別名。此外,如果與系統(tǒng)連接的有線設(shè)備注冊的回調(diào)不為空,則需回調(diào)通知其輸入系統(tǒng) IMS 已準(zhǔn)備就緒。
三、總結(jié)
1、IMS 啟動時序圖
2、IMS 成員關(guān)系圖
最后結(jié)合 IMS 的啟動時序圖和成員關(guān)系圖可以更深刻的理解 IMS 系統(tǒng)的構(gòu)成與啟動過程,下一篇文章來繼續(xù)探索輸入系統(tǒng)的重要組成成員,等待后續(xù)吧!
參考
- 深入理解Android:卷III