看源码主要是找到切入点,这个可以参考网上已有源码阅读资料,然后找到一个自己觉得可以下手的地方,我这里就从配置文件开始。

配置文件 config.xml

在阅读之前,需要注意配置文件

1
\frameworks\base\core\res\res\values\config.xml

我们只关注两个地方,长按和短按相关的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 长按 -->
<!-- Control the behavior when the user long presses the power button.
0 - Nothing
1 - Global actions menu
2 - Power off (with confirmation)
3 - Power off (without confirmation)
4 - Go to voice assist
-->
<integer name="config_longPressOnPowerBehavior">1</integer>

<!-- 短按 -->
<!-- Control the behavior when the user short presses the power button.
0 - Nothing
1 - Go to sleep (doze)
2 - Really go to sleep (don't doze)
3 - Really go to sleep and go home (don't doze)
4 - Go to home
5 - Dismiss IME if shown. Otherwise go to home
-->
<integer name="config_shortPressOnPowerBehavior">2</integer>

这里看下注释基本明白是做什么的了。
下来看源码。

阅读代码

PhoneWindowManager

这个关键类相信看源码的同学都是避免不了的,在这里还是只关注长按关机键相关的。
这个类位于:

1
\frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java

文件内搜索上边配置文件中长按 config_longPressOnPowerBehavior 关键字:

1
2
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_longPressOnPowerBehavior);

接着搜索 mLongPressOnPowerBehavior:

1
2
3
4
5
6
private int getResolvedLongPressOnPowerBehavior() {
if (FactoryTest.isLongPressOnPowerOffEnabled()) {
return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
}
return mLongPressOnPowerBehavior;
}

继续搜索这个方法调用的位置,然后你就会发现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private void powerLongPress() {
final int behavior = getResolvedLongPressOnPowerBehavior();
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
showGlobalActionsInternal();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case 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;
case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
final boolean keyguardActive = mKeyguardDelegate == null
? false
: mKeyguardDelegate.isShowing();
if (!keyguardActive) {
Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
if (mAllowStartActivityForLongPressOnPowerDuringSetup) {
mContext.startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
} else {
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
}
break;
}
}

看到这个方法,就会和前文中的配置文件对应起来了,各种不同的长按关机配置。
接着再找这个方法的调用。
主要有两个地方调用了。
第一:interceptPowerKeyDown 方法。看方法名就知道这个是拦截处理电源键按下事件的。
第二:内部类 PolicyHandler,他是个 Handler,可以看见在处理消息 MSG_POWER_LONG_PRESS 时候调用了 powerLongPress 方法。
到这里就要分成两条线去看了?不尽然,简单搜索下消息关键字 MSG_POWER_LONG_PRESS,就会发现如下代码:

1
2
3
4
5
6
7
8
9
10
if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
powerLongPress();
} else {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

......
}

这个逻辑是在 interceptPowerKeyDown 方法内,所以继续看这个方法即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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);
}
// Detect user pressing the power button in panic when an application has
// taken over the whole screen.
boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags),
isNavBarEmpty(mLastSystemUiFlags));
if (panic) {
mHandler.post(mHiddenNavPanic);
}
// Abort possibly stuck animations.
mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
// Latch power key state to detect screenshot chord.
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordPowerKeyTriggered = true;
mScreenshotChordPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
interceptRingerToggleChord();
}
// Stop ringing or end call if configured to do so when power is pressed.
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();
}
}
GestureLauncherService gestureService = LocalServices.getService(
GestureLauncherService.class);
boolean gesturedServiceIntercepted = false;
if (gestureService != null) {
gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
mTmpBoolean);
if (mTmpBoolean.value && mRequestedOrGoingToSleep) {
mCameraGestureTriggeredDuringGoingToSleep = true;
}
}
// Inform the StatusBar; but do not allow it to consume the event.
sendSystemKeyToStatusBarAsync(event.getKeyCode());
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
|| mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
if (!mPowerKeyHandled) {
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()) {
if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
powerLongPress();
} else {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
if (hasVeryLongPressOnPowerBehavior()) {
Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
longMsg.setAsynchronous(true);
mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
}
}
}
} else {
wakeUpFromPowerKey(event.getDownTime());
if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
powerLongPress();
} else {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
if (hasVeryLongPressOnPowerBehavior()) {
Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
longMsg.setAsynchronous(true);
mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
}
}
mBeganFromNonInteractive = true;
} else {
final int maxCount = getMaxMultiPressPowerCount();
if (maxCount <= 1) {
mPowerKeyHandled = true;
} else {
mBeganFromNonInteractive = true;
}
}
}
}
}

搜索这个方法调用, 方法: interceptKeyBeforeQueueing
方法: interceptFallback 方法名顾名思义就是备用,果然再向上搜就会发现,调用方法是: dispatchUnhandledKey,未处理的 key 事件,所以回头来看 interceptKeyBeforeQueueing,这个才是重点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
......
    // Handle special keys.
    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;
            }
    }
    ......
}
    ......
}

WindowManagerPolicy.WindowManagerFuncs

代码位于:\frameworks\base\services\core\java\com\android\server\policy\WindowManagerPolicy.java
回头再看方法 powerLongPress ,关机的代码是:

1
mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);

接着找 mWindowManagerFuncs ,发现他是 WindowManagerPolicy.WindowManagerFuncs 接口。接着找实现。

WindowManagerService

代码位于: \frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

经过搜索发现 WindowManagerService 实现了接口 WindowManagerPolicy.WindowManagerFuncs

1
2
3
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {}

方法 shutdown 如下:

1
2
3
4
5
6
7
// Called by window manager policy.  Not exposed externally.
@Override
public void shutdown(boolean confirm) {
// Pass in the UI context, since ShutdownThread requires it (to show UI).
ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(),
PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
}

ShutdownThread

接着 ShutdownThread,位于: \frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* Request a clean shutdown, waiting for subsystems to clean up their
* state etc.  Must be called from a Looper thread in which its UI
* is shown.
*
* @param context Context used to display the shutdown progress dialog. This must be a context
*                suitable for displaying UI (aka Themable).
* @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void shutdown(final Context context, String reason, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
mReason = reason;
shutdownInner(context, confirm);
}

private static void shutdownInner(final Context context, boolean confirm) {
// ShutdownThread is called from many places, so best to verify here that the context passed
// in is themed.
context.assertRuntimeOverlayThemable();
// ensure that only one thread is trying to power down.
// any additional calls are just returned
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Request to shutdown already running, returning.");
return;
}
}
final int longPressBehavior = context.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
final int resourceId = mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_confirm
: (longPressBehavior == 2
? com.android.internal.R.string.shutdown_confirm_question
: com.android.internal.R.string.shutdown_confirm);
Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
if (sConfirmDialog != null) {
sConfirmDialog.dismiss();
}
sConfirmDialog = new AlertDialog.Builder(context)
.setTitle(mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_title
: com.android.internal.R.string.power_off)
.setMessage(resourceId)
.setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
beginShutdownSequence(context);
}
})
.setNegativeButton(com.android.internal.R.string.no, null)
.create();
closer.dialog = sConfirmDialog;
sConfirmDialog.setOnDismissListener(closer); sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
sConfirmDialog.show();
} else {
beginShutdownSequence(context);
}
}

方法 shuadown 调用了方法 shutdownInner,内容大体就是根据是否有确认框,调用不同的实现。如果是有确认框,则通过AlertDialog 来实现弹框确认。否则调用方法 beginShutdownSequence
查看方法 beginShutdownSequence发现关机界面调用的是 showShutdownDialog,继续。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
private static ProgressDialog showShutdownDialog(Context context) {
// Throw up a system dialog to indicate the device is rebooting / shutting down.
ProgressDialog pd = new ProgressDialog(context);
// Path 1: Reboot to recovery for update
//   Condition: mReason startswith REBOOT_RECOVERY_UPDATE
//
//  Path 1a: uncrypt needed
//   Condition: if /cache/recovery/uncrypt_file exists but
//              /cache/recovery/block.map doesn't.
//   UI: determinate progress bar (mRebootHasProgressBar == True)
//
// * Path 1a is expected to be removed once the GmsCore shipped on
//   device always calls uncrypt prior to reboot.
//
//  Path 1b: uncrypt already done
//   UI: spinning circle only (no progress bar)
//
// Path 2: Reboot to recovery for factory reset
//   Condition: mReason == REBOOT_RECOVERY
//   UI: spinning circle only (no progress bar)
//
// Path 3: Regular reboot / shutdown
//   Condition: Otherwise
//   UI: spinning circle only (no progress bar)
// mReason could be "recovery-update" or "recovery-update,quiescent".
if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
// We need the progress bar if uncrypt will be invoked during the
// reboot, which might be time-consuming.
mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
&& !(RecoverySystem.BLOCK_MAP_FILE.exists());
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
if (mRebootHasProgressBar) {
pd.setMax(100);
pd.setProgress(0);
pd.setIndeterminate(false);
pd.setProgressNumberFormat(null);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_update_prepare));
} else {
if (showSysuiReboot()) {
return null;
}
pd.setIndeterminate(true);
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_update_reboot));
}
} else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
if (RescueParty.isAttemptingFactoryReset()) {
// We're not actually doing a factory reset yet; we're rebooting
// to ask the user if they'd like to reset, so give them a less
// scary dialog message.
pd.setTitle(context.getText(com.android.internal.R.string.power_off)); pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
} else {
// Factory reset path. Set the dialog message accordingly.
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_reset_message));
pd.setIndeterminate(true);
}
} else {
if (showSysuiReboot()) {
return null;
}
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
}
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
pd.show();
return pd;
}

通过上边代码可以发现,这个方法不止是用在关机时候,还在重启、刷机等多环境下使用。
我们这里只关心正常关机,所以直接下一步,方法 showSysuiReboot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 private static boolean showSysuiReboot() {
Log.d(TAG, "Attempting to use SysUI shutdown UI");
try {
StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
if (service.showShutdownUi(mReboot, mReason)) {
// Sysui will handle shutdown UI.
Log.d(TAG, "SysUI handling shutdown UI");
return true;
}
} catch (Exception e) {
// If anything went wrong, ignore it and use fallback ui
}
Log.d(TAG, "SysUI is unavailable");
return false;
}

通过方法发现这里调用了系统服务 StatusBarManagerInternal

StatusBarManagerService/StatusBarManagerInternal

位于:\frameworks\base\services\core\java\com\android\server\statusbar\StatusBarManagerService.java\frameworks\base\services\core\java\com\android\server\statusbar\StatusBarManagerInternal.java

StatusBarManagerInternal 的实现在 StatusBarManagerService 内部,是个匿名内部类实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Private API used by NotificationManagerService.
*/
private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
......

@Override
public boolean showShutdownUi(boolean isReboot, String reason) {
if (!mContext.getResources().getBoolean(R.bool.config_showSysuiShutdown)) {
return false;
}
if (mBar != null) {
try {
mBar.showShutdownUi(isReboot, reason);
return true;
} catch (RemoteException ex) {}
}
return false;
}
       
......
}

继续 mBar,呃,看到这里就有点蒙圈了????

1
private volatile IStatusBar mBar;

再往下,就会发现IStatusBar是个Binder : IStatusBar.aidl,到这里就需要了解下系统 Binder 的实现思路了,具体自己百度。

他的实现在 SystemUI 中。

CommandQueue

继续。下来切换到 **SystemUI**,找到 CommandQueue
位于: \frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\CommandQueue.java

1
public class CommandQueue extends IStatusBar.Stub {......}

CommandQueue中,

1
2
3
4
5
6
7
8
@Override
public void showShutdownUi(boolean isReboot, String reason) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_SHUTDOWN_UI);
mHandler.obtainMessage(MSG_SHOW_SHUTDOWN_UI, isReboot ? 1 : 0, 0, reason)
.sendToTarget();
}
}

Handler 处理消息 MSG_SHOW_SHUTDOWN_UI

1
2
3
4
5
case MSG_SHOW_SHUTDOWN_UI:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).handleShowShutdownUi(msg.arg1 != 0, (String) msg.obj);
}
break;

GlobalActionsComponent

位于: \frameworks\base\packages\SystemUI\src\com\android\systemui\globalactions\GlobalActionsComponent.java
这里的 callback 实现其实就是 GlobalActionsComponent 系统UI组件。方法如下:

1
2
3
4
@Override
public void handleShowShutdownUi(boolean isReboot, String reason) {
mExtension.get().showShutdownUi(isReboot, reason);
}

到这里,就可以发现,SystemUI 是通过插件模式实现的了。mExtension 的获取如下:

1
2
3
4
5
mExtension = Dependency.get(ExtensionController.class).newExtension(GlobalActions.class)
.withPlugin(GlobalActions.class)
.withDefault(() -> new GlobalActionsImpl(mContext))
.withCallback(this::onExtensionCallback)
.build();

GlobalActions

位于:\frameworks\base\packages\SystemUI\plugin\src\com\android\systemui\plugins\GlobalActions.java
系统默认GlobalActions 实现为 GlobalActionsImpl,在这个类里边就可以看到真正的关机界面了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Override
public void showShutdownUi(boolean isReboot, String reason) {
GradientDrawable background = new GradientDrawable(mContext);
background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
Dialog d = new Dialog(mContext,
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
// Window initialization
Window window = d.getWindow();
window.requestFeature(Window.FEATURE_NO_TITLE);
window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
// Inflate the decor view, so the attributes below are not overwritten by the theme.
window.getDecorView();
window.getAttributes().width = ViewGroup.LayoutParams.MATCH_PARENT;
window.getAttributes().height = ViewGroup.LayoutParams.MATCH_PARENT;
window.getAttributes().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
window.setBackgroundDrawable(background);
window.setWindowAnimations(R.style.Animation_Toast);

d.setContentView(R.layout.shutdown_dialog);
d.setCancelable(false);

int color = Utils.getColorAttr(mContext, com.android.systemui.R.attr.wallpaperTextColor);
boolean onKeyguard = mContext.getSystemService(
KeyguardManager.class).isKeyguardLocked();

ProgressBar bar = d.findViewById(R.id.progress);
bar.getIndeterminateDrawable().setTint(color);
TextView message = d.findViewById(R.id.text1);
message.setTextColor(color);
if (isReboot) message.setText(R.string.reboot_to_reset_message);

Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
GradientColors colors = Dependency.get(SysuiColorExtractor.class).getColors(
onKeyguard ? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
background.setColors(colors, false);
background.setScreenSize(displaySize.x, displaySize.y);

d.show();
}

到这里,关机逻辑基本就理通了。