Android O版本power按键锁屏亮屏流程

news/2024/11/8 14:21:22/

今天记录下跟踪的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 shortcutcancelPendingAccessibilityShortcutAction();result &= ~ACTION_PASS_TO_USER;isWakeKey = false; // wake-up will be handled separatelyif (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 {@Overridepublic 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 completesmHandler.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-stateif (!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) {@Overridevoid onToggle(boolean on) {if (mHasTelephony && Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {mIsWaitingForEcmExit = true;// Launch ECM exit dialogIntent ecmDialogIntent =new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);mContext.startActivity(ecmDialogIntent);} else {changeAirplaneModeSystemSetting(on);}}@Overrideprotected void changeStateFromPress(boolean buttonOn) {if (!mHasTelephony) return;// In ECM mode airplane state cannot be changedif (!(Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {mState = buttonOn ? State.TurningOn : State.TurningOff;mAirplaneState = mState;}}@Overridepublic boolean showDuringKeyguard() {return true;}@Overridepublic boolean showBeforeProvisioning() {return false;}};onAirplaneModeChanged();mItems = new ArrayList<Action>();// 获取fwk中配置的默认显示的处理String[] defaultActions = mContext.getResources().getStringArray(com.android.internal.R.array.config_globalActionsList);ArraySet<String> addedKeys = new ArraySet<String>();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() {@Overridepublic 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);}@Overridepublic boolean onLongPress() {UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {mWindowManagerFuncs.rebootSafeMode(true);return true;}return false;}@Overridepublic boolean showDuringKeyguard() {return true;}@Overridepublic boolean showBeforeProvisioning() {return true;}@Overridepublic void onPress() {// 执行关机操作// shutdown by making sure radio and power are handled accordingly.mWindowManagerFuncs.shutdown(false /* confirm */);}}

好了,这一篇就到这,预备下一篇,定制关机界面。。。。_

专注技术分享,包括Java,python,AI人工智能,Android分享,不定期更新学习视频,欢迎关注
在这里插入图片描述


http://www.ppmy.cn/news/484349.html

相关文章

prisma 结合 mongodb 查询地理空间坐标,实现 “附近的人”功能

前言&#xff1a;我们创建一个集合&#xff0c;添加测试数据&#xff0c;并执行 mongodb 的地理空间查询&#xff0c;返回需要的数据。 1、通过schema.prisma, 创建 store 集合 2、通过 prisma/client &#xff0c;插入 几条测试数据 // 构造测试数据createList: async () >…

如何修改Linux的锁屏时间,调整Kali Linux的锁屏时间

调整Kali Linux的锁屏时间 锁屏是保护隐私的一种重要机制。当用户不操作电脑一段时间后,系统会进入锁屏状态。用户需要输入口令,才能重新进入系统。避免因为操作人员离开电脑后,被其他人员利用现有帐号权限对电脑进行操作。Kali Linux的锁屏是和黑屏功能是关联的。默认设置是…

js实现一个简单的锁屏功能

//********* 锁屏DIV *************************** function LockScreen(tag,title,width,height,url) { if (tag) //锁屏 { var lockdiv document.getElementById("lockscreen"); if (lockdiv!null) { lockdiv.style…

Android WallpaperManager 同时设置桌面壁纸与锁屏的问题

最近项目中&#xff0c;需要给手机设置桌面壁纸与锁屏。 大家都知道使用 WallpaperManager.setStream()方法就好 RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStream(InputStream bitmapData, Rect visibleCropHint,boolean allowBackup, …

使用CiLocks绕过Android的锁屏功能

关于CiLocks CiLocks是一款功能强大的Android渗透测试工具&#xff0c;在该工具的帮助下&#xff0c;研究人员可以轻松绕过Android设备的锁屏保护。 功能介绍 爆破四位PIN码&#xff1b; 爆破六位PIN码&#xff1b; 使用字典爆破锁屏密码&#xff1b; 绕过锁屏保护&#xff0…

Android:获取当前的锁屏壁纸或桌面壁纸

使用WallpaperManager类 FLAG_LOCK为锁屏壁纸 FLAG_SYSTEM为桌面壁纸 //使用WallpaperManager类TargetApi(Build.VERSION_CODES.N)private Bitmap getLockWallpaper(){WallpaperManager wallpaperManager WallpaperManager.getInstance(mContext);//获取WallpaperManager实例…

银河麒麟liunux下的屏幕保护与锁屏设置

windows中有屏幕保护程序&#xff0c;在人离开的一段时间后可以开启屏幕保护&#xff0c;而且还可以设置密码&#xff0c;非常有利于短时间离开电脑时的保护隐私。 在linux中其实也有类似的功能&#xff0c;只不过一开始以为要安装termsaver什么的&#xff0c;还要用xset命令等…

linux系统之屏保与锁屏命令

linux系统之屏保与锁屏命令。 一、屏保命令。 #显示屏保但不锁定&#xff0c;移动鼠标或敲击键盘后自动消失 gnome-screensaver-command -a#显示并锁定&#xff0c;移动鼠标或敲击键盘弹出密码框 gnome-screensaver-command -l二、锁屏命令。 #列出当前所有的会话。这是默认…