由于源码分析的代码量比较大,大部分博客网站的内容显示页面都比较窄,显示出来的效果都异常丑陋,所以您也可以直接查看 《 Thinking in Android 》 来阅读这边文章,希望这篇文章能帮你梳理清楚 “Android 关机流程”。
关键类 | 路径 |
---|---|
GlobalActions.java | frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java |
LegacyGlobalActions.java | frameworks/base/services/core/java/com/android/server/policy/LegacyGlobalActions.java |
PhoneWindowManager.java | frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java |
PowerManagerService.java | frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java |
ShutdownThread.java | frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java |
WindowManagerService.java | frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java |
Android 系统的关机流程是从用户按 power
键开始的,所有的按键处理都是通过 PhoneWindowManager.interceptKeyBeforeQueueing()
方法进行处理。
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
... ...
// Handle special keys.
switch (keyCode) {
... ...
case KeyEvent.KEYCODE_POWER: {
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter);
// 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) {
// down 为 true,代表按下 power 键,走 interceptPowerKeyDown() 方法
interceptPowerKeyDown(event, interactive);
} else {
interceptPowerKeyUp(event, interactive, canceled);
}
break;
}
... ...
}
}
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public class PhoneWindowManager implements WindowManagerPolicy {
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
... ...
// 截屏功能
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordPowerKeyTriggered = true;
mScreenshotChordPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
interceptRingerToggleChord();
}
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
telecomManager.silenceRinger(); // 如果来电时,按 Power 则静音
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
hungUp = telecomManager.endCall();
}
}
... ...
mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
|| mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
if (!mPowerKeyHandled) {
if (interactive) {
if (hasLongPressOnPowerBehavior()) {
if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
powerLongPress(); // 长按 Power 键,核心方法
} else {
... ...
}
}
} else {
wakeUpFromPowerKey(event.getDownTime());
if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
powerLongPress(); // 长按 Power 键,核心方法
} else {
... ...
}
mBeganFromNonInteractive = true;
} else {
... ...
}
}
}
}
}
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public class PhoneWindowManager implements WindowManagerPolicy {
private void powerLongPress() {
final int behavior = getResolvedLongPressOnPowerBehavior(); // 得到长按电源键的行为
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS: // 原生的会走这个 case
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
showGlobalActionsInternal(); // 调用 showGlobalActionsInternal() 方法
break;
... ...
}
}
}
powerLongPress()
有两个核心方法,getResolvedLongPressOnPowerBehavior()
和 showGlobalActionsInternal()
,我们分别看下。
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public class PhoneWindowManager implements WindowManagerPolicy {
int mLongPressOnPowerBehavior;
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
private int getResolvedLongPressOnPowerBehavior() {
if (FactoryTest.isLongPressOnPowerOffEnabled()) {
return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
}
return mLongPressOnPowerBehavior; // 返回 config_longPressOnPowerBehavior 的值
}
}
其实就是获取 config_longPressOnPowerBehavior
的值,这个值是什么?
// frameworks/base/core/res/res/values/config.xml
<!-- Control the behavior when the user long presses the power button.
0 - Nothing // 表示直接关机
1 - Global actions menu // 关机要显示 Global actions
2 - Power off (with confirmation) // 关机要弹出对话框再次确认
3 - Power off (without confirmation) // 关机不需要弹出对话框
4 - Go to voice assist
-->
<integer name="config_longPressOnPowerBehavior">1</integer> // 可设置默认值
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public class PhoneWindowManager implements WindowManagerPolicy {
void showGlobalActionsInternal() {
if (mGlobalActions == null) {
mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned()); // 弹出关机的对话框
// 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);
}
}
// frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java
class GlobalActions implements GlobalActionsProvider.GlobalActionsListener {
private LegacyGlobalActions mLegacyGlobalActions;
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
if (mGlobalActionsProvider != null && mGlobalActionsProvider.isGlobalActionsDisabled()) {
return;
}
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = deviceProvisioned;
mShowing = true;
if (mGlobalActionsAvailable) {
mHandler.postDelayed(mShowTimeout, 5000);
mGlobalActionsProvider.showGlobalActions();
} else {
// SysUI isn't alive, show legacy menu.
ensureLegacyCreated();
// 调用 LegacyGlobalActions.showDialog() 方法
mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
}
}
}
// frameworks/base/services/core/java/com/android/server/policy/LegacyGlobalActions.java
class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
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(); // 如果 Dialog 不为空,则创建
}
}
}
// frameworks/base/services/core/java/com/android/server/policy/LegacyGlobalActions.java
class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
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(); // 调用 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);
}
}
}
}
当点击 dialog
的 power off
时,会调用 PowerAction
的 onPress()
方法。
// frameworks/base/services/core/java/com/android/server/policy/PowerAction.java
public final class PowerAction extends SinglePressAction implements LongPressAction {
@Override
public void onPress() {
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown(false /* confirm */); // 调用 WindowManagerService.shutdown() 方法
}
}
// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
// 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() 方法
ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(),
PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
}
}
Android 关机的流程最终是通过 ShutdownThread
线程实现。
// frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
public final class ShutdownThread extends Thread {
public static void shutdown(final Context context, String reason, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
mReason = reason;
shutdownInner(context, confirm); // 内部调用 shutdownInner() 方法
}
}
// frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
public final class ShutdownThread extends Thread {
private static void shutdownInner(final Context context, boolean confirm) {
context.assertRuntimeOverlayThemable();
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Request to shutdown already running, returning.");
return;
}
}
// 获取用户长按 Power 键的处理行为
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();
}
// 创建关机 Dialog
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); // 执行 beginShutdownSequence() 方法,开始关机流程
}
})
.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);
}
}
}
// frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
public final class ShutdownThread extends Thread {
// static instance of this thread
private static final ShutdownThread sInstance = new ShutdownThread();
// 我们一般可以在这个方法里面添加关机动画
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
return;
}
sIsStarted = true;
}
sInstance.mProgressDialog = showShutdownDialog(context); // 显示关机进度对话框
sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
... ...
// // start the thread that initiates shutdown -- 启动关机线程 ,执行 Run 方法
sInstance.mHandler = new Handler() {
};
sInstance.start();
}
}
启动关机线程,执行 run()
;
// frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
public final class ShutdownThread extends Thread {
public void run() {
... ...
{
String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); // 保存关机的原因
}
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
... ...
// First send the high-level shut down broadcast.
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
// 发送关机广播
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, null, br, mHandler, 0, null, null);
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
... ...
if (mRebootHasProgressBar) {
sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
}
shutdownTimingLog.traceEnd(); // SendShutdownBroadcast -- 关机广播所用的时间
... ...
final IActivityManager am = IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
try {
am.shutdown(MAX_BROADCAST_TIME); // 关闭 ActivityManagerService 服务
} catch (RemoteException e) {
}
}
if (mRebootHasProgressBar) {
sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
}
shutdownTimingLog.traceEnd();// ShutdownActivityManager
metricEnded(METRIC_AM);
Log.i(TAG, "Shutting down package manager...");
shutdownTimingLog.traceBegin("ShutdownPackageManager");
metricStarted(METRIC_PM);
final PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package");
if (pm != null) {
pm.shutdown(); // 关闭 PackageManagerService 服务
}
... ... // 关闭一系列核心服务
saveMetrics(mReboot, mReason);
rebootOrShutdown(mContext, mReboot, mReason); // 执行 rebootOrShutdown() 方法
}
}
rebootOrShutdown()
方法决定 关机
还是 重启
。
// frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
public final class ShutdownThread extends Thread {
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
if (reboot) { // 判断是否重启
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
Log.e(TAG, "Reboot failed, will attempt shutdown instead");
reason = null;
} else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator(context);
try {
// 关机之前震动
vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
} catch (Exception e) {
// Failure to vibrate shouldn't interrupt shutdown. Just log it.
Log.w(TAG, "Failed to vibrate during shutdown.", e);
}
// vibrator is asynchronous so we need to wait to avoid shutting down too soon.
try {
Thread.sleep(SHUTDOWN_VIBRATE_MS);
} catch (InterruptedException unused) {
}
}
// Shutdown power
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown(reason);
}
}