一个key事件的调用过程
一个偶然的机会,有一个报错的问题,日志如下:
AndroidRuntime: at android.view.View.performClick(View.java:7259)
AndroidRuntime: at android.view.View.performClickInternal(View.java:7236)
AndroidRuntime: at android.view.View.onKeyUp(View.java:14697)
AndroidRuntime: at android.view.KeyEvent.dispatch(KeyEvent.java:2902)
AndroidRuntime: at android.view.View.dispatchKeyEvent(View.java:13890)
AndroidRuntime: at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1917)
AndroidRuntime: at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: at com.android.internal.policy.DecorView.superDispatchKeyEvent(DecorView.java:473)
AndroidRuntime: at com.android.internal.policy.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1871)
AndroidRuntime: at android.app.Activity.dispatchKeyEvent(Activity.java:4093)
AndroidRuntime: at androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.java:122)
AndroidRuntime: at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84)
AndroidRuntime: at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:140)
AndroidRuntime: at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:569)
AndroidRuntime: at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59)
AndroidRuntime: at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:3054)
AndroidRuntime: at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:385)
AndroidRuntime: at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:5698)
AndroidRuntime: at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5562)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5069)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5122)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5088)
AndroidRuntime: at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5228)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5096)
AndroidRuntime: at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5285)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5069)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5122)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5088)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5096)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5069)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5122)
AndroidRuntime: at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5088)
AndroidRuntime: at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5261)
AndroidRuntime: at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:5422)
AndroidRuntime: at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:3067)
AndroidRuntime: at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2610)
AndroidRuntime: at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2601)
AndroidRuntime: at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:3044)
AndroidRuntime: at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:143)
AndroidRuntime: at android.os.MessageQueue.nativePollOnce(Native Method)
AndroidRuntime: at android.os.MessageQueue.next(MessageQueue.java:336)
AndroidRuntime: at android.os.Looper.loop(Looper.java:174)
AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7618)
AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
一看这个报错日志,乖乖,这不就是一个key事件的完整调用过程吗。
下面我们重点关注一下几个关键的方法:
关键方法的分析
ViewRootImpl$ViewPostImeInputStage.onProcess
ViewRootImpl$ViewPostImeInputStage.onProcess方法:
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);//如果是keyEvent事件,就调用processKeyEvent方法
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}
ViewRootImpl$ViewPostImeInputStage.processKeyEvent
ViewRootImpl$ViewPostImeInputStage.processKeyEvent方法:
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mUnhandledKeyManager.preViewDispatch(event)) {
return FINISH_HANDLED;
}
Slog.d(mTag, "processKeyEvent , event = " + event + ", mView = " + mView + ", context = " + mContext + ", package = " + mContext.getPackageName());
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {//调用mView.dispatchKeyEvent方法
return FINISH_HANDLED;
}
......
}
DecorView.dispatchKeyEvent
DecorView.dispatchKeyEvent方法:
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
final int action = event.getAction();
final boolean isDown = action == KeyEvent.ACTION_DOWN;
if (isDown && (event.getRepeatCount() == 0)) {
// First handle chording of panel key: if a panel key is held
// but not released, try to execute a shortcut in it.
if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
boolean handled = dispatchKeyShortcutEvent(event);//调用dispatchKeyShortcutEvent方法
if (handled) {
return true;
}
}
// If a panel is open, perform a shortcut on it without the
// chorded panel key
if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
return true;
}
}
}
if (!mWindow.isDestroyed()) {
Log.i(mLogTag, "dispatchKeyEvent , event = " + event + ", cb = " + mWindow.getCallback() + ", mFeatureId = " + mFeatureId + ", context = " + mContext
+ ", package = " + mContext.getPackageName());
final Window.Callback cb = mWindow.getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);//如果mWindow有Callback 接口,就会调用Callback的dispatchKeyEvent方法,或者调用super.dispatchKeyEvent
Log.i(mLogTag, "dispatchKeyEvent , handled = " + handled);
if (handled) {
return true;
}
}
return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
: mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);//调用mWindow的onKeyDown或onKeyUp方法
}
Activity.dispatchKeyEvent
Activity.dispatchKeyEvent方法:
/**
* Called to process key events. You can override this to intercept all
* key events before they are dispatched to the window. Be sure to call
* this implementation for key events that should be handled normally.
*
* @param event The key event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchKeyEvent(KeyEvent event) {
onUserInteraction();
// Let action bars open menus in response to the menu key prioritized over
// the window handling it
final int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.KEYCODE_MENU &&
mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
return true;
}
Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {//调用win.superDispatchKeyEvent方法
return true;
}
View decor = mDecor;
if (decor == null) decor = win.getDecorView();
return event.dispatch(this, decor != null
? decor.getKeyDispatcherState() : null, this);//调用event.dispatch方法
}
PhoneWindow.superDispatchKeyEvent
PhoneWindow.superDispatchKeyEvent方法:
public boolean superDispatchKeyEvent(KeyEvent event) {
return mDecor.superDispatchKeyEvent(event);
}
DecorView.superDispatchKeyEvent
DecorView.superDispatchKeyEvent方法:
public boolean superDispatchKeyEvent(KeyEvent event) {
// Give priority to closing action modes if applicable.
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {//处理back key
final int action = event.getAction();
// Back cancels action modes first.
if (mPrimaryActionMode != null) {
if (action == KeyEvent.ACTION_UP) {
mPrimaryActionMode.finish();
}
return true;
}
}
if (super.dispatchKeyEvent(event)) {//调用super.dispatchKeyEvent
return true;
}
return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);//调用getViewRootImpl().dispatchUnhandledKeyEvent
}
ViewGroup.dispatchKeyEvent
ViewGroup.dispatchKeyEvent方法:
// The view contained within this ViewGroup that has or contains focus.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private View mFocused;
......
public boolean dispatchKeyEvent(KeyEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 1);
}
if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
if (super.dispatchKeyEvent(event)) {
return true;
}
} else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
== PFLAG_HAS_BOUNDS) {
if (mFocused.dispatchKeyEvent(event)) {//调用mFocused.dispatchKeyEvent
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
}
return false;
}
View.dispatchKeyEvent
View.dispatchKeyEvent方法:
/**
* Dispatch a key event to the next view on the focus path. This path runs
* from the top of the view tree down to the currently focused view. If this
* view has focus, it will dispatch to itself. Otherwise it will dispatch
* the next node down the focus path. This method also fires any key
* listeners.
*
* @param event The key event to be dispatched.
* @return True if the event was handled, false otherwise.
*/
public boolean dispatchKeyEvent(KeyEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 0);
}
// Give any attached key listener a first crack at the event.
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {//对于view,调用li.mOnKeyListener.onKey方法处理key事件
return true;
}
if (event.dispatch(this, mAttachInfo != null
? mAttachInfo.mKeyDispatchState : null, this)) {//调用event.dispatch方法
return true;
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);//调用mInputEventConsistencyVerifier.onUnhandledEvent方法
}
return false;
}
KeyEvent.dispatch
KeyEvent.dispatch方法:
static final boolean DEBUG = (!android.os.Build.IS_USER);//false;
......
/**
* Deliver this key event to a {@link Callback} interface. If this is
* an ACTION_MULTIPLE event and it is not handled, then an attempt will
* be made to deliver a single normal event.
*
* @param receiver The Callback that will be given the event.
* @param state State information retained across events.
* @param target The target of the dispatch, for use in tracking.
*
* @return The return value from the Callback method that was called.
*/
public final boolean dispatch(Callback receiver, DispatcherState state,
Object target) {
switch (mAction) {
case ACTION_DOWN: {
mFlags &= ~FLAG_START_TRACKING;
if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
+ ": " + this);
boolean res = receiver.onKeyDown(mKeyCode, this);//调用receiver.onKeyDown
if (state != null) {
if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
if (DEBUG) Log.v(TAG, " Start tracking!");
state.startTracking(this, target);//调用state.startTracking
} else if (isLongPress() && state.isTracking(this)) {
try {
if (receiver.onKeyLongPress(mKeyCode, this)) {//调用receiver.onKeyLongPress方法
if (DEBUG) Log.v(TAG, " Clear from long press!");
state.performedLongPress(this);
res = true;
}
} catch (AbstractMethodError e) {
}
}
}
return res;
}
case ACTION_UP:
if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
+ ": " + this);
if (state != null) {
state.handleUpEvent(this);//调用state.handleUpEvent方法
}
return receiver.onKeyUp(mKeyCode, this);//调用receiver.onKeyUp方法
case ACTION_MULTIPLE:
final int count = mRepeatCount;
final int code = mKeyCode;
if (receiver.onKeyMultiple(code, count, this)) {//调用receiver.onKeyMultiple方法
return true;
}
if (code != KeyEvent.KEYCODE_UNKNOWN) {
mAction = ACTION_DOWN;
mRepeatCount = 0;
boolean handled = receiver.onKeyDown(code, this);//调用receiver.onKeyDown
if (handled) {
mAction = ACTION_UP;
receiver.onKeyUp(code, this);//调用receiver.onKeyUp方法
}
mAction = ACTION_MULTIPLE;
mRepeatCount = count;
return handled;
}
return false;
}
return false;
}
这个日志的关键标记位:DEBUG ,建议userdebug版本打开,方便定位KeyEvent的问题。
View.onKeyUp
View.onKeyUp方法:
/**
* Default implementation of {@link KeyEvent.Callback#onKeyUp(int, KeyEvent)
* KeyEvent.Callback.onKeyUp()}: perform clicking of the view
* when {@link KeyEvent#KEYCODE_DPAD_CENTER}, {@link KeyEvent#KEYCODE_ENTER}
* or {@link KeyEvent#KEYCODE_SPACE} is released.
* <p>Key presses in software keyboards will generally NOT trigger this listener,
* although some may elect to do so in some situations. Do not rely on this to
* catch software key presses.
*
* @param keyCode A key code that represents the button pressed, from
* {@link android.view.KeyEvent}.
* @param event The KeyEvent object that defines the button action.
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
android.util.SeempLog.record(5);
if (KeyEvent.isConfirmKey(keyCode)) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) {
setPressed(false);
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
if (!event.isCanceled()) {
return performClickInternal();//调用performClickInternal,响应View的click事件
}
}
}
}
return false;
}
经过上面的几个核心类的源码查看,我们可以看出KeyEvent从上到下分发的具体流程:
ViewRootImpl$ViewPostImeInputStage.processKeyEvent===>DecorView.dispatchKeyEvent===>Activity.dispatchKeyEvent===>PhoneWindow.superDispatchKeyEvent===>DecorView.superDispatchKeyEvent===>ViewGroup.dispatchKeyEvent===>View.dispatchKeyEventt===>KeyEvent.dispatch===>View.onKeyUp.
也就是将KeyEvent依次传递:ViewRootImpl–DecorView–Activity–PhoneWindow–DecorView–ViewGroup–View–KeyEvent.dispatch–View.onKeyUp.这个非常明显,是从View的上层往下层传递的。
KeyEvent传递的时序图
Home 键的处理
Home键有一点特殊,我们可以看一下Home键的处理流程:
Home键的堆栈调用:
com.android.server.policy.PhoneWindowManager$DisplayHomeButtonHandler.handleHomeButton(PhoneWindowManager.java:1784)
com.android.server.policy.PhoneWindowManager.interceptKeyBeforeDispatchingInner(PhoneWindowManager.java:3090)
com.android.server.policy.PhoneWindowManager.interceptKeyBeforeDispatching(PhoneWindowManager.java:2955)
com.android.server.wm.InputManagerCallback.interceptKeyBeforeDispatching(InputManagerCallback.java:183)
com.android.server.input.InputManagerService.interceptKeyBeforeDispatching(InputManagerService.java:1900)
Home键调用的关键方法
- InputManagerService.interceptKeyBeforeDispatching
// Native callback.
private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
- InputManagerCallback.interceptKeyBeforeDispatching
/**
* Provides an opportunity for the window manager policy to process a key before
* ordinary dispatch.
*/
@Override
public long interceptKeyBeforeDispatching(
IBinder focus, KeyEvent event, int policyFlags) {
WindowState windowState = mService.windowForClientLocked(null, focus, false);
return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}
- PhoneWindowManager.interceptKeyBeforeDispatching
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
final long result = interceptKeyBeforeDispatchingInner(win, event, policyFlags);
......
- PhoneWindowManager.interceptKeyBeforeDispatchingInner
private long interceptKeyBeforeDispatchingInner(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();
final int displayId = event.getDisplayId();
if (DEBUG_INPUT) {
Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
+ repeatCount + " keyguardOn=" + keyguardOn + " canceled=" + canceled);
}
......
// 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) {//这里才是处理Home Key的逻辑片,事实上,许多Key都是在这个方法里处理的。
if (getKeyCodeDispatchEnable()) {
Slog.w("PhoneWindowManager", "Ignoring HOME because getKeyCodeDispatchEnable() = true");
return 0;
}
DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId);
if (handler == null) {
handler = new DisplayHomeButtonHandler(displayId);
mDisplayHomeButtonHandlers.put(displayId, handler);
}
return handler.handleHomeButton(win, event);
- PhoneWindowManager$DisplayHomeButtonHandler.handleHomeButton
int handleHomeButton(WindowState win, KeyEvent event) {
......
// Delay handling home if a double-tap is possible.
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
mHomeDoubleTapPending = true;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return -1;
}
// Post to main thread to avoid blocking input pipeline.
mHandler.post(() -> handleShortPressOnHome(mDisplayId));
return -1;
}
......
// Remember that home is pressed and handle special actions.
if (repeatCount == 0) {
mHomePressed = true;
if (mHomeDoubleTapPending) {
mHomeDoubleTapPending = false;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
handleDoubleTapOnHome();
// TODO(multi-display): Remove display id check once we support recents on
// multi-display
} else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
&& mDisplayId == DEFAULT_DISPLAY) {
preloadRecentApps();
}
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
if (!keyguardOn) {
// Post to main thread to avoid blocking input pipeline.
mHandler.post(() -> handleLongPressOnHome(event.getDeviceId()));
}
}
return -1;
}
Back键的处理
这是back键的调用堆栈
com.android.server.policy.PhoneWindowManager.interceptBackKeyUp(PhoneWindowManager.java:1042)
com.android.server.policy.PhoneWindowManager.interceptBackKeyDown(PhoneWindowManager.java:1021)
com.android.server.policy.PhoneWindowManager.interceptKeyBeforeQueueing(PhoneWindowManager.java:4191)
com.android.server.wm.InputManagerCallback.interceptKeyBeforeQueueing(InputManagerCallback.java:164)
com.android.server.input.InputManagerService.interceptKeyBeforeQueueing(InputManagerService.java:1888)
com.android.server.input.InputManagerService.nativeInjectInputEvent(Native Method)
com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:643)
com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:625)
android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:392)
android.os.Binder.execTransactInternal(Binder.java:1021)
android.os.Binder.execTransact(Binder.java:994)
我们重点查看几个方法:
- PhoneWindowManager.interceptKeyBeforeQueueing
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
......
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_BACK: {
if (down) {
interceptBackKeyDown();//调用interceptBackKeyDown
} else {
boolean handled = interceptBackKeyUp(event);//调用interceptBackKeyUp
// Don't pass back press to app if we've already handled it via long press
if (handled) {
result &= ~ACTION_PASS_TO_USER;
}
}
break;
}
......
- PhoneWindowManager.interceptBackKeyDown
private void interceptBackKeyDown() {
mLogger.count("key_back_down", 1);
// Reset back key state for long press
mBackKeyHandled = false;
if (hasLongPressOnBackBehavior()) {//长按back键
Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
}
}
- PhoneWindowManager.interceptBackKeyUp
// returns true if the key was handled and should not be passed to the user
private boolean interceptBackKeyUp(KeyEvent event) {
mLogger.count("key_back_up", 1);
// Cache handled state
boolean handled = mBackKeyHandled;
// Reset back long press state
cancelPendingBackKeyAction();
if (mHasFeatureWatch) {
TelecomManager telecomManager = getTelecommService();
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing back while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();//来电静音
// It should not prevent navigating away
return false;
} else if (
(mIncallBackBehavior & Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall()) {
// Otherwise, if "Back button ends call" is enabled,
// the Back button will hang up any current active call.
return telecomManager.endCall();//挂断来电电话
}
}
}
if (mAutofillManagerInternal != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL));
}
return handled;
}