1. InputDispatcher::notifyKey
如果系统发现一个Event是KeyEvent, 会调用nofityKey去通知收到一个KeyEvent. 在notifyKey中会查看policy的策略。
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); //mPolicy实际上就是 NativeInputManager。 当这个函数执行后会把对应的policy也设置到poicyFlags里面。
2. NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags)
NativeInputManager是一个JNI类,自己不做什么工作,只是通过env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptKeyBeforeQueueing, keyEventObj, policyFlags, isScreenOn); 去调用Java的interceptKeyBeforeQueueing方法。 实际上就是调用 InputManagerService.interceptKeyBeforeQueueing()
3. InputManagerService.interceptKeyBeforeQueueing()
// Native callback. private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { return mCallbacks.interceptKeyBeforeQueueing( event, policyFlags, isScreenOn); }可以看到里面只是调用了回到函数而已,而mCallbacks就是InputMoniter.java
4. InputMoniter.interceptKeyBeforeQueueing()
/* Provides an opportunity for the window manager policy to intercept early key * processing as soon as the key has been read from the device. */ public int interceptKeyBeforeQueueing( KeyEvent event, int policyFlags, boolean isScreenOn) { Slog.d("InputMonitor.java", "interceptKeyBeforeQueueing !!!!!!"); return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn); }
5. mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
// Native callback. private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { Slog.d("InputManagerService.java", "interceptKeyBeforeQueueing !!!!!!!!!!!!!!"); return mCallbacks.interceptKeyBeforeQueueing( event, policyFlags, isScreenOn); }InputMoniter.interceptKeyBeforeQueueing()中就是直接调了mService.mPolicy.interceptKeyBeforeQueueing, mService是WindowManagerService, mPolicy是PhoneWindowManager. 简而言之就是会去直走到PhoneWindowManager中。
6. PhoneWindowManager.interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn)
代码很长,需要慢慢分析。
7. InputDispatcher::dispatchKeyLocked
当InputDispatcher::notifyKey 完成之后,InputDispatcher就会调用dispatchKeyLocked去做进一步的分配。
一个Event过来都是通过InputDispatcher进行分发的,在dispatchKeyLocked中会调用 InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible 去
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. // Give the policy a chance to intercept the key. if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); ... ... } else { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { ... ... } // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true; }
8. InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible
在这个函数中哦你依旧还是通过mPolicy(NativeInputManager)去通过JNI调用InputManagerService.
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( CommandEntry* commandEntry) { ... ... nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle, &event, entry->policyFlags); mLock.lock(); if (delay < 0) { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP; } else if (!delay) { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } else { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER; entry->interceptKeyWakeupTime = now() + delay; } entry->release(); }
9. NativeInputManager::interceptKeyBeforeDispatching
nsecs_t NativeInputManager::interceptKeyBeforeDispatching( const sp<InputWindowHandle>& inputWindowHandle, const KeyEvent* keyEvent, uint32_t policyFlags) { // Policy: // - Ignore untrusted events and pass them along. // - Filter normal events and trusted injected events through the window manager policy to // handle the HOME key and the like. nsecs_t result = 0; if (policyFlags & POLICY_FLAG_TRUSTED) { jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle); jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); if (keyEventObj) { jlong delayMillis = env->CallLongMethod(mServiceObj, gServiceClassInfo.interceptKeyBeforeDispatching, inputWindowHandleObj, keyEventObj, policyFlags); } env->DeleteLocalRef(inputWindowHandleObj); } return result; }
// Native callback. private long interceptKeyBeforeDispatching(InputWindowHandle focus, KeyEvent event, int policyFlags) { return mCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags); }
11. InputMonitor.interceptKeyBeforeDispatching()
/* Provides an opportunity for the window manager policy to process a key before * ordinary dispatch. */ public long interceptKeyBeforeDispatching( InputWindowHandle focus, KeyEvent event, int policyFlags) { Log.d("InputMonitor.java","interceptKeyBeforeDispatching !!!!!!"); WindowState windowState = focus != null ? (WindowState) focus.windowState : null; return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); }
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(); final int flags = event.getFlags(); final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final boolean canceled = event.isCanceled(); if (DEBUG_INPUT) { Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount=" + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed + " canceled=" + canceled); } // If we think we might have a volume down & power key chord on the way // but we're not sure, then tell the dispatcher to wait a little while and // try again later before dispatching. if (mScreenshotChordEnabled && (flags & KeyEvent.FLAG_FALLBACK) == 0) { if (mVolumeDownKeyTriggered && !mPowerKeyTriggered) { final long now = SystemClock.uptimeMillis(); final long timeoutTime = mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS; if (now < timeoutTime) { return timeoutTime - now; } } if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeDownKeyConsumedByScreenshotChord) { if (!down) { mVolumeDownKeyConsumedByScreenshotChord = false; } return -1; } } // First we always handle the home key here, so applications // can never break it, although if keyguard is on, we do let // it handle it, because that gives us the correct 5 second // timeout. if (keyCode == KeyEvent.KEYCODE_HOME) { // If we have released the home key, and didn't do anything else // while it was pressed, then it is time to go home! if (!down) { final boolean homeWasLongPressed = mHomeLongPressed; mHomePressed = false; mHomeLongPressed = false; if (!homeWasLongPressed) { try { IStatusBarService statusbar = getStatusBarService(); if (statusbar != null) { statusbar.cancelPreloadRecentApps(); } } catch (RemoteException e) { Slog.e(TAG, "RemoteException when showing recent apps", e); // re-acquire status bar service next time it is needed. mStatusBarService = null; } mHomePressed = false; if (!canceled) { // If an incoming call is ringing, HOME is totally disabled. // (The user is already on the InCallScreen at this point, // and his ONLY options are to answer or reject the call.) boolean incomingRinging = false; try { ITelephony telephonyService = getTelephonyService(); if (telephonyService != null) { incomingRinging = telephonyService.isRinging(); } } catch (RemoteException ex) { Log.w(TAG, "RemoteException from getPhoneInterface()", ex); } if (incomingRinging) { Log.i(TAG, "Ignoring HOME; there's a ringing incoming call."); } else { launchHomeFromHotKey(); //launch home } } else { Log.i(TAG, "Ignoring HOME; event canceled."); } return -1; } } // If a system window has focus, then it doesn't make sense // right now to interact with applications. WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null; if (attrs != null) { final int type = attrs.type; if (type == WindowManager.LayoutParams.TYPE_KEYGUARD || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) { // the "app" is keyguard, so give it the key return 0; } final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length; for (int i=0; i<typeCount; i++) { if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) { // don't do anything, but also don't pass it to the app return -1; } } } if (down) { if (!mHomePressed && mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) { try { IStatusBarService statusbar = getStatusBarService(); if (statusbar != null) { statusbar.preloadRecentApps(); } } catch (RemoteException e) { Slog.e(TAG, "RemoteException when preloading recent apps", e); // re-acquire status bar service next time it is needed. mStatusBarService = null; } } if (repeatCount == 0) { mHomePressed = true; } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { if (!keyguardOn) { handleLongPressOnHome(); } } } return -1; } else if (keyCode == KeyEvent.KEYCODE_MENU) { // Hijack modified menu keys for debugging features final int chordBug = KeyEvent.META_SHIFT_ON; if (down && repeatCount == 0) { if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) { Intent intent = new Intent(Intent.ACTION_BUG_REPORT); mContext.sendOrderedBroadcast(intent, null); return -1; } else if (SHOW_PROCESSES_ON_ALT_MENU && (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) { Intent service = new Intent(); service.setClassName(mContext, "com.android.server.LoadAverageService"); ContentResolver res = mContext.getContentResolver(); boolean shown = Settings.System.getInt( res, Settings.System.SHOW_PROCESSES, 0) != 0; if (!shown) { mContext.startService(service); } else { mContext.stopService(service); } Settings.System.putInt( res, Settings.System.SHOW_PROCESSES, shown ? 0 : 1); return -1; } } } else if (keyCode == KeyEvent.KEYCODE_SEARCH) { if (down) { if (repeatCount == 0) { mSearchKeyShortcutPending = true; mConsumeSearchKeyUp = false; } } else { mSearchKeyShortcutPending = false; if (mConsumeSearchKeyUp) { mConsumeSearchKeyUp = false; return -1; } } return 0; } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) { if (down && repeatCount == 0 && !keyguardOn) { showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS); } return -1; } else if (keyCode == KeyEvent.KEYCODE_ASSIST) { if (down) { if (repeatCount == 0) { mAssistKeyLongPressed = false; } else if (repeatCount == 1) { mAssistKeyLongPressed = true; if (!keyguardOn) { launchAssistLongPressAction(); } } } else { if (mAssistKeyLongPressed) { mAssistKeyLongPressed = false; } else { if (!keyguardOn) { launchAssistAction(); } } } return -1; } // Shortcuts are invoked through Search+key, so intercept those here // Any printing key that is chorded with Search should be consumed // even if no shortcut was invoked. This prevents text from being // inadvertently inserted when using a keyboard that has built-in macro // shortcut keys (that emit Search+x) and some of them are not registered. if (mSearchKeyShortcutPending) { final KeyCharacterMap kcm = event.getKeyCharacterMap(); if (kcm.isPrintingKey(keyCode)) { mConsumeSearchKeyUp = true; mSearchKeyShortcutPending = false; if (down && repeatCount == 0 && !keyguardOn) { Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode, metaState); if (shortcutIntent != null) { shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { mContext.startActivity(shortcutIntent); } catch (ActivityNotFoundException ex) { Slog.w(TAG, "Dropping shortcut key combination because " + "the activity to which it is registered was not found: " + "SEARCH+" + KeyEvent.keyCodeToString(keyCode), ex); } } else { Slog.i(TAG, "Dropping unregistered shortcut key combination: " + "SEARCH+" + KeyEvent.keyCodeToString(keyCode)); } } return -1; } } // Invoke shortcuts using Meta. if (down && repeatCount == 0 && !keyguardOn && (metaState & KeyEvent.META_META_ON) != 0) { final KeyCharacterMap kcm = event.getKeyCharacterMap(); if (kcm.isPrintingKey(keyCode)) { Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode, metaState & ~(KeyEvent.META_META_ON | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON)); if (shortcutIntent != null) { shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { mContext.startActivity(shortcutIntent); } catch (ActivityNotFoundException ex) { Slog.w(TAG, "Dropping shortcut key combination because " + "the activity to which it is registered was not found: " + "META+" + KeyEvent.keyCodeToString(keyCode), ex); } return -1; } } } // Handle application launch keys. if (down && repeatCount == 0 && !keyguardOn) { String category = sApplicationLaunchKeyCategories.get(keyCode); if (category != null) { Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { mContext.startActivity(intent); } catch (ActivityNotFoundException ex) { Slog.w(TAG, "Dropping application launch key because " + "the activity to which it is registered was not found: " + "keyCode=" + keyCode + ", category=" + category, ex); } return -1; } } // Display task switcher for ALT-TAB or Meta-TAB. if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) { if (mRecentAppsDialogHeldModifiers == 0 && !keyguardOn) { final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON) || KeyEvent.metaStateHasModifiers( shiftlessModifiers, KeyEvent.META_META_ON)) { mRecentAppsDialogHeldModifiers = shiftlessModifiers; showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW); return -1; } } } else if (!down && mRecentAppsDialogHeldModifiers != 0 && (metaState & mRecentAppsDialogHeldModifiers) == 0) { mRecentAppsDialogHeldModifiers = 0; showOrHideRecentAppsDialog(keyguardOn ? RECENT_APPS_BEHAVIOR_DISMISS : RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH); } // Handle keyboard language switching. if (down && repeatCount == 0 && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH || (keyCode == KeyEvent.KEYCODE_SPACE && (metaState & KeyEvent.META_CTRL_MASK) != 0))) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); return -1; } if (mLanguageSwitchKeyPressed && !down && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH || keyCode == KeyEvent.KEYCODE_SPACE)) { mLanguageSwitchKeyPressed = false; return -1; } // Let the application handle the key. return 0; }