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流程。



                        



      Android Recovery 模式学习