一、总控室收到停工指令
假设安卓手机是一个大型智能工厂,PowerManager 就是工厂的总控室。当你按下电源键选择 "重启" 时,就像总控室接到了 "全厂停工后重新启动" 的命令。
java
// 总控室接到重启指令
public void reboot(String reason) {
// 通过对讲机通知底层控制中心
mService.reboot(false, reason, true);
}
二、各部门停工前的准备
总控室的工作人员(PowerManagerService)接到指令后,开始安排各部门有序停工:
java
// 总控室调度员处理重启请求
public void reboot(boolean confirm, String reason, boolean wait) {
// 检查是否有停工权限
mContext.enforceCallingOrSelfPermission(Manifest.permission.REBOOT, null);
// 启动停工协调流程
shutdownOrRebootInternal(false, confirm, reason, wait);
}
这里有三个关键参数:
shutdown=false:不是彻底停工(关机),而是重启confirm=false:不需要弹窗确认,直接执行wait=true:总控室需要等待所有部门完成停工
三、停工协调小组开始工作
总控室派出了专门的停工协调小组(ShutdownThread),这个小组负责指挥各部门按顺序停工:
java
// 停工协调小组核心工作流程
public static void reboot(Context context, String reason, boolean confirm) {
mReboot = true; // 标记为重启而非关机
mReason = reason;
// 开始内部停工协调
shutdownInner(context, confirm);
}
static void shutdownInner(Context context, boolean confirm) {
if (confirm) {
// 需要用户确认的情况(这里不涉及)
} else {
// 直接开始停工流程
beginShutdownSequence(context);
}
}
四、拉起停工进度显示
协调小组先拉起一个进度显示窗口,就像工厂门口的电子屏显示停工进度:
java
private static void beginShutdownSequence(Context context) {
// 创建进度显示对话框
ProgressDialog pd = new ProgressDialog(context);
if (PowerManager.REBOOT_RECOVERY.equals(mReason)) {
// 恢复模式重启的特殊显示
pd.setTitle("准备进入恢复模式");
} else {
// 常规重启显示
pd.setTitle("正在关机");
pd.setMessage("正在保存数据...");
}
pd.show(); // 显示进度窗口
// 确保工厂设备不会中途休眠
acquireWakeLocks();
// 启动协调小组工作线程
sInstance.start();
}
五、各部门按顺序停工
1. 发送停工广播(全厂通知)
java
public void run() {
// 设置停工属性(记录重启原因)
SystemProperties.set("sys.shutdown.requested", reason);
// 1. 发送停工广播(通知所有应用准备关闭)
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, null, br, mHandler, 0, null, null);
// 等待广播接收方回应,最多10秒
waitForBroadcastResponse();
这一步就像工厂广播通知所有车间 "10 分钟后开始停工,请做好数据保存"。
2. 应用管理部(AMS)停工
java
// 应用管理部停工流程
public boolean shutdown(int timeout) {
mShuttingDown = true;
// 停止接收新任务
updateEventDispatchingLocked();
// 各应用车间有序停工
timedout = mStackSupervisor.shutdownLocked(timeout);
// 关闭统计服务
mBatteryStatsService.shutdown();
return timedout;
}
AMS 就像工厂的生产调度部门,负责通知各个应用车间(App)停止接收新任务,保存当前工作状态。
3. 包管理部(PMS)停工
java
// 包管理部停工时保存应用使用记录
public void shutdown() {
mPackageUsage.write(true); // 强制保存数据
}
private void writeInternal() {
// 打开数据文件(/data/system/package-usage.list)
AtomicFile file = getFile();
FileOutputStream f = file.startWrite();
// 写入每个应用的最后使用时间
for (PackageParser.Package pkg : mPackages.values()) {
sb.append(pkg.packageName).append(' ').append(pkg.mLastPackageUsageTimeInMills).append('\n');
}
// 确保数据写入磁盘
file.finishWrite(f);
}
PMS 就像工厂的物资管理部门,停工前会记录每个车间(应用)的最后工作时间,以便重启后恢复状态。
4. 射频设备部(NFC / 蓝牙 / 4G)停工
java
// 关闭射频设备的专门线程
private void shutdownRadios(final int timeout) {
Thread t = new Thread() {
public void run() {
// 关闭NFC
if (nfc != null && nfc.getState() != STATE_OFF) nfc.disable(false);
// 关闭蓝牙
if (bluetooth != null && bluetooth.isEnabled()) bluetooth.disable(false);
// 关闭4G网络
if (phone != null && !phone.needMobileRadioShutdown())
phone.shutdownMobileRadios();
// 每隔500ms检查是否全部关闭,最多等12秒
waitForRadioShutdownComplete();
}
};
t.start();
t.join(timeout);
}
这一步就像关闭工厂里的无线通信设备(如对讲机、无线传感器),避免停工期间出现信号干扰。
5. 存储管理部(MountService)停工
java
// 存储管理部向底层存储系统发送停工命令
public void shutdown(IMountShutdownObserver observer) {
// 发送停工消息到工作线程
mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
}
// 工作线程处理停工消息
class MountServiceHandler extends Handler {
public void handleMessage(Message msg) {
if (msg.what == H_SHUTDOWN) {
// 向底层存储系统(vold)发送shutdown命令
boolean success = mConnector.execute("volume", "shutdown").isClassOk();
// 通知协调小组停工完成
obs.onShutDownComplete(success ? 0 : -1);
}
}
}
MountService 就像工厂的仓库管理员,停工前会确保所有文件存储操作完成,避免数据丢失。
六、总控室下达最终重启命令
当所有部门都完成停工准备后,总控室向工厂底层控制系统发送重启命令:
java
public static void rebootOrShutdown(Context context, boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
// 关键!向底层发送重启命令
PowerManagerService.lowLevelReboot(reason);
} else {
// 关机流程(这里不涉及)
}
}
public static void lowLevelReboot(String reason) {
if (reason.equals("recovery")) {
// 进入恢复模式的特殊命令
SystemProperties.set("ctl.start", "pre-recovery");
} else {
// 常规重启命令
SystemProperties.set("sys.powerctl", "reboot," + reason);
}
// 等待20秒让底层处理
Thread.sleep(20000);
}
sys.powerctl=reboot这条命令就像总控室按下了工厂的 "重启按钮",底层控制系统(Linux 内核)收到后会执行真正的硬件重启操作。
七、故事总结
整个重启流程可以概括为:
-
用户按下重启按钮,总控室(PowerManager)收到指令
-
停工协调小组(ShutdownThread)拉起进度显示,开始指挥各部门停工
-
依次关闭应用管理部(AMS)、包管理部(PMS)、射频设备、存储管理部
-
所有部门准备就绪后,总控室向底层发送重启命令(sys.powerctl=reboot)
-
底层系统执行硬件重启,就像工厂切断电源后重新启动
如果用 adb 命令模拟这个过程,就是:
plaintext
adb shell setprop sys.powerctl reboot
这条命令相当于直接在总控室按下了重启按钮,跳过了前面的部门停工流程(实际系统中仍会按流程执行)。
后续故事将继续讲解:当总控室下达重启命令后,底层工厂(Linux 内核)是如何执行具体的硬件重启操作的,以及不同重启原因(如恢复模式)会有什么不同处理。