該系列文章總綱連結:專題分綱目錄 Android Framework 輸入子系統
本章關鍵點總結 & 說明:
這裡思維導圖主要關注➕派發流程 以及啟動流程的按鍵視角。因為圖比較大,是以這裡單獨把新增的部分單獨截圖,IMS的架構部分新增:
這裡新增加了 按鍵視角部分,以按鍵的流程來解讀整個IMS的資料流走向的架構。新增的派發資料流程截圖如下:
這裡整個InputSispatcher的分析流程就如上思維導圖所示。
1 Input子系統架構
這裡 步驟1和2的目的是獲得事件,步驟3和4的目的是稍加處理,步驟5 目的是為了發送給APP。接下來用一張架構圖來表示InputDispatcher的整體架構,如下所示:
以上架構圖以圖形的方式表達了Input輸入子系統中關鍵的五步,如下所示:
- 在放入隊列前先稍加處理,主要針對System Key、Global Key、User Key事件,以及處理緊急事件(比如來電靜音等)。
- 把InputReader線程中事件放入mInboundQueue中即可。
- 對于Global Key、Sysytem Key按鍵處理,放入mCommandQueue,依次處理。
- 對于User Key,放入隊列,查找目标APP,并放入它的outBoundQueue。
- 從OutboundQueue取出 事件,通過connection發送給APP。
對于InputDispatcher線程,更關注3 、4、5步。
2 Dispatcher對按鍵的處理流程關鍵點解析
2.1 通用模型的處理流程
回顧之前key的處理流程,關鍵點在于這段代碼:
//InputReader的分析:KeyboardInputMapper::processKey中資料最後同步到dispatcher中流程:
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是InputReader初始化時傳入的對象,即InputDispatcher
getListener()->notifyKey(&args);
這裡繼續分析notifyKey中的關鍵點,如下所示:
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
//...
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
//...
needWake = enqueueInboundEventLocked(newEntry);
//...
if (needWake) {
//喚醒等待的InputDispatcher,進行輸入事件分發。
mLooper->wake();
}
}
這裡關注➕三個主要步驟:
- interceptKeyBeforeQueueing函數,調用後對應PhoneWindowManager的同名函數處理,根據傳回值設定PolicyFlags
- enqueueInboundEventLocked函數,将newEntry放入到mInBoundQueue隊列
- mLooper->wake();根據需要喚醒Dispatcher線程
2.2 案例簡要說明
@1 針對Global Key的處理模式
這裡以按鍵AKEY_CODE_TV為例,對于Global Key,直接傳回ACTION_PASS_TO_USER。
@2 針對System Key的處理模式
這裡以按鍵AKEY_CODE_VOLUME_DOWN為例,對于System Key,如果可以直接處理則設定傳回值~ACTION_PASS_TO_USER,否則設定ACTION_PASS_TO_USER。
@3 針對User Key的處理模式
這裡以按鍵AKEY_CODE_A為例,對于普通按鍵則直接傳回ACTION_PASS_TO_USER。
3 接下來是對DispatcherThread的分析,實際流程如下:
這裡從InputManager的start函數,延續之前 章節的分析,代碼如下:
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
//...
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
//...
return OK;
}
InputManager啟動了一個InputReaderThread和InputDispatcherThread來讀取和分發輸入消息,調用它們的run方法後,會進入threadLoop函數(隻要threadLoop函數傳回true,該函數就會循環執行)。是以接下來直接分析InputDispatcherThread的threadLoop函數,代碼實作如下:
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
3.1 dispatchOnce分析
其中 dispatchOnce代碼實作如下:
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();
/* 指令隊列為空時
@a 從mInboundQueue中取出事件
@b 用它來生成指令放入指令隊列 or 直接丢棄
@d 對于經過處理的事件:
Global Key:丢棄
System Key:丢棄
User Key:找到target,執行dispatch*/
if (!haveCommandsLocked()) {//判斷CommandQueue是否有指令
dispatchOnceInnerLocked(&nextWakeupTime);//關鍵點
}
/*@c 當指令隊列有資料時,執行指令:
Global Key:發廣播
System Key:直接處理
User Key:不處理*/
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
@1 這裡關注runCommandsLockedInterruptible函數,實作如下:
bool InputDispatcher::runCommandsLockedInterruptible() {
if (mCommandQueue.isEmpty()) {
return false;
}
do {
CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
Command command = commandEntry->command;
(this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
commandEntry->connection.clear();
delete commandEntry;
} while (! mCommandQueue.isEmpty());
return true;
}
以上是将 指令 放入指令隊列裡的過程,後面将會繼續執行指令。
@2 一次輸入事件分發,當沒有事件輸入消息時會走到mLooper->pollOnce(timeoutMillis);這個函數會進入睡眠狀态。
當有按鍵消息發生時該函數會傳回,然後走到dispatchOnceInnerLocked函數。代碼實作如下:
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
//...
// If we don't already have a pending event, go grab one.
if (! mPendingEvent) {
//當InputReader隊列中插入一個輸入事件後,此處mInboundQueue不為空
if (mInboundQueue.isEmpty()) {
...
} else {
// Inbound queue has at least one entry.
mPendingEvent = mInboundQueue.dequeueAtHead();
//...
}
//...
}
//...
switch (mPendingEvent->type) {
//...
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
//...
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
//...
}
//...
}
上面是以按鍵為例,是以派發按鍵事件時僅關注 按鍵部分,繼續分析dispatchKeyLocked,之前在InputReaderThread線程中調用enqueueInboundEventLocked函數,将EventEntry加入到mInboundQueue中,然後調用mLooper->wake函數會喚醒InputDispatcherThread線程,InputDispatcher中把隊列的第一個事件取出來,因為這裡是鍵盤事件,是以mPendingEvent->type是EventEntry::TYPE_KEY,然後調用dispatchKeyLocked函數。
3.2 按鍵案例
選擇 dispatchKeyLocked繼續分析,代碼實作如下:
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
//...
//如果按鍵是第一次分發,則将指令封裝為CommandEntry加入隊列
//執行doInterceptKeyBeforeDispatchingLockedInterruptible,以給java層攔截按鍵的機會
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
if (mFocusedWindowHandle != NULL) {
commandEntry->inputWindowHandle = mFocusedWindowHandle;
}
commandEntry->keyEntry = entry;
entry->refCount += 1;
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
}
} else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
if (*dropReason == DROP_REASON_NOT_DROPPED) {
*dropReason = DROP_REASON_POLICY;
}
}
//...
// Identify targets.
Vector<InputTarget> inputTargets;
//找到目前激活的Window視窗,并将其加入到Vendor中
int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
setInjectionResultLocked(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
return true;
}
//找到需要監聽按鍵的InputChannel,封裝成InputTarget,加入到Vendor中
addMonitoringTargetsLocked(inputTargets);
//将按鍵分發到上面Vendor的InputChannel中
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
@1 這裡關注doInterceptKeyBeforeDispatchingLockedInterruptible的實作,代碼如下:
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
KeyEntry* entry = commandEntry->keyEntry;
KeyEvent event;
initializeKeyEvent(&event, entry);
mLock.unlock();
nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
&event, entry->policyFlags);
mLock.lock();
if (delay < 0) {
//不會上傳給APP,如果是system Key則直接處理,傳回-1
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
} else if (!delay) {
//會上傳給APP,讓APP處理,傳回0
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
} else {
//杜宇User Key 會再次執行前面的dispatcherOnceInnerLocked
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
entry->interceptKeyWakeupTime = now() + delay;
}
entry->release();
}
這裡調用interceptKeyBeforeDispatching,會調用到PhoneWindowManager.java,如果這裡是針對Global Key,則代碼如下:
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
final boolean keyguardOn = keyguardOn();
final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
final int metaState = event.getMetaState();
//...
//根據Global Key.xml發送廣播給元件
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
return -1;
}
//...
}
@2 同時 這裡繼續分析dispatchEventLocked,代碼實作如下:
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
pokeUserActivityLocked(eventEntry);
for (size_t i = 0; i < inputTargets.size(); i++) {
const InputTarget& inputTarget = inputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
}
}
這裡會依次取出Vector中的InputTarget,根據InputTarget的InputChannel找到儲存在mConnectionByFd中的Connection對象,
調用prepareDispatchCycleLocked函數進行分發。prepareDispatchCycleLocked代碼實作如下:
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
//...各種檢查
//這裡将connection分裝成DispatchEntry,加入到connection->outboundQueue的隊列中
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
enqueueDispatchEntriesLocked的代碼實作如下:
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
startDispatchCycleLocked(currentTime, connection);
}
}
這個函數首先擷取以前的connection的outboundQueue是否為空,然後調用enqueueDispatchEntryLocked将事件加入到outboundQueue中。
- 如果以前為空,現在不為空,則調用startDispatchCycleLocked開始分發。
- 如果以前不為空,說明目前的Activity正在處理前面的按鍵,則不需要再調用startDispatchCycleLocked。
這裡 startDispatchCycleLocked的代碼實作如下:
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
while (connection->status == Connection::STATUS_NORMAL
&& !connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.head;
dispatchEntry->deliveryTime = currentTime;
// Publish the event.
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
keyEntry->deviceId, keyEntry->source,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);
break;
}
//...
}
//... Check the result.
// Re-enqueue the event on the wait queue.
connection->outboundQueue.dequeue(dispatchEntry);
traceOutboundQueueLengthLocked(connection);
connection->waitQueue.enqueueAtTail(dispatchEntry);
traceWaitQueueLengthLocked(connection);
}
}
從outboundQueue中取出需要處理的事件,交給connection的inputPublisher去分發,将事件加入到connection的waitQueue中。同時這裡 分發事件是通過InputPublisher的publishKeyEvent來完成,代碼如下:
status_t InputPublisher::publishKeyEvent(
uint32_t seq,
//...
nsecs_t downTime,
nsecs_t eventTime) {
//...
InputMessage msg;
msg.header.type = InputMessage::TYPE_KEY;
//...
msg.body.key.eventTime = eventTime;
return mChannel->sendMessage(&msg);
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
size_t msgLength = msg->size();
ssize_t nWrite;
do {
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
//...
if (size_t(nWrite) != msgLength) {
return DEAD_OBJECT;
}
return OK;
}
- 查找目标:向WindowManagerService查詢目前window,獲得對應的connection
- 把事件放入到connection的outboundQueue隊列當中
- 從隊列中逐個把事件寫入fd,取出并構造InputMessage最後發送。