InputサブシステムフレームワークのInputDispatcher
126310 ワード
元のリンク:http://gityuan.com/2016/12/17/input-dispatcher/
一.InputDispatcher始点
前編ではシステムのInputReaderスレッドを入力し,InputReaderがEventHubを利用してデータを取得した後にEventEntryイベントを生成し,InputDispatcherのmInboundQueueに参加し,InputDispatcherスレッドを起動することを紹介した.ここではInputDispatcherを紹介し,同様にthreadLoopを起点として解析を開始する.
1.1 threadLoop
まず、InputDispatcherオブジェクトの初期化手順を確認します.
このメソッドの主な作業:
自分のスレッドに属するLooperオブジェクトを作成します.タイムアウトパラメータはIMSから来ており、パラメータのデフォルト値はkeyRepeatTimeout=500、keyRepeatDelay=50である.
イベントを配信するためにInputDispatcherのdispatchOnce()を繰り返し呼び出します.
1.2 dispatchOnce
スレッドはLooper->pollOnceを実行し、epoll_に入ります.wait待機状態は、次のいずれかの場合に待機状態を終了します.
callback:コールバック方法で起動します.timeout:nextWakeupTimeに到着し、タイムアウトが発生しました.wake:Looperのwake()メソッドをアクティブに呼び出す;
二.InputDispatcher
2.1 dispatchOnceInnerLocked
enqueueInboundEventLocked()中にmAppSwitchDueTimeがeventTimeに500 ms加算されるように設定されています.
このメソッドの主な機能:
mDispatchFrozenは、イベント配信作業を凍結するかどうかを決定するために使用されます.イベント配信の時点がイベントにmInboundQueueが加わるまでの時間が500 msを超える場合、app切替は期限切れ、すなわちisAppSwitchDue=trueとなる.mInboundQueueが空でない場合、頭部のイベントを取り出し、mPendingEvent変数を入れる.ANR時間をリセットします.ボタン呼び出しdispatchKeyLocked配布イベントなど、EventEntryのtypeタイプに応じてそれぞれ処理される.配布結果に基づいてdoneに入るかどうかを決定する.実行完了(done)の処理:dropReason(デフォルトNOT_DROPPEDは処理しない)に基づいてイベントを失うかどうかを決定する.dropInboundEventLockedは現在処理中のイベント(すなわちmPendingEvent)を解放する.releasePendingEventLocked dispatchKeyLocked配布イベントについて、
doneオーバーライドは実行されません:現在のEvent時間は起動時間より小さいです.policyにブロック操作を実行する機会を与える.findFocusedWindowTargetsLockedメソッドを呼び出すと、INPUT_が返されます.EVENT_INJECTION_PENDING、すなわちtargetsはReady状態にない.doneが実行される場合:このイベントは破棄する必要があります.つまりdropReason!=DROP_REASON_NOT_DROPPED; findFocusedWindowTargetsLockedの返却結果はINPUT_ではありませんEVENT_INJECTION_PENDING(処理中のイベントはありません);次にボタンを例として説明を展開すると、[小節2.2]dispatchKeyLockedに入る.
2.1.1 resetANRTimeoutsLocked
2.1.2 dropInboundEventLocked
2.2 dispatchKeyLocked
次のシーンでは、イベントを配布できない場合があります.
現在の時間が起動時間(nextWakeupTime)より小さい場合.policyが事前にイベントをブロックする必要がある場合.dropイベントが必要な場合.フォーカスウィンドウが失敗した場合を探します.上記のすべての状況をスキップすると、イベントの配布を実行するプロセスに入ります.
2.3 findFocusedWindowTargetsLocked
ここでmFocusedWindowHandleはどこで値を付けますか?インパルスでsetInput Windows()メソッドは、次のInputシステム-UIスレッドを参照してください.
フォーカスウィンドウの検索に失敗した場合:
ウィンドウなし、アプリケーションなし:Dropping event because there is no focused window or focused application.(ANRの場合、handleTargetsNotReadyLockedを呼び出す機会がないため)ウィンドウがなく、アプリケーションがある:Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.また、checkWindowReadyForMoreInputLockedのプロセスを参照してください.
2.3.1 checkWindowReadyForMoreInputLocked
2.3.2 handleTargetsNotReadyLocked
ANRタイムアウトポイントはmInputTargetWaitTimeoutTimeであり、この値はcurrentTime+5 sに等しく、ここでcurrentTimeはdispatchOnceInnerLockedメソッドを実行する起点を指す.ここでmInputTargetWaitCauseがINPUT_に等しいことを設定します.TARGET_WAIT_CAUSE_APPLICATION_NOT_READY(アプリケーションの準備ができていない)は、前のresetANRTimeoutsLocked()プロセスが待機理由をリセットする唯一の場所です.
ANR時間区間は、handleTargetsNotReadyLocked()メソッドが次回実行されるまで、dispatchOnceInnerLockedメソッド体の開始点であり、ANRがトリガされるか否かを決定するために準備されていない時間帯である.
現在のイベントdispatch中にf i n d FocusedWindowTargetsLocked()メソッドを実行し、resetANRTimeoutsLocked()を次の実行までの時間間隔.
handleTargetsNotReadyLocked()の判断プロセス:
アプリケーションHandleとwindowHandleが同時に空で、システムの準備が整っている場合に待機理由INPUT_を設定します.TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; タイムアウト待ち時間を無限大に設定します.TimeoutExpired=falseクリア待ちキューを設定します.アプリケーションHandleとwindowHandleの少なくとも1つが空でなく、アプリケーションの準備が整っている場合:待機理由INPUT_を設定します.TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; タイムアウト待ち時間を5 sに設定します.TimeoutExpired=falseクリア待ちキューを設定します.[セクション2.3]f i n d FocusedWindowTargetsLockedに戻り、ANRが発生しない場合、addWindowTargetLocked()はイベントをinputTargetsに追加します.
2.3.3 addWindowTargetLocked
現在のフォーカスウィンドウmFocusedWindowHandleのinputChannelをinputTargetsに渡します.
2.4 dispatchEventLocked
この方法の主な機能はeventEntryをターゲットinputTargetsに送信することである.
ここでpokeUserActivity Locked(eventEntry)メソッドは最終的にJava層のPowerManagerServicesに呼び出される.JAvaにおけるuserActivity FromNative()メソッド.これもPMSにおける唯一のnative callメソッドである.
2.4.1 pokeUserActivityLocked
2.4.2 postCommandLocked
2.4.3 getConnectionIndexLocked
inputChannelのfdに基づいてmConnectionsByFdキューからターゲットconnectionを問い合わせる.
2.5 prepareDispatchCycleLocked
接続ステータスが正しくない場合は、直接戻ります.
2.6 enqueueDispatchEntriesLocked
このメソッドの主な機能:
DispatchEntryイベントのキューへの参加は、dispatchModeに従ってそれぞれ実行される.最初に接続するとoutboundQueueが空に等しい、enqueueDispatchEntryLocked処理後、outboundQueueが空に等しくない場合、startDispatchCycleLocked()メソッドを実行する.
2.7 enqueueDispatchEntryLocked
このメソッドの主な機能:
dispatchModeに基づいてoutboundQueueに参加する必要があるかどうかを決定します.EventEntryに基づいてDispatchEntryイベントを生成する.dispatchEntryをconnectionのoutboundキューに追加します.ここまで実行することは、一度搬送を行うことにより、InputDispatcherにおけるmInboundQueueにおけるイベントを取り出し、ターゲットwindowを見つけた後、パッケージdispatchEntryをconnectionのoutboundキューに組み込むことに等しい.
2.8 startDispatchCycleLocked
startDispatchCycleLockedの主な機能:outboundQueueからイベントを取り出し、waitQueueキューに再読み込み
startDispatchCycleLockedトリガタイミング:最初はconnection.outboundQueueは空に等しく、enqueueDispatchEntryLocked処理後、outboundQueueは空に等しくありません.startDispatchCycleLocked主な機能:outboundQueueからイベントを取り出し、waitQueueキューpublishKeyEvent実行結果statusがOKに等しくない場合:WOULD_BLOCK、waitQueueが空である場合、abortBrokenDispatchCycleLocked()が呼び出され、このメソッドは最終的にJava層のIMSに呼び出される.notifyInputChannelBroken(). WOULD_BLOCK、waitQueueが空でない場合、ブロック状態、すなわちinputPublisherBlocked=true他の場合、a b o r t BrokenDispatchCycleLocked abortBrokenDispatchCycleLocked()メソッドを呼び出すと最終的にJava層のIMSに呼び出される.notifyInputChannelBroken().
2.9 inputPublisher.publishKeyEvent
InputChannelは、socketを介して遠隔地のsocketにメッセージを送信する.ソケットチャネルはどのように構築されていますか?InputDispatcherはどのようにフロントのwindowと通信していますか?次の記事Inputシステム-プロセスインタラクションを参照して、記事のセクション2.1から続けてください.
2.10 releasePendingEventLocked
三.処理Command
3.1 runCommandsLockedInterruptible
mCommandQueueキューのすべてのコマンドをループで処理し、処理はmCommandQueueからCommandEntryを取り出す.
前節【2.4.1】に追加するdoPokeUserActivity LockedInterruptibleコマンド.次に、このメソッドに入ります.
3.2 doPokeUserActivityLockedInterruptible
3.3 pokeUserActivity
3.4 android_server_PowerManagerService_userActivity
3.5 PMS.userActivityFromNative
runCommandsLockedInterruptibleは、mCommandQueueキューからコマンドを取り出し、すべての実行が完了するまで実行します.doPokeUserActivity LockedInterruptibleのほかに、次のコマンドがあります. doNotifyANRLockedInterruptible doInterceptKeyBeforeDispatchingLockedInterruptible doDispatchCycleFinishedLockedInterruptible doNotifyInputChannelBrokenLockedInterruptible doNotifyConfigurationChangedInterruptible
四.まとめ
一.InputDispatcher始点
前編ではシステムのInputReaderスレッドを入力し,InputReaderがEventHubを利用してデータを取得した後にEventEntryイベントを生成し,InputDispatcherのmInboundQueueに参加し,InputDispatcherスレッドを起動することを紹介した.ここではInputDispatcherを紹介し,同様にthreadLoopを起点として解析を開始する.
1.1 threadLoop
まず、InputDispatcherオブジェクトの初期化手順を確認します.
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
mPolicy(policy),
mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
mNextUnblockedEvent(NULL),
mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
// Looper
mLooper = new Looper(false);
mKeyRepeatState.lastKeyEntry = NULL;
//
policy->getDispatcherConfiguration(&mConfig);
}
このメソッドの主な作業:
自分のスレッドに属するLooperオブジェクトを作成します.タイムアウトパラメータはIMSから来ており、パラメータのデフォルト値はkeyRepeatTimeout=500、keyRepeatDelay=50である.
[-> InputDispatcher.cpp]
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce(); //【 1.2】
return true;
}
イベントを配信するためにInputDispatcherのdispatchOnce()を繰り返し呼び出します.
1.2 dispatchOnce
[-> InputDispatcher.cpp]
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{
AutoMutex _l(mLock);
// ,monitor() dispatcher
mDispatcherIsAliveCondition.broadcast();
if (!haveCommandsLocked()) {
// mCommandQueue 【 2.1】
dispatchOnceInnerLocked(&nextWakeupTime);
}
//【 3.1】
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
}
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis); // epoll_wait
}
スレッドはLooper->pollOnceを実行し、epoll_に入ります.wait待機状態は、次のいずれかの場合に待機状態を終了します.
callback:コールバック方法で起動します.timeout:nextWakeupTimeに到着し、タイムアウトが発生しました.wake:Looperのwake()メソッドをアクティブに呼び出す;
二.InputDispatcher
2.1 dispatchOnceInnerLocked
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now(); // , ANR
if (!mDispatchEnabled) { // false
resetKeyRepeatLocked(); //
}
if (mDispatchFrozen) { // false
return; // , ,
}
// app , , , 。
bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
...
if (!mPendingEvent) {
if (mInboundQueue.isEmpty()) {
if (!mPendingEvent) {
return; // ,
}
} else {
// mInboundQueue
mPendingEvent = mInboundQueue.dequeueAtHead();
}
...
resetANRTimeoutsLocked(); // ANR [ 2.1.1]
}
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
dropReason = DROP_REASON_POLICY;
} else if (!mDispatchEnabled) {
dropReason = DROP_REASON_DISABLED;
}
...
switch (mPendingEvent->type) {
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;
}
// [ 2.2]
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
...
}
...
// ,
if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
//[ 2.1.2]
dropInboundEventLocked(mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked(); // pending 2.10]
*nextWakeupTime = LONG_LONG_MIN; //
}
}
enqueueInboundEventLocked()中にmAppSwitchDueTimeがeventTimeに500 ms加算されるように設定されています.
mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
このメソッドの主な機能:
mDispatchFrozenは、イベント配信作業を凍結するかどうかを決定するために使用されます.イベント配信の時点がイベントにmInboundQueueが加わるまでの時間が500 msを超える場合、app切替は期限切れ、すなわちisAppSwitchDue=trueとなる.mInboundQueueが空でない場合、頭部のイベントを取り出し、mPendingEvent変数を入れる.ANR時間をリセットします.ボタン呼び出しdispatchKeyLocked配布イベントなど、EventEntryのtypeタイプに応じてそれぞれ処理される.配布結果に基づいてdoneに入るかどうかを決定する.実行完了(done)の処理:dropReason(デフォルトNOT_DROPPEDは処理しない)に基づいてイベントを失うかどうかを決定する.dropInboundEventLockedは現在処理中のイベント(すなわちmPendingEvent)を解放する.releasePendingEventLocked dispatchKeyLocked配布イベントについて、
doneオーバーライドは実行されません:現在のEvent時間は起動時間より小さいです.policyにブロック操作を実行する機会を与える.findFocusedWindowTargetsLockedメソッドを呼び出すと、INPUT_が返されます.EVENT_INJECTION_PENDING、すなわちtargetsはReady状態にない.doneが実行される場合:このイベントは破棄する必要があります.つまりdropReason!=DROP_REASON_NOT_DROPPED; findFocusedWindowTargetsLockedの返却結果はINPUT_ではありませんEVENT_INJECTION_PENDING(処理中のイベントはありません);次にボタンを例として説明を展開すると、[小節2.2]dispatchKeyLockedに入る.
2.1.1 resetANRTimeoutsLocked
void InputDispatcher::resetANRTimeoutsLocked() {
// cause handle
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
mInputTargetWaitApplicationHandle.clear();
}
2.1.2 dropInboundEventLocked
void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
case DROP_REASON_POLICY:
reason = "inbound event was dropped because the policy consumed it";
break;
case DROP_REASON_DISABLED:
if (mLastDropReason != DROP_REASON_DISABLED) {
ALOGI("Dropped event because input dispatch is disabled.");
}
reason = "inbound event was dropped because input dispatch is disabled";
break;
case DROP_REASON_APP_SWITCH:
ALOGI("Dropped event because of pending overdue app switch.");
reason = "inbound event was dropped because of pending overdue app switch";
break;
case DROP_REASON_BLOCKED:
ALOGI("Dropped event because the current application is not responding and the user "
"has started interacting with a different application.");
reason = "inbound event was dropped because the current application is not responding "
"and the user has started interacting with a different application";
break;
case DROP_REASON_STALE:
ALOGI("Dropped event because it is stale.");
reason = "inbound event was dropped because it is stale";
break;
default:
return;
}
switch (entry->type) {
case EventEntry::TYPE_KEY: {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
...
}
}
2.2 dispatchKeyLocked
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
...
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
// case1: , 。
if (currentTime < entry->interceptKeyWakeupTime) {
if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
*nextWakeupTime = entry->interceptKeyWakeupTime;
}
return false; //
}
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
entry->interceptKeyWakeupTime = 0;
}
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
//case2: policy
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; //
} 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;
}
}
//case3: ,
if (*dropReason != DROP_REASON_NOT_DROPPED) {
setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
return true; //
}
Vector<InputTarget> inputTargets;
//case4: 【 2.3】
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; //
}
addMonitoringTargetsLocked(inputTargets);
// injectionResult , 【 2.4】
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
次のシーンでは、イベントを配布できない場合があります.
現在の時間が起動時間(nextWakeupTime)より小さい場合.policyが事前にイベントをブロックする必要がある場合.dropイベントが必要な場合.フォーカスウィンドウが失敗した場合を探します.上記のすべての状況をスキップすると、イベントの配布を実行するプロセスに入ります.
2.3 findFocusedWindowTargetsLocked
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
int32_t injectionResult;
String8 reason;
if (mFocusedWindowHandle == NULL) {
if (mFocusedApplicationHandle != NULL) {
//【 2.3.2】
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplicationHandle, NULL, nextWakeupTime,
"Waiting because no window has focus but there is a "
"focused application that may eventually add a window "
"when it finishes starting up.");
goto Unresponsive;
}
ALOGI("Dropping event because there is no focused window or focused application.");
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
//
if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
goto Failed;
}
// 【 2.3.1】
reason = checkWindowReadyForMoreInputLocked(currentTime,
mFocusedWindowHandle, entry, "focused");
if (!reason.isEmpty()) {
//【 2.3.2】
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.string());
goto Unresponsive;
}
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
// , [ 2.3.3]
addWindowTargetLocked(mFocusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
inputTargets);
Failed:
Unresponsive:
//TODO: , ,
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
updateDispatchStatisticsLocked(currentTime, entry,
injectionResult, timeSpentWaitingForApplication);
return injectionResult;
}
ここでmFocusedWindowHandleはどこで値を付けますか?インパルスでsetInput Windows()メソッドは、次のInputシステム-UIスレッドを参照してください.
フォーカスウィンドウの検索に失敗した場合:
ウィンドウなし、アプリケーションなし:Dropping event because there is no focused window or focused application.(ANRの場合、handleTargetsNotReadyLockedを呼び出す機会がないため)ウィンドウがなく、アプリケーションがある:Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.また、checkWindowReadyForMoreInputLockedのプロセスを参照してください.
2.3.1 checkWindowReadyForMoreInputLocked
String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
const char* targetType) {
// ,
if (windowHandle->getInfo()->paused) {
return String8::format("Waiting because the %s window is paused.", targetType);
}
// ,
ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
if (connectionIndex < 0) {
return String8::format("Waiting because the %s window's input channel is not "
"registered with the input dispatcher. The window may be in the process "
"of being removed.", targetType);
}
// ,
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
if (connection->status != Connection::STATUS_NORMAL) {
return String8::format("Waiting because the %s window's input connection is %s."
"The window may be in the process of being removed.", targetType,
connection->getStatusLabel());
}
// ,
if (connection->inputPublisherBlocked) {
return String8::format("Waiting because the %s window's input channel is full. "
"Outbound queue length: %d. Wait queue length: %d.",
targetType, connection->outboundQueue.count(), connection->waitQueue.count());
}
if (eventEntry->type == EventEntry::TYPE_KEY) {
// ,
if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) {
return String8::format("Waiting to send key event because the %s window has not "
"finished processing all of the input events that were previously "
"delivered to it. Outbound queue length: %d. Wait queue length: %d.",
targetType, connection->outboundQueue.count(), connection->waitQueue.count());
}
} else {
// , 500ms
if (!connection->waitQueue.isEmpty()
&& currentTime >= connection->waitQueue.head->deliveryTime
+ STREAM_AHEAD_EVENT_TIMEOUT) {
return String8::format("Waiting to send non-key event because the %s window has not "
"finished processing certain input events that were delivered to it over "
"%0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.",
targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
connection->waitQueue.count(),
(currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f);
}
}
return String8::empty();
}
2.3.2 handleTargetsNotReadyLocked
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
const EventEntry* entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t* nextWakeupTime, const char* reason) {
if (applicationHandle == NULL && windowHandle == NULL) {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
mInputTargetWaitStartTime = currentTime; //
mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationHandle.clear();
}
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
nsecs_t timeout;
if (windowHandle != NULL) {
timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else if (applicationHandle != NULL) {
timeout = applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else {
timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; // 5s
}
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
// currentTime dispatchOnceInnerLocked
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = currentTime + timeout;
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationHandle.clear();
if (windowHandle != NULL) {
mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
}
if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
mInputTargetWaitApplicationHandle = applicationHandle;
}
}
}
if (mInputTargetWaitTimeoutExpired) {
return INPUT_EVENT_INJECTION_TIMED_OUT; // ,
}
// 5s, ANR
if (currentTime >= mInputTargetWaitTimeoutTime) {
onANRLocked(currentTime, applicationHandle, windowHandle,
entry->eventTime, mInputTargetWaitStartTime, reason);
*nextWakeupTime = LONG_LONG_MIN; // ANR
return INPUT_EVENT_INJECTION_PENDING;
} else {
if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
*nextWakeupTime = mInputTargetWaitTimeoutTime; //
}
return INPUT_EVENT_INJECTION_PENDING;
}
}
ANRタイムアウトポイントはmInputTargetWaitTimeoutTimeであり、この値はcurrentTime+5 sに等しく、ここでcurrentTimeはdispatchOnceInnerLockedメソッドを実行する起点を指す.ここでmInputTargetWaitCauseがINPUT_に等しいことを設定します.TARGET_WAIT_CAUSE_APPLICATION_NOT_READY(アプリケーションの準備ができていない)は、前のresetANRTimeoutsLocked()プロセスが待機理由をリセットする唯一の場所です.
ANR時間区間は、handleTargetsNotReadyLocked()メソッドが次回実行されるまで、dispatchOnceInnerLockedメソッド体の開始点であり、ANRがトリガされるか否かを決定するために準備されていない時間帯である.
現在のイベントdispatch中にf i n d FocusedWindowTargetsLocked()メソッドを実行し、resetANRTimeoutsLocked()を次の実行までの時間間隔.
handleTargetsNotReadyLocked()の判断プロセス:
アプリケーションHandleとwindowHandleが同時に空で、システムの準備が整っている場合に待機理由INPUT_を設定します.TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; タイムアウト待ち時間を無限大に設定します.TimeoutExpired=falseクリア待ちキューを設定します.アプリケーションHandleとwindowHandleの少なくとも1つが空でなく、アプリケーションの準備が整っている場合:待機理由INPUT_を設定します.TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; タイムアウト待ち時間を5 sに設定します.TimeoutExpired=falseクリア待ちキューを設定します.[セクション2.3]f i n d FocusedWindowTargetsLockedに戻り、ANRが発生しない場合、addWindowTargetLocked()はイベントをinputTargetsに追加します.
2.3.3 addWindowTargetLocked
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
inputTargets.push();
const InputWindowInfo* windowInfo = windowHandle->getInfo();
InputTarget& target = inputTargets.editTop();
target.inputChannel = windowInfo->inputChannel;
target.flags = targetFlags;
target.xOffset = - windowInfo->frameLeft;
target.yOffset = - windowInfo->frameTop;
target.scaleFactor = windowInfo->scaleFactor;
target.pointerIds = pointerIds;
}
現在のフォーカスウィンドウmFocusedWindowHandleのinputChannelをinputTargetsに渡します.
2.4 dispatchEventLocked
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
//【 2.4.1】 mCommandQueue doPokeUserActivityLockedInterruptible
pokeUserActivityLocked(eventEntry);
for (size_t i = 0; i < inputTargets.size(); i++) {
const InputTarget& inputTarget = inputTargets.itemAt(i);
//[ 2.4.3]
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
// [ 2.5]
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
}
}
この方法の主な機能はeventEntryをターゲットinputTargetsに送信することである.
ここでpokeUserActivity Locked(eventEntry)メソッドは最終的にJava層のPowerManagerServicesに呼び出される.JAvaにおけるuserActivity FromNative()メソッド.これもPMSにおける唯一のnative callメソッドである.
2.4.1 pokeUserActivityLocked
void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
if (mFocusedWindowHandle != NULL) {
const InputWindowInfo* info = mFocusedWindowHandle->getInfo();
if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
return;
}
}
...
//【 2.4.2】
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doPokeUserActivityLockedInterruptible);
commandEntry->eventTime = eventEntry->eventTime;
commandEntry->userActivityEventType = eventType;
}
2.4.2 postCommandLocked
InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
CommandEntry* commandEntry = new CommandEntry(command);
// mCommandQueue
mCommandQueue.enqueueAtTail(commandEntry);
return commandEntry;
}
2.4.3 getConnectionIndexLocked
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
if (connection->inputChannel.get() == inputChannel.get()) {
return connectionIndex;
}
}
return -1;
}
inputChannelのfdに基づいてmConnectionsByFdキューからターゲットconnectionを問い合わせる.
2.5 prepareDispatchCycleLocked
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
if (connection->status != Connection::STATUS_NORMAL) {
return; // ,
}
...
//[ 2.6]
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
接続ステータスが正しくない場合は、直接戻ります.
2.6 enqueueDispatchEntriesLocked
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
bool wasEmpty = connection->outboundQueue.isEmpty();
//[ 2.7]
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 (wasEmpty && !connection->outboundQueue.isEmpty()) {
// outbound , outbound .[ 2.8]
startDispatchCycleLocked(currentTime, connection);
}
}
このメソッドの主な機能:
DispatchEntryイベントのキューへの参加は、dispatchModeに従ってそれぞれ実行される.最初に接続するとoutboundQueueが空に等しい、enqueueDispatchEntryLocked処理後、outboundQueueが空に等しくない場合、startDispatchCycleLocked()メソッドを実行する.
2.7 enqueueDispatchEntryLocked
void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
int32_t inputTargetFlags = inputTarget->flags;
if (!(inputTargetFlags & dispatchMode)) {
return; // ,
}
inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
// , connection outbound
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry,
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
dispatchEntry->resolvedAction = keyEntry->action;
dispatchEntry->resolvedFlags = keyEntry->flags;
if (!connection->inputState.trackKey(keyEntry,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
delete dispatchEntry;
return; //
}
break;
}
...
}
...
// outboundQueue
connection->outboundQueue.enqueueAtTail(dispatchEntry);
}
このメソッドの主な機能:
dispatchModeに基づいてoutboundQueueに参加する必要があるかどうかを決定します.EventEntryに基づいてDispatchEntryイベントを生成する.dispatchEntryをconnectionのoutboundキューに追加します.ここまで実行することは、一度搬送を行うことにより、InputDispatcherにおけるmInboundQueueにおけるイベントを取り出し、ターゲットwindowを見つけた後、パッケージdispatchEntryをconnectionのoutboundキューに組み込むことに等しい.
2.8 startDispatchCycleLocked
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
// Connection , outboundQueue
while (connection->status == Connection::STATUS_NORMAL
&& !connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.head;
dispatchEntry->deliveryTime = currentTime; // deliveryTime
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
// Key [ 2.9]
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;
}
...
}
if (status) { //publishKeyEvent
if (status == WOULD_BLOCK) {
if (connection->waitQueue.isEmpty()) {
//pipe , waitQueue .
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
} else {
//
connection->inputPublisherBlocked = true;
}
} else {
//
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
}
return;
}
// outboundQueue , waitQueue
connection->outboundQueue.dequeue(dispatchEntry);
connection->waitQueue.enqueueAtTail(dispatchEntry);
}
}
startDispatchCycleLockedの主な機能:outboundQueueからイベントを取り出し、waitQueueキューに再読み込み
startDispatchCycleLockedトリガタイミング:最初はconnection.outboundQueueは空に等しく、enqueueDispatchEntryLocked処理後、outboundQueueは空に等しくありません.startDispatchCycleLocked主な機能:outboundQueueからイベントを取り出し、waitQueueキューpublishKeyEvent実行結果statusがOKに等しくない場合:WOULD_BLOCK、waitQueueが空である場合、abortBrokenDispatchCycleLocked()が呼び出され、このメソッドは最終的にJava層のIMSに呼び出される.notifyInputChannelBroken(). WOULD_BLOCK、waitQueueが空でない場合、ブロック状態、すなわちinputPublisherBlocked=true他の場合、a b o r t BrokenDispatchCycleLocked abortBrokenDispatchCycleLocked()メソッドを呼び出すと最終的にJava層のIMSに呼び出される.notifyInputChannelBroken().
2.9 inputPublisher.publishKeyEvent
[-> InputTransport.cpp]
status_t InputPublisher::publishKeyEvent(...) {
if (!seq) {
return BAD_VALUE;
}
InputMessage msg;
msg.header.type = InputMessage::TYPE_KEY;
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
// InputChannel
return mChannel->sendMessage(&msg);
}
InputChannelは、socketを介して遠隔地のsocketにメッセージを送信する.ソケットチャネルはどのように構築されていますか?InputDispatcherはどのようにフロントのwindowと通信していますか?次の記事Inputシステム-プロセスインタラクションを参照して、記事のセクション2.1から続けてください.
2.10 releasePendingEventLocked
void InputDispatcher::releasePendingEventLocked() {
if (mPendingEvent) {
resetANRTimeoutsLocked(); // ANR
releaseInboundEventLocked(mPendingEvent); // mPendingEvent , mRecentQueue
mPendingEvent = NULL; // mPendingEvent .
}
}
三.処理Command
3.1 runCommandsLockedInterruptible
bool InputDispatcher::runCommandsLockedInterruptible() {
if (mCommandQueue.isEmpty()) {
return false;
}
do {
// mCommandQueue
CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
Command command = commandEntry->command;
// 'LockedInterruptible'
(this->*command)(commandEntry);
commandEntry->connection.clear();
delete commandEntry;
} while (! mCommandQueue.isEmpty());
return true;
}
mCommandQueueキューのすべてのコマンドをループで処理し、処理はmCommandQueueからCommandEntryを取り出す.
typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);
struct CommandEntry : Link<CommandEntry> {
CommandEntry(Command command);
Command command;
sp<Connection> connection;
nsecs_t eventTime;
KeyEntry* keyEntry;
sp<InputApplicationHandle> inputApplicationHandle;
sp<InputWindowHandle> inputWindowHandle;
String8 reason;
int32_t userActivityEventType;
uint32_t seq;
bool handled;
};
前節【2.4.1】に追加するdoPokeUserActivity LockedInterruptibleコマンド.次に、このメソッドに入ります.
3.2 doPokeUserActivityLockedInterruptible
[-> InputDispatcher]
void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
//【 4.3】
mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
mLock.lock();
}
3.3 pokeUserActivity
[-> com_android_server_input_InputManagerService.cpp]
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
//[ 4.4]
android_server_PowerManagerService_userActivity(eventTime, eventType);
}
3.4 android_server_PowerManagerService_userActivity
[-> com_android_server_power_PowerManagerService.cpp]
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
// Tell the power HAL when user activity occurs.
if (gPowerModule && gPowerModule->powerHint) {
gPowerModule->powerHint(gPowerModule, POWER_HINT_INTERACTION, NULL);
}
if (gPowerManagerServiceObj) {
...
//[ 4.5]
env->CallVoidMethod(gPowerManagerServiceObj,
gPowerManagerServiceClassInfo.userActivityFromNative,
nanoseconds_to_milliseconds(eventTime), eventType, 0);
}
}
3.5 PMS.userActivityFromNative
[-> PowerManagerService.java]
private void userActivityFromNative(long eventTime, int event, int flags) {
userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
}
private void userActivityInternal(long eventTime, int event, int flags, int uid) {
synchronized (mLock) {
if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
updatePowerStateLocked();
}
}
}
runCommandsLockedInterruptibleは、mCommandQueueキューからコマンドを取り出し、すべての実行が完了するまで実行します.doPokeUserActivity LockedInterruptibleのほかに、次のコマンドがあります.
四.まとめ