按鍵處理設計的總體思路是驅動層會有一個消息隊列來存放事件,會有一個Reader來不停的讀取事件,一個Dispatcher來分發消息隊列中的事件。Dispatcher分發的事件最後會經過jni上報到InputManagerService,而後經過接口最後傳遞給PhoneWindow,這裏再根據不一樣的按鍵事件類型來作不一樣的處理。java
當系統開機以後SystemServer會啓動InputManagerService,在SystemServer.java中的startOtherServices()方法中啓動:android
Slog.i(TAG, "Input Manager");
inputManager = new InputManagerService(context);
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
mActivityManagerService.setWindowManager(wm);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
複製代碼
看一下InputManagerService的構造方法:c++
public InputManagerService(Context context) {
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
複製代碼
主要是經過JNI的方式調用nativeInit方法傳入一個消息隊列做爲參數,nativeInit對應是com_android_server_input_InputManagerService.cpp中的nativeInit。shell
static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
複製代碼
這裏又new了一個NativeInputManager的對象,構造的參數主要仍是這個消息隊列的Looper。繼續看一下NativeInputManager的構造函數,仍是在com_android_server_input_InputManagerService.cpp中。bash
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
mContextObj = env->NewGlobalRef(contextObj);
mServiceObj = env->NewGlobalRef(serviceObj);
{
AutoMutex _l(mLock);
mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
}
mInteractive = true;
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
複製代碼
這裏主要就是new出了InputManager的實例,最後再看一下構造函數和initialize方法,代碼位於frameworks/native/services/inputflinger/InputManager.cpp中。app
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
InputManager::InputManager(
const sp<InputReaderInterface>& reader,
const sp<InputDispatcherInterface>& dispatcher) :
mReader(reader),
mDispatcher(dispatcher) {
initialize();
}
InputManager::~InputManager() {
stop();
}
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
複製代碼
在initialize中初始化了mReader和mDispatcher,以及兩個相關的Thread。至此所需的關鍵對象都已經建立準備好,可是線程並未run起來。ide
在SystemServer中啓動InputManagerService以後,調用了start方法:函數
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
複製代碼
在InputManagerService中的start方法中又調用了nativeStart方法,這個方法也是本地方法,具體實如今com_android_server_input_InputManagerService.cpp中。oop
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.");
}
}
複製代碼
接下來又調用了InputManager.cpp的start方法:ui
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
}
return OK;
}
複製代碼
在這裏將DispatcherThread和ReaderThread運行起來。
一張圖總結下:
首先由InputReaderThread等待按鍵消息到來,該thread在threadLoop中無盡的調用InputReader的loopOnce方法,代碼位於frameworks/native/services/inputflinger/InputReader.cpp:
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
複製代碼
在loopOnce方法中會經過EventHub來獲取事件放入buffer中:
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
複製代碼
frameworks/native/services/inputflinger/EventHub.cpp中的getEvents方法一部分:
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;
複製代碼
InputReader從設備文件(adb shell getevent就是讀的這個文件)中讀取的是RawEvent,在交給InputDispatcher進行分發以前,它須要先把RawEvent進行轉化分類,拆分紅KeyEvent、MotionEvent、TrackEvent各類類型等。如需瞭解拆分過程能夠參照文章 Android輸入事件流程中的EventHub分析及源碼演示 來深刻理解EventHub.cpp中的拆分過程。
在getEvents方法的最後來將隊列中事件刷給監聽器,監聽器實際上就是InputDispatcher事件分發器。
// Flush queued events out to the listener. This must happen outside of the lock because the listener could potentially call back into the InputReader's methods, such as getScanCodeState, or become blocked on another thread similarly waiting to acquire the InputReader lock thereby resulting in a deadlock. This situation is actually quite plausible because the listener is actually the input dispatcher, which calls into the window manager, which occasionally calls into the input reader. mQueuedListener->flush(); 複製代碼
而後會調用到frameworks/native/services/inputflinger/InputDispatcher.cpp的notifyKey方法。
KeyEvent event;
event.initialize(args->deviceId, args->source, args->action,
flags, keyCode, args->scanCode, metaState, 0,
args->downTime, args->eventTime);
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
複製代碼
在notifyKey方法中經過InputDispatcherPolicyInterface接口來調用到NativeInputManager的interceptKeyBeforeQueueing方法通知是否須要在入隊前對事件進行處理,而後接着調用InputDispatcher的enqueueInboundEventLocked方法將事件放入到隊尾中。
KeyEntry* newEntry = new KeyEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, flags, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);
needWake = enqueueInboundEventLocked(newEntry);
複製代碼
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
traceInboundQueueLengthLocked();
switch (entry->type) {
複製代碼
以上流程是InputReader獲取到設備事件通知分發器並存放到事件隊列中。
下面將介紹InputDispatcher如何從事件隊列中讀取事件並分發出去。首先在InputDispatcherThread的threadLoop中無盡的調用dispatchOnce方法,該方法兩個功能:一、調用dispatchOnceInnerLocked分發事件;二、調用runCommandsLockedInterruptible來處理CommandQueue中的命令,出隊並處理,直到隊列爲空。
下面具體介紹事件的分發,若是當前沒有掛起的命令即CommandQueue爲空,則調用dispatchOnceInnerLocked方法來分發事件,這裏也是android系統出現事件響應ANR的地方,在以前掛起的事件命令過多時就會致使新的事件沒法分發致使ANR,在dispatchOnceInnerLocked中成功分發後會去調用resetANRTimeoutsLocked()來重置ANR的時間。
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();
// Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
// Run all pending commands if there are any. // If any commands were run then force the next poll to wake up immediately. if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
複製代碼
在dispatchOnceInnerLocked中會處理多種類型的事件,這裏關注按鍵類型的(其餘如觸摸,設備重置等事件流程稍有區別)。若是Event類型爲KEY,最後調用dispatchKeyLocked,再通過doInterceptKeyBeforeDispatchingLockedInterruptible這個方法以後就調用到了InputDispatcherPolicyInterface接口的interceptKeyBeforeDispatching方法。到這裏就又很熟悉了,由NativeInputManager實現這個接口,而後又經過jni的方式調用到PhoneWindowManager的interceptKeyBeforeDispatching將按鍵事件分發傳遞給了java層。
case EventEntry::TYPE_KEY: {// 若是事件爲按鍵類型 KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEventLocked(typedEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DROP_REASON_NOT_DROPPED) {
dropReason = DROP_REASON_APP_SWITCH;
}
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
複製代碼
事件的處理包括了兩條主線:
這一部分介紹了事件上報和處理機制,首先是準備工做,SystemServer啓動InputManagerService,而後依次建立了NativeInputManager、InputManager、InputReader、InputDispatcher這幾個關鍵的對象以及InputReaderThread和InputDispatcherThread這兩個關鍵線程。而後讓這個兩個thread啓動起來,在InputReaderThread無限循環運行時,經過InputReader從EventHub中不斷讀取events而後通知InputDispatcher將事件入隊。而InputDispatcherThread則經過InputDispatcher不停的將隊列中的事件分發出去,這就是整個input系統的基本機制。