今天记录下跟踪的Android O版本,power键按下流程,包括关机,锁屏,亮屏流程,没有太多新的扩展,只是做了源码流程的记录分析,若有异议,欢迎提出,下面开始
##power按键传递
对于按下power按键,在fwk中首先会传递到PhoneWindowManager#dispatchUnhandledKey
@Override
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
....
if (!interceptFallback(win, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
}
继续看PhoneWindowManager#interceptFallback
private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
win, fallbackEvent, policyFlags);
if (delayMillis == 0) {
return true;
}
}
return false;
}
可以看到上面的interceptKeyBeforeQueueing方法的返回值决定了是否由应用fwk自己处理power按键
由于Power Key默认都是由系统的Framework来响应,如果想让自己的应用捕获响应,可以在这里做具体的处理
##power按键处理
有了上面的分析,可以知道,power按键按下和抬起都是在interceptKeyBeforeQueueing中处理的
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
switch (keyCode) {
....
case KeyEvent.KEYCODE_POWER: {
// Any activity on the power button stops the accessibility shortcut
cancelPendingAccessibilityShortcutAction();
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
interceptPowerKeyDown(event, interactive);
} else {
interceptPowerKeyUp(event, interactive, canceled);
}
break;
}
....
}
return result;
}
###power按下流程
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
// Hold a wake lock until the power key is released.
if (!mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.acquire();
}
// Cancel multi-press detection timeout.
if (mPowerKeyPressCounter != 0) {
mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);
}
// 是否符合截屏条件
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordPowerKeyTriggered = true;
mScreenshotChordPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
}
// 停止tele相关的服务操作,比如打电话
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
}
}
if (!mPowerKeyHandled) {
// interactive表示当前是否亮屏
if (interactive) {
// When interactive, we're already awake.
// Wait for a long press or for the button to be released to decide what to do.
if (hasLongPressOnPowerBehavior()) { // 亮屏时候长按power按键
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
}
} else {
// 唤醒系统
wakeUpFromPowerKey(event.getDownTime());
// 下面处理灭屏幕时候长按power按键处理
if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
mBeganFromNonInteractive = true;
} else {
final int maxCount = getMaxMultiPressPowerCount();
if (maxCount <= 1) {
mPowerKeyHandled = true;
} else {
mBeganFromNonInteractive = true;
}
}
}
}
}
以上,需要说明的是,不同OEM厂商可能定制的power按钮事件不同,有的亮屏的时候,按下power就熄屏了,有的是在抬起时候熄屏的,这里我们就就以AOSP为参考来分析吧
可以看出,原生的代码,对于按下power只有满足指定条件截屏的处理,这里那如果在亮屏期间只是单纯按了power键,肯定是会锁屏的,所以这里的锁屏就是在抬起power键时候触发的
###亮屏抬起power按键
private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
final boolean handled = canceled || mPowerKeyHandled;
mScreenshotChordPowerKeyTriggered = false;
cancelPendingScreenshotChordAction();
cancelPendingPowerKeyAction();
if (!handled) {
// Figure out how to handle the key now that it has been released.
mPowerKeyPressCounter += 1;
final int maxCount = getMaxMultiPressPowerCount();
final long eventTime = event.getDownTime();
if (mPowerKeyPressCounter < maxCount) { // 处理频繁多次按下抬起power按键
// This could be a multi-press. Wait a little bit longer to confirm.
// Continue holding the wake lock.
Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,
interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout());
return;
}
// 这里会具体处理,当前来说就是灭屏操作
powerPress(eventTime, interactive, mPowerKeyPressCounter);
}
// Done. Reset our state.
finishPowerKeyPress();
}
private void powerPress(long eventTime, boolean interactive, int count) {
....
if (interactive && !mBeganFromNonInteractive) {
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
case SHORT_PRESS_POWER_GO_TO_SLEEP:
goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
break;
}
....
}
上述抬起行为和mShortPressOnPowerBehavior的配置有关,默认也是1,即SHORT_PRESS_POWER_GO_TO_SLEEP
mShortPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnPowerBehavior);
private void goToSleep(long eventTime, int reason, int flags) {
mRequestedOrGoingToSleep = true;
mPowerManager.goToSleep(eventTime, reason, flags);
}
@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
goToSleepInternal(eventTime, reason, flags, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
到此为止,具体处理锁屏的操作是PowerManagerService#goToSleep处理的
##按下power按键,亮屏处理
继续回到interceptPowerKeyDown方法,灭屏时候,按下power键触发亮屏的流程
private void wakeUpFromPowerKey(long eventTime) {
wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER");
}
private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {
final boolean theaterModeEnabled = isTheaterModeEnabled();
if (!wakeInTheaterMode && theaterModeEnabled) {
return false;
}
if (theaterModeEnabled) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0);
}
mPowerManager.wakeUp(wakeTime, reason);
return true;
}
##亮屏长按power弹出关机界面
上面在interceptPowerKeyDown方法中,可以看到,若当前亮屏,长按power,会发送MSG_POWER_LONG_PRESS消息
private class PolicyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
case MSG_POWER_LONG_PRESS:
powerLongPress();
break;
}
}
继续看powerLongPress方法
private void powerLongPress() {
final int behavior = getResolvedLongPressOnPowerBehavior();
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS: // 正常用户长按power的消息,会弹出确认框
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
showGlobalActionsInternal();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM: // LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM是对工厂模式测试的处理
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
break;
}
}
上面的分析可以看出,用户直接长按power按键的处理行为和behavior的值有关系
private int getResolvedLongPressOnPowerBehavior() {
if (FactoryTest.isLongPressOnPowerOffEnabled()) {
return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
}
return mLongPressOnPowerBehavior;
}
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
所以默认的处理行为和config_longPressOnPowerBehavior的配置有关系
/frameworks/base/core/res/res/values/config.xml中默认配置的是1,也即是LONG_PRESS_POWER_GLOBAL_ACTIONS消息会弹出确认框,
这里若是LONG_PRESS_POWER_SHUT_OFF和LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM会直接关机处理,所以如果有这样的需求,可以直接配置config_longPressOnPowerBehavior默认值为2即可
继续分析LONG_PRESS_POWER_GLOBAL_ACTIONS
PhoneWindowManager#showGlobalActionsInternal
void showGlobalActionsInternal() {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
if (mGlobalActions == null) {
mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
if (keyguardShowing) {
// since it took two seconds of long press to bring this up,
// poke the wake lock so they have some time to see the dialog.
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
}
GlobalActions#showDialog
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = deviceProvisioned;
mShowing = true;
if (mStatusBarConnected) {
mStatusBarInternal.showGlobalActions();
mHandler.postDelayed(mShowTimeout, 5000);
} else {
// SysUI isn't alive, show legacy menu.
ensureLegacyCreated();
mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
}
}
上述GlobalActions#showDialog方法中,不管if条件分支是哪一个,都会到LegacyGlobalActions#showDialog方法处理
LegacyGlobalActions#showDialog
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
// Show delayed, so that the dismiss of the previous dialog completes
mHandler.sendEmptyMessage(MESSAGE_SHOW);
} else {
handleShow();
}
}
可以看到,当长按power按钮,最终会通过handleShow方法显示关机确认弹框
private void handleShow() {
awakenIfNecessary();
mDialog = createDialog();
prepareDialog();
// If we only have 1 item and it's a simple press action, just do this action.
if (mAdapter.getCount() == 1
&& mAdapter.getItem(0) instanceof SinglePressAction
&& !(mAdapter.getItem(0) instanceof LongPressAction)) {
((SinglePressAction) mAdapter.getItem(0)).onPress();
} else {
if (mDialog != null) {
WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
attrs.setTitle("LegacyGlobalActions");
mDialog.getWindow().setAttributes(attrs);
mDialog.show();
mDialog.getWindow().getDecorView().setSystemUiVisibility(
View.STATUS_BAR_DISABLE_EXPAND);
}
}
}
上述createDialog是创建关机界面的布局显示,以及对应的按钮按键行为的,所以如果需要自己定制关机界面,就可以在这里来修改定制自己的界面和行为了
private GlobalActionsDialog createDialog() {
// Simple toggle style if there's no vibrator, otherwise use a tri-state
if (!mHasVibrator) {
mSilentModeAction = new SilentModeToggleAction();
} else {
mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
}
mAirplaneModeOn = new ToggleAction(
R.drawable.ic_lock_airplane_mode,
R.drawable.ic_lock_airplane_mode_off,
R.string.global_actions_toggle_airplane_mode,
R.string.global_actions_airplane_mode_on_status,
R.string.global_actions_airplane_mode_off_status) {
@Override
void onToggle(boolean on) {
if (mHasTelephony && Boolean.parseBoolean(
SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
mIsWaitingForEcmExit = true;
// Launch ECM exit dialog
Intent ecmDialogIntent =
new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(ecmDialogIntent);
} else {
changeAirplaneModeSystemSetting(on);
}
}
@Override
protected void changeStateFromPress(boolean buttonOn) {
if (!mHasTelephony) return;
// In ECM mode airplane state cannot be changed
if (!(Boolean.parseBoolean(
SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
mState = buttonOn ? State.TurningOn : State.TurningOff;
mAirplaneState = mState;
}
}
@Override
public boolean showDuringKeyguard() {
return true;
}
@Override
public boolean showBeforeProvisioning() {
return false;
}
};
onAirplaneModeChanged();
mItems = new ArrayList();
// 获取fwk中配置的默认显示的处理
String[] defaultActions = mContext.getResources().getStringArray(
com.android.internal.R.array.config_globalActionsList);
ArraySet addedKeys = new ArraySet();
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
// If we already have added this, don't add it again.
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
mItems.add(new PowerAction());
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
mItems.add(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
if (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
mItems.add(new BugReportAction());
}
} else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
if (mShowSilentToggle) {
mItems.add(mSilentModeAction);
}
} else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
addUsersToMenu(mItems);
}
} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
mItems.add(getSettingsAction());
} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
mItems.add(getLockdownAction());
} else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
mItems.add(getVoiceAssistAction());
} else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
mItems.add(getAssistAction());
} else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
mItems.add(new RestartAction());
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
}
// Add here so we don't add more than one.
addedKeys.add(actionKey);
}
if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
mItems.add(getEmergencyAction());
}
mAdapter = new MyAdapter();
AlertParams params = new AlertParams(mContext);
params.mAdapter = mAdapter;
params.mOnClickListener = this;
params.mForceInverseBackground = true;
GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.getListView().setItemsCanFocus(true);
dialog.getListView().setLongClickable(true);
dialog.getListView().setOnItemLongClickListener(
new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView parent, View view, int position,
long id) {
final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) {
return ((LongPressAction) action).onLongPress();
}
return false;
}
});
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
dialog.setOnDismissListener(this);
return dialog;
}
上述创建dialog的时候,默认显示fwk中 config_globalActionsList的配置,如下:
可以看到,上述对于关机按键的处理,由PowerAction来执行
private final class PowerAction extends SinglePressAction implements LongPressAction {
private PowerAction() {
super(com.android.internal.R.drawable.ic_lock_power_off,
R.string.global_action_power_off);
}
@Override
public boolean onLongPress() {
UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.rebootSafeMode(true);
return true;
}
return false;
}
@Override
public boolean showDuringKeyguard() {
return true;
}
@Override
public boolean showBeforeProvisioning() {
return true;
}
@Override
public void onPress() {
// 执行关机操作
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown(false /* confirm */);
}
}
好了,这一篇就到这,预备下一篇,定制关机界面。。。。_
专注技术分享,包括Java,python,AI人工智能,Android分享,不定期更新学习视频,欢迎关注