Android 恢复出厂设置基本流程
(1)遥控器/按键板后门键触发,或者应用里面从系统设置里面恢复出厂选项也可触发; // 后面以系统设置的应用触发为例
(2)选择恢复出厂设置之后,就会发送广播“android.intent.action.MASTER_CLEAR” ;// framework/base/core/res/AndroidManifest.xml
(3)MasterClearReceiver 捕获广播 ,并进行android 层的相关处理最后重启 ;
(4)往 /cache/recovery/command 文件中写入命令字段;
(5)重启系统;
recovery 进入方式
(1) 通过读取 /cache 分区中文件 /cache/recovery/command 内容进入
(2)通过按键操作进入 (G1 通过同时按 HOME 和 挂断键)
进入recovery 的条件
(1)
blob 必须能从 recovery 分区中装载内核和文件系统
(2
)flash 必须有 cache 分区 和 recovery 分区
(3
)必须编译提供 recovery.img 烧录到 recovery 分区
Android 的处理流程
广播接收
framework/base/core/res/AndroidManifest.xml
1. <receiver android:name="com.android.server.MasterClearReceiver"
2. android:permission="android.permission.MASTER_CLEAR">
3. <intent-filter
4. android:priority="100" >
5. <!-- For Checkin, Settings, etc.: action=MASTER_CLEAR -->
6. <action android:name="android.intent.action.MASTER_CLEAR" />
7.
8. <!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR -->
9. <action android:name="com.google.android.c2dm.intent.RECEIVE" />
10. <category android:name="android.intent.category.MASTER_CLEAR" />
11. </intent-filter>
12. </receiver>
MasterClearReceiver接收广播android.intent.action.MASTER_CLEAR,创建一个县城作一下处理
1. Thread thr = new Thread("Reboot") {
2. @Override
3. public void run() {
4. try {
5. RecoverySystem.rebootWipeUserData(context, shutdown, reason);
6. "Still running after master clear?!");
7. catch (IOException e) {
8. "Can't perform master clear/factory reset", e);
9. catch (SecurityException e) {
10. "Can't perform master clear/factory reset", e);
11. }
12. }
13. };
14. thr.start();
RecoverySystem
1. /**
2. * Reboots the device and wipes the user data and cache
3. * partitions. This is sometimes called a "factory reset", which
4. * is something of a misnomer because the system partition is not
5. * restored to its factory state. Requires the
6. * {@link android.Manifest.permission#REBOOT} permission.
7. *
8. * @param context the Context to use
9. * @param shutdown if true, the device will be powered down after
10. * the wipe completes, rather than being rebooted
11. * back to the regular system.
12. *
13. * @throws IOException if writing the recovery command file
14. * fails, or if the reboot itself fails.
15. * @throws SecurityException if the current user is not allowed to wipe data.
16. *
17. * @hide
18. */
19. public static void rebootWipeUserData(Context context, boolean shutdown, String reason)
20. throws IOException {
21. UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
22. if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
23. throw new SecurityException("Wiping data is not allowed for this user.");
24. }
25. final ConditionVariable condition = new ConditionVariable();
26.
27. new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
28. intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
29. context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
30. android.Manifest.permission.MASTER_CLEAR,
31. new BroadcastReceiver() {
32. @Override
33. public void onReceive(Context context, Intent intent) {
34. condition.open();
35. }
36. null, 0, null, null);
37.
38. // Block until the ordered broadcast has completed.
39. condition.block();
40.
41. null;
42. if (shutdown) {
43. "--shutdown_after";
44. }
45.
46. null;
47. if (!TextUtils.isEmpty(reason)) {
48. "--reason=" + sanitizeArg(reason);
49. }
50.
51. final String localeArg = "--locale=" + Locale.getDefault().toString();
52. "--wipe_data", reasonArg, localeArg);
53. }
我们可以注意到在启动bootCommand传递命令时,封装参数 --wipe_data , --locale , 这些命令我们可以在查看recovery log ( /cache/recovery/*.log )信息时看到
“Command: "/sbin/recovery" "--wipe_data" "--locale=zh_CN" ,其实这应该也就是bootCommand 执行的命令
1. /**
2. * Reboot into the recovery system with the supplied argument.
3. * @param args to pass to the recovery utility.
4. * @throws IOException if something goes wrong.
5. */
6. private static void bootCommand(Context context, String... args) throws IOException {
7. // In case we need it
8. // In case it's not writable
9. LOG_FILE.delete();
10.
11. new FileWriter(COMMAND_FILE);
12. try {
13. for (String arg : args) {
14. if (!TextUtils.isEmpty(arg)) {
15. // MStar Android Patch Begin
16. String cmd = arg;
17. null;
18. null;
19. if (cmd.startsWith("--update_package")) {
20. 17, 23);
21. if (cmd.equals("/cache")) {
22. "--uuid=mstar-cache");
23. "\n");
24. "--label=mstar-cache");
25. "\n");
26. else {
27. 17, 28);
28. if (cmd.equals("/mnt/usb/sd")) {
29. 17, 30);
30. "--uuid=" + getVolumeUUID(cmd).toString();
31. "--label=" + getVolumeLabel(cmd).toString();
32. command.write(uuid);
33. "\n");
34. command.write(label);
35. "\n");
36. else {
37. if (cmd.equals("/mnt/sdcard")) {
38. "--uuid=" + getVolumeUUID(cmd).toString();
39. "--label=" + getVolumeLabel(cmd).toString();
40. command.write(uuid);
41. "\n");
42. command.write(label);
43. "\n");
44. else {
45. 17, 32);
46. if (cmd.equals("/mnt/usb/mmcblk")) {
47. 17, 35);
48. "--uuid=" + getVolumeUUID(cmd).toString();
49. "--label=" + getVolumeLabel(cmd).toString();
50. command.write(uuid);
51. "\n");
52. command.write(label);
53. "\n");
54. }
55. }
56. }
57. }
58. }
59. // MStar Android Patch End
60. command.write(arg);
61. "\n");
62. }
63. }
64. finally {
65. command.close();
66. }
67.
68. // Having written the command file, go ahead and reboot
69. PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
70. pm.reboot(PowerManager.REBOOT_RECOVERY);
71.
72. throw new IOException("Reboot failed (no permissions?)");
73. }
从以上代码分析,bootCommand 主要工作就是重启进入recovery,此处可以看到COMMAND_FILE 就是文件 “ /cache/recovery/command " , 将上面封装的参数信息
写入改文件,待重启之后读取该文件时进入recovery模式,另外我们看到写完文件之后,调用PowerManager 来reboot,注意参数PowerManager.REBOOT_RECOVERY
1. // Having written the command file, go ahead and reboot
2. PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
3. pm.reboot(PowerManager.REBOOT_RECOVERY);
4.
5. /**
6. * Reboot the device. Will not return if the reboot is successful.
7. * <p>
8. * Requires the {@link android.Manifest.permission#REBOOT} permission.
9. * </p>
10. *
11. * @param reason code to pass to the kernel (e.g., "recovery") to
12. * request special boot modes, or null.
13. */
14. public void reboot(String reason) {
15. try {
16. false, reason, true);
17. catch (RemoteException e) {
18. }
19. }
最后又进入PowerManagerService 的reboot函数
1. /**
2. * Reboots the device.
3. *
4. * @param confirm If true, shows a reboot confirmation dialog.
5. * @param reason The reason for the reboot, or null if none.
6. * @param wait If true, this call waits for the reboot to complete and does not return.
7. */
8. @Override // Binder call
9. public void reboot(boolean confirm, String reason, boolean wait) {
10. null);
11. if (PowerManager.REBOOT_RECOVERY.equals(reason)) {
12. null);
13. }
14.
15. final long ident = Binder.clearCallingIdentity();
16. try {
17. false, confirm, reason, wait);
18. finally {
19. Binder.restoreCallingIdentity(ident);
20. }
21. }
接着进入shutdownOrRebootInternal
1. private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
2. final String reason, boolean wait) {
3. if (mHandler == null || !mSystemReady) {
4. throw new IllegalStateException("Too early to call shutdown() or reboot()");
5. }
6.
7. new Runnable() {
8. @Override
9. public void run() {
10. synchronized (this) {
11. if (shutdown) {
12. ShutdownThread.shutdown(mContext, confirm);
13. else {
14. ShutdownThread.reboot(mContext, reason, confirm);
15. }
16. }
17. }
18. };
19.
20. // ShutdownThread must run on a looper capable of displaying the UI.
21. Message msg = Message.obtain(mHandler, runnable);
22. true);
23. mHandler.sendMessage(msg);
24.
25. // PowerManager.reboot() is documented not to return so just wait for the inevitable.
26. if (wait) {
27. synchronized (runnable) {
28. while (true) {
29. try {
30. runnable.wait();
31. catch (InterruptedException e) {
32. }
33. }
34. }
35. }
36. }
ShutdownThread来负责重启动作
1. /**
2. * Request a clean shutdown, waiting for subsystems to clean up their
3. * state etc. Must be called from a Looper thread in which its UI
4. * is shown.
5. *
6. * @param context Context used to display the shutdown progress dialog.
7. * @param reason code to pass to the kernel (e.g. "recovery"), or null.
8. * @param confirm true if user confirmation is needed before shutting down.
9. */
10. public static void reboot(final Context context, String reason, boolean confirm) {
11. true;
12. false;
13. mRebootReason = reason;
14. shutdownInner(context, confirm);
15. }
16.
17. static void shutdownInner(final Context context, boolean confirm) {
18. // ensure that only one thread is trying to power down.
19. // any additional calls are just returned
20. synchronized (sIsStartedGuard) {
21. if (sIsStarted) {
22. "Request to shutdown already running, returning.");
23. return;
24. }
25. }
26.
27. final int longPressBehavior = context.getResources().getInteger(
28. com.android.internal.R.integer.config_longPressOnPowerBehavior);
29. final int resourceId = mRebootSafeMode
30. ? com.android.internal.R.string.reboot_safemode_confirm
31. 2
32. ? com.android.internal.R.string.shutdown_confirm_question
33. : com.android.internal.R.string.shutdown_confirm);
34.
35. "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
36.
37. if (confirm) {
38. final CloseDialogReceiver closer = new CloseDialogReceiver(context);
39. if (sConfirmDialog != null) {
40. sConfirmDialog.dismiss();
41. }
42. new AlertDialog.Builder(context)
43. .setTitle(mRebootSafeMode
44. ? com.android.internal.R.string.reboot_safemode_title
45. : com.android.internal.R.string.power_off)
46. .setMessage(resourceId)
47. new DialogInterface.OnClickListener() {
48. public void onClick(DialogInterface dialog, int which) {
49. beginShutdownSequence(context);
50. }
51. })
52. null)
53. .create();
54. closer.dialog = sConfirmDialog;
55. sConfirmDialog.setOnDismissListener(closer);
56. sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
57. sConfirmDialog.show();
58. else {
59. beginShutdownSequence(context);
60. }
61. }
beginShutdownSequence进入主要的关机流程, 接着启动ShutdownThread.run() , 发送光机广播,关闭核心服务,最后进入rebootOrShutdown重启。
recovery进入流程
进入recovery有几种途径:
(1)进入recovery前先写misc分区,重启时发现变化就直接进入recovery模式;
(2)写文件 /cache/recovery/command 文件,重启时进去recovery模式; // 此种模式暂未找到启动recovery的地方,只是在启动recovery后有看到读
// 取/cache/recovery/command 文件数据再做后续操作
MISC分区内容
Bootloader Control Block(BCB) 存放recovery bootloader message,结构如下:
struct bootloader_message {
char command[32];
char status[32];
char recovery[768];
// The 'recovery' field used to be 1024 bytes. It has only ever
// been used to store the recovery command line, so 768 bytes
// should be plenty. We carve off the last 256 bytes to store the
// stage string (for multistage packages) and possible future
// expansion.
char stage[32];
char reserved[224];
};
command可以有以下两个值
“boot-recovery”:表示recovery正在进行或者指示bootloader应该进入recovery mode
“update--hboot/radio”:标志bootloader更新fireware
recovery内容
“recovery\n
<recovery command>\n
<recovery command>\n”其中 recovery command为CACHE:/recovery/command命令
Recovery Case
Factory reset(恢复出厂设置)
1. 用户选择“恢复出厂设置”
2. 设置系统将“--wipe_data”命令写入 /cache/recovery/command
3. 系统重启,并进入recovery模式 (sbin/recovery or /system/bin/recovery)
4. recovery get_args() 将“boot-recovery”和“--wipe_data”写入BCB
5. erase_root 格式化DATA 分区
6. erase_root 格式化CACHE 分区
7. finish_recovery 擦除BCB分区
8. 重启系统
OTA INSTALL (OTA升级)
1. 升级系统系在OTA包包/cache/some-filename.zip
2. 升级系统写入recovery命令 “--update_package=CACHE:some-filename.zip”
3. 重启系统,进入recovery模式
4. get_args()将“boot-recovery”和“--wipe_packkage=...”写入BCB
5. install_package 做升级
6. finish_recovery() 擦除BCB
7. **如果安装包失败**prompt_and_wait()等待用户操作,选择ALT+S或者ALT+W升级或回复出厂设置
8. main() 里面调用maybe_install_firmware_update()
1.如果包里含有hboot/radio的fireware则继续,否则返回
2.将"boot-recovery"和"--wipe_cache"写入BCB
3.将fireware image写入cache分区
4.将"update-readio/hboot"和“--wipe_date”写入BCB
5.重启系统
6.bootloader自身更新fireware
7.bootloader将"boot-recovery"写入BCB
8.erase_root擦除CACHE分区
9.清除BCB
9. main 调用reboot重启系统
Recovery代码位置:bootable/recovery/ ,主文件recovery.cpp
后续再分析 recovery流程。