将解析后的参数保存到全局变量。
恢复出厂设置流程:
选择“设置”->“系统”->”系统还原”。
Main system 向 BCB 写入 “–wipe_data”;
Main system 重新启动 reboot(‘recovery’),进入 Recovery 模式;
get_args() 函数读取 BCB写入 “boot-recovery” 和 “–wipe_data”,获知需要擦除数据。
erase_volume() 执行格式化/data分区
同样格式化 /cache 分区
finish_recovery() 清除 BCB。然后重启进入 Main system。
OTA Recovery 升级流程:
主系统下载更新包到 /cache/some-filename.zip
主系统向 BCB 写入 “–update_package=/cache/some-filename.zip”
主系统重启进入 recovery
get_args() 向 BCB 写入 “boot-recovery” and “–update_package=…”
– 如此再下次异常重启后会重新进入 recovery 安装更新 –
nstall_package() 安装更新
finish_recovery() 完成安装,完成 recovery,擦除 BCB
– 如此之后系统重启将进入主系统 –
如果安装失败
7a. prompt_and_wait() 显示错误,并等待用户操作
7b. 用户重启进入主系统
3、恢复出厂设置流程 1.原生设置中响应 恢复出厂设置 功能,继而发出重置广播(Intent.ACTION_FACTORY_RESET) .
2.frameWork层 接收到此广播,根据广播所携带的参数执行Android层的Reset设定.
3.Android层执行到最后会将Reset配置信息写入 BCB 中,最终进入Recovery
Master Reset 的时序图:
Master Reset 流程分析如下
(一)java层的代码解析 1.framework接收 Reset 广播 在framework/base/core/res/AndroidManifest.xml 中声明 MasterClearReceiver .java静态接收 Reset 广播
1.
3.
5.
//这条广播表示 恢复出厂设置
6.
7.
9.
10.
11.
12.
13.
14.
15. 2.MasterClearReceiver .onReceive() 在 MasterClearReceiver .java 执行 onReceive() 方法
framework/base/services/core/java/com/android/server/MasterClearReceiver .java
1. public void onReceive(final Context context, final Intent intent) {
2. if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
3. if (!"google.com".equals(intent.getStringExtra("from"))) {
4. Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");
5. return;
6. }
7. }
8. if (Intent.ACTION_MASTER_CLEAR.equals(intent.getAction())) {
9. Slog.w(TAG, "The request uses the deprecated Intent#ACTION_MASTER_CLEAR, "
10. + "Intent#ACTION_FACTORY_RESET should be used instead.");
11. }
12. if (intent.hasExtra(Intent.EXTRA_FORCE_MASTER_CLEAR)) {
13. Slog.w(TAG, "The request uses the deprecated Intent#EXTRA_FORCE_MASTER_CLEAR, "
14. + "Intent#EXTRA_FORCE_FACTORY_RESET should be used instead.");
15. }
16.
17. final String factoryResetPackage = context
18. .getString(com.android.internal.R.string.config_factoryResetPackage);
//判断如果广播是Intent.ACTION_FACTORY_RESET,且factoryResetPackage不为空
19. if (Intent.ACTION_FACTORY_RESET.equals(intent.getAction())
20. && !TextUtils.isEmpty(factoryResetPackage)) {
21. intent.setPackage(factoryResetPackage).setComponent(null);
//重新将广播发出去
22. context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
23. return;
24. }
25. // 从广播中获取shutdown、reason、forceWipe、mWipeEsims等参数的值
//mWipeExternalStorage代表是否需要擦除外部储存的标志
// mWipeEsims代表是否需要擦除eSIM的标志
26. final boolean shutdown = intent.getBooleanExtra("shutdown", false);
27. final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
28. mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
29. mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
30. final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
31. || intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);
32.
33. Slog.w(TAG, "!!! FACTORY RESET !!!");
34. // The reboot call is blocking, so we need to do it on another thread.
// 创建一个新线程thr
35. Thread thr = new Thread("Reboot") {
36. @Override
37. public void run() {
38. try {
39. RecoverySystem
40. .rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims);
41. Log.wtf(TAG, "Still running after master clear?!");
42. } catch (IOException e) {
43. Slog.e(TAG, "Can't perform master clear/factory reset", e);
44. } catch (SecurityException e) {
45. Slog.e(TAG, "Can't perform master clear/factory reset", e);
46. }
47. }
48. };
49.
50. if (mWipeExternalStorage) {
51. // thr will be started at the end of this task.
52. new WipeDataTask(context, thr).execute();
53. } else {
54. thr.start();
55. }
56. }
查看MasterClearReceiver代码,onReceive接收到广播时,判断如果广播是Intent.ACTION_FACTORY_RESET,且factoryResetPackage不为空,就重新将广播发出去,return退出。之后会创建一个新线程thr,该线程内部会重启并擦除用户数据,但是thr线程并不会立刻执行,而是会判断是否需要擦除外置存储卡或者sim卡中的数据,如果需要则会创建WipeDataTask对象,该对象内部会调用StorageManager的wipeAdoptableDisks方法清除外置存储卡的数据。
在调用StorageManager的wipeAdoptableDisks清除完外置存储卡中的数据之后,会执行thr线程的start方法,触发run方法,调用RecoverySystem的rebootWipeUserData方法,
3.RecoverySystem.rebootWipeUserData() frameworks/base/core/java/android/os/RecoverySystem.java
RecoverySystem的rebootWipeUserData方法如下所示。
1. public static void rebootWipeUserData(Context context, boolean shutdown, String reason, boolean force, boolean wipeEuicc) throws IOException {
// 检查是否允许执行工厂重置,如果不允许则抛出SecurityException
3. UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
4. if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
5. throw new SecurityException("Wiping data is not allowed for this user.");
6. }
7. final ConditionVariable condition = new ConditionVariable();
8. //发送Intent.ACTION_MASTER_CLEAR_NOTIFICATION广播,并等待其完成
9. Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
10. intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
11. | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
12. context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,
13. android.Manifest.permission.MASTER_CLEAR,
14. new BroadcastReceiver() {
15. @Override
16. public void onReceive(Context context, Intent intent) {
17. condition.open();
18. }
19. }, null, 0, null, null);
20.
21. // Block until the ordered broadcast has completed.
22. condition.block();
23. //如果需要擦除eUICC数据,则调用wipeEuiccData()执行擦除操作。否则调用removeEuiccInvisibleSubs()
24. EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
25. if (wipeEuicc) {
26. wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
27. } else {
28. removeEuiccInvisibleSubs(context, euiccManager);
29. }
30.
31. String shutdownArg = null;
32. if (shutdown) {
33. shutdownArg = "--shutdown_after";
34. }
35.
36. String reasonArg = null;
37. if (!TextUtils.isEmpty(reason)) {
38. String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
39. reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
40. }
41.
42. final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
43. Log.i(TAG," RecoverySystem.java rebootWipeUserData");
44. bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
45. }
rebootWipeUserData方法执行过程中,会发送封装好的参数 --wipe_data,–locale,然后调用bootCommand方法。
4.RecoverySystem.bootCommand() BootCommand方法如下:
1. private static void bootCommand(Context context, String... args) throws IOException {
2. LOG_FILE.delete();
3. //构建command字符串,将args中的每个参数拼接并换行
4. StringBuilder command = new StringBuilder();
5. for (String arg : args) {
6. if (!TextUtils.isEmpty(arg)) {
7. command.append(arg);
8. command.append("\n");
9. }
10. }
11.
12. // Write the command into BCB (bootloader control block) and boot from
13. // there. Will not return unless failed.
14. RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
15. Log.i(TAG," RecoverySystem.java bootcommand");
16. rs.rebootRecoveryWithCommand(command.toString());
17.
18. throw new IOException("Reboot failed (no permissions?)");
19. }
这个方法会把传入的命令参数写入bootCommand中,bootCommand方法会进一步调用RecoverySystemService的rebootRecoveryWithCommand方法
5.RecoverySystemService.rebootRecoveryWithCommand() frameworks/base/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
RecoverySystemService的rebootRecoveryWithCommand方法如下:
1. public void rebootRecoveryWithCommand(String command) {
2. if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
3. synchronized (sRequestLock) {
//setupOrClearBcb将之前传递过来的参数写入BCB中
4. if (!setupOrClearBcb(true, command)) {
5. return;
6. }
7.
8. // Having set up the BCB, go ahead and reboot.
9. PowerManager pm = mInjector.getPowerManager();
10. pm.reboot(PowerManager.REBOOT_RECOVERY);
11. }
12. }
private boolean setupOrClearBcb(boolean isSetup, String command) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
//检查解密服务是否可用,如果不可用直接返回false
final boolean available = checkAndWaitForUncryptService();
if (!available) {
Slog.e(TAG, "uncrypt service is unavailable.");
return false;
}
// 根据isSetup参数决定是设置BCB(setup-bcb)还是清除BCB(clear-bcb)
if (isSetup) {
mInjector.systemPropertiesSet("ctl.start", "setup-bcb");
} else {
mInjector.systemPropertiesSet("ctl.start", "clear-bcb");
}
//通过socket与 uncrypt通信,将之前的参数发送给 uncrypt
// Connect to the uncrypt service socket.
UncryptSocket socket = mInjector.connectService();
if (socket == null) {
Slog.e(TAG, "Failed to connect to uncrypt socket");
return false;
}
try {
// Send the BCB commands if it's to setup BCB.
if (isSetup) {
socket.sendCommand(command);
}
// Read the status from the socket.
int status = socket.getPercentageUncrypted();
// Ack receipt of the status code. uncrypt waits for the ack so
// the socket won't be destroyed before we receive the code.
socket.sendAck();
//根据状态码判断设置/清除BCB是否成功,成功则返回true,失败返回false
if (status == 100) {
Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear")
+ " bcb successfully finished.");
} else {
// Error in /system/bin/uncrypt.
Slog.e(TAG, "uncrypt failed with status: " + status);
return false;
}
} catch (IOException e) {
Slog.e(TAG, "IOException when communicating with uncrypt:", e);
return false;
} finally {
socket.close();
}
return true;
}
uncrypt.cpp对程序启动的参数分别处理,当参数为“setup-bcb”时调用setup_bcb()方法
bootable/recovery/uncrypt/uncrypt.cpp
static bool setup_bcb(const int socket) {
// c5. receive message length
int length;
if (!android::base::ReadFully(socket, &length, 4)) {
PLOG(ERROR)
PLOG(ERROR)
if (android::base::StartsWith(option, "--wipe_package=")) {
std::string path = option.substr(strlen("--wipe_package="));
if (!android::base::ReadFileToString(path, &wipe_package)) {
PLOG(ERROR)
LOG(ERROR)
PLOG(ERROR)
android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
if (fd == -1) {
*err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
strerror(errno));
return false;
}
//通过lseek()将文件指针移动到offset偏移位置
if (lseek(fd, static_cast
*err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
strerror(errno));
return false;
}
//通过WriteFully()将p指向的缓冲区的数据写入fd文件描述符,大小为size个字节
if (!android::base::WriteFully(fd, p, size)) {
*err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
strerror(errno));
return false;
}
//通过fsync()刷新文件缓冲,将数据写入物理设备
if (fsync(fd) == -1) {
*err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
strerror(errno));
return false;
}
return true;
}
2. if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
3. throw new UnsupportedOperationException(
4. "Attempted userspace reboot on a device that doesn't support it");
5. }
6. try {
7. mService.reboot(false, reason, true);
8. } catch (RemoteException e) {
9. throw e.rethrowFromSystemServer();
10. }
11. }
//检查调用方是否有REBOOT和RECOVERY权限
2. mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
3. if (PowerManager.REBOOT_RECOVERY.equals(reason)
4. || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
5. mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
6. }
7.
8. final long ident = Binder.clearCallingIdentity();
9. try {
10. Log.i(TAG," powermanagerservice.java reboot start");
11. (HALT_MODE_REBOOT, confirm, reason, wait);
12. Log.i(TAG," powermanagerservice.java reboot--- shutdownOrRebootInternal");
13. } finally {
14. Binder.restoreCallingIdentity(ident);
15. }
16. }
17. private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,@Nullable final String reason, boolean wait) {
19. if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
20. if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
21. throw new UnsupportedOperationException(
22. "Attempted userspace reboot on a device that doesn't support it");
23. }
24. UserspaceRebootLogger.noteUserspaceRebootWasRequested();
25. }
26. if (mHandler == null || !mSystemReady) {
27. if (RescueParty.isAttemptingFactoryReset()) {
28. // If we're stuck in a really low-level reboot loop, and a
29. // rescue party is trying to prompt the user for a factory data
30. // reset, we must GET TO DA CHOPPA!
31. PowerManagerService.lowLevelReboot(reason);
32. } else {
33. throw new IllegalStateException("Too early to call shutdown() or reboot()");
34. }
35. }
36. //创建一个Runnable,在run()方法中根据haltMode的值来调用ShutdownThread的reboot()、 rebootSafeMode()或shutdown()方法执行重启或关机操作
37. Runnable runnable = new Runnable() {
38. @Override
39. public void run() {
40. synchronized (this) {
41. if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
42. ShutdownThread.rebootSafeMode(getUiContext(), confirm);
43. } else if (haltMode == HALT_MODE_REBOOT) {
44. //走到这里
45. ShutdownThread.reboot(getUiContext(), reason, confirm);
46. } else {
47. ShutdownThread.shutdown(getUiContext(), reason, confirm);
48. }
49. }
50. }
51. };
52.
53. // ShutdownThread must run on a looper capable of displaying the UI.
54. Message msg = Message.obtain(UiThread.getHandler(), runnable);
55. msg.setAsynchronous(true);
56. UiThread.getHandler().sendMessage(msg);
57.
58. // PowerManager.reboot() is documented not to return so just wait for the inevitable.
//PowerManager.reboot()被记录为不会返回,所以只需等待不可避免的结果。
59. if (wait) {
60. synchronized (runnable) {
61. while (true) {
62. try {
63. runnable.wait();
64. } catch (InterruptedException e) {
65. }
66. }
67. }
68. }
69. }
2. mReboot = true;
3. mRebootSafeMode = false;
4. mRebootHasProgressBar = false;
5. mReason = reason;
6. //走到这里
7. shutdownInner(context, confirm);
8. }
9. private static void shutdownInner(final Context context, boolean confirm) {
10. // ShutdownThread is called from many places, so best to verify here that the context passed
11. // in is themed.
12. context.assertRuntimeOverlayThemable();
13.
14. // ensure that only one thread is trying to power down.
15. // any additional calls are just returned
// 检查sIsStarted标志位,确保重启操作没有已经启动,防止重复调用
16. synchronized (sIsStartedGuard) {
17. if (sIsStarted) {
18. Log.d(TAG, "Request to shutdown already running, returning.");
19. return;
20. }
21. }
22. //根据longPressBehavior和mRebootSafeMode的值决定显示哪个确认对话框
23. final int longPressBehavior = context.getResources().getInteger(
24. com.android.internal.R.integer.config_longPressOnPowerBehavior);
25. final int resourceId = mRebootSafeMode
26. ? com.android.internal.R.string.reboot_safemode_confirm
27. : (longPressBehavior == 2
28. ? com.android.internal.R.string.shutdown_confirm_question
29. : com.android.internal.R.string.shutdown_confirm);
30.
31. Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
32. //如果confirm为true,则显示确认对话框
33. if (confirm) {
34. final CloseDialogReceiver closer = new CloseDialogReceiver(context);
35. if (sConfirmDialog != null) {
36. sConfirmDialog.dismiss();
37. }
38. sConfirmDialog = new AlertDialog.Builder(context)
39. .setTitle(mRebootSafeMode
40. ? com.android.internal.R.string.reboot_safemode_title
41. : com.android.internal.R.string.power_off)
42. .setMessage(resourceId)
43. .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
44. public void onClick(DialogInterface dialog, int which) {
45. beginShutdownSequence(context);
46. }
47. })
48. .setNegativeButton(com.android.internal.R.string.no, null)
49. .create();
50. closer.dialog = sConfirmDialog;
51. sConfirmDialog.setOnDismissListener(closer);
52. sConfirmDialog.getWindow().
setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
53. sConfirmDialog.show();
54. } else {// 如果confirm为false,则直接调用beginShutdownSequence启动关机序列
55. beginShutdownSequence(context);
56. }
57. }
2. synchronized (sIsStartedGuard) {
//检查sIsStarted标志位,确保关机序列没有已经启动,防止重复启动
3. if (sIsStarted) {
4. Log.d(TAG, "Shutdown sequence already running, returning.");
5. return;
6. }
7. sIsStarted = true;
8. }
9. //显示shutdownDialog对话框并获取Context
10. sInstance.mProgressDialog = showShutdownDialog(context);
11. sInstance.mContext = context;
//获取PowerManager实例,用于后续获取WakeLock
12. sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
13.
14. // make sure we never fall asleep again
15. sInstance.mCpuWakeLock = null;
16. try {
17. sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
18. PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
19. sInstance.mCpuWakeLock.setReferenceCounted(false);
20. sInstance.mCpuWakeLock.acquire();
21. } catch (SecurityException e) {
22. Log.w(TAG, "No permission to acquire wake lock", e);
23. sInstance.mCpuWakeLock = null;
24. }
25.
26. // also make sure the screen stays on for better user experience
//检查屏幕是否已开启,如果是则获取一个FULL_WAKE_LOCK权限的ScreenWakeLock,保持屏幕常亮
27. sInstance.mScreenWakeLock = null;
28. if (sInstance.mPowerManager.isScreenOn()) {
29. try {
30. sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
31. PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
32. sInstance.mScreenWakeLock.setReferenceCounted(false);
33. sInstance.mScreenWakeLock.acquire();
34. } catch (SecurityException e) {
35. Log.w(TAG, "No permission to acquire wake lock", e);
36. sInstance.mScreenWakeLock = null;
37. }
38. }
39.
40. if (SecurityLog.isLoggingEnabled()) {
41. SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
42. }
43.
44. // start the thread that initiates shutdown
//创建Handler并启动关机线程
45. sInstance.mHandler = new Handler() {
46. };
47. // 启动线程
48. sInstance.start();
49. }
2. TimingsTraceLog shutdownTimingLog = newTimingsLog();
3. shutdownTimingLog.traceBegin("SystemServerShutdown");
4. metricShutdownStart();
5. metricStarted(METRIC_SYSTEM_SERVER);
6.
7. BroadcastReceiver br = new BroadcastReceiver() {
8. @Override public void onReceive(Context context, Intent intent) {
9. // We don't allow apps to cancel this, so ignore the result.
10. actionDone();
11. }
12. };
13. ……
14. shutdownTimingLog.traceEnd(); // SystemServerShutdown
15. metricEnded(METRIC_SYSTEM_SERVER);
16. saveMetrics(mReboot, mReason);
17. // Remaining work will be done by init, including vold shutdown
// 重启前的准备工作结束, 开始重启
18. rebootOrShutdown(mContext, mReboot, mReason);
19. }
// 如果reboot为true,则先调用PowerManagerService.lowLevelReboot()尝试重启。如果重启失败,则将reason设置为null,并进行关机操作
2. if (reboot) {
3. Log.i(TAG, "Rebooting, reason: " + reason);
4. PowerManagerService.lowLevelReboot(reason);
5. Log.e(TAG, "Reboot failed, will attempt shutdown instead");
6. reason = null;
7. } else if (SHUTDOWN_VIBRATE_MS 0 && context != null) {
8. // vibrate before shutting down(关闭前振动)
9. Vibrator vibrator = new SystemVibrator(context);
10. try {
11. vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
12. } catch (Exception e) {
13. // Failure to vibrate shouldn't interrupt shutdown. Just log it.
//振动故障不应中断停机。只需记录即可。
14. Log.w(TAG, "Failed to vibrate during shutdown.", e);
15. }
16.
17. // vibrator is asynchronous so we need to wait to avoid shutting down too soon.(可控震源是异步的,所以我们需要等待以避免过早关闭。)
18. try {
19. Thread.sleep(SHUTDOWN_VIBRATE_MS);
20. } catch (InterruptedException unused) {
21. }
22. }
23. // Shutdown power
24. Log.i(TAG, "Performing low-level shutdown...");
25. PowerManagerService.lowLevelShutdown(reason);
26. }
//检查reason是否为空,如果是则设置为空字符串
2. if (reason == null) {
3. reason = "";
4. }
5.
6. // If the reason is "quiescent", it means that the boot process should proceed
7. // without turning on the screen/lights.
8. // The "quiescent" property is sticky, meaning that any number
9. // of subsequent reboots should honor the property until it is reset.
//如果reason为"quiescent"或以",quiescent"结尾,则设置sQuiescent标志位,并截取reason
10. if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
11. sQuiescent = true;
12. reason = "";
13. } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
14. sQuiescent = true;
15. reason = reason.substring(0,
16. reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
17. }
18. // 如果reason为REBOOT_RECOVERY或REBOOT_RECOVERY_UPDATE,则将reason设置为"recovery"
19. if (reason.equals(PowerManager.REBOOT_RECOVERY)
20. || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
21. // 走的这里
22. reason = "recovery";
23. }
24. // 如果sQuiescent被设置,则将",quiescent"追加到reason的末尾
25. if (sQuiescent) {
28. reason = reason + ",quiescent";
29. }
30. // 设置这个prop触发init进程重启进入recovery模式
31. SystemProperties.set("sys.powerctl", "reboot," + reason);
32. try {
33. Thread.sleep(20 * 1000L);
34. } catch (InterruptedException e) {
35. Thread.currentThread().interrupt();
36. }
37. Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
38. }