Android6.0 Reset恢复出厂设置流程分析

原文地址:http://blog.csdn.net/otaku_627/article/details/53899346

点击Settings应用中的恢复出厂设置按钮后流程分析:

先使用grep命令搜索"恢复出厂设置"字符串,找到相应的布局文件:

packages/apps/Settings/res/xml/privacy_settings.xml

[html]  view plain  copy
  1. <PreferenceScreen  
  2.     android:key="factory_reset"  
  3.     android:title="@string/master_clear_title"  
  4.     settings:keywords="@string/keywords_factory_data_reset"  
  5.     android:fragment="com.android.settings.MasterClear" />  
在这个节点下我们可以看到这么一句话:

[html]  view plain  copy
  1. android:fragment="com.android.settings.MasterClear"  
这句话表示当点击"恢复出厂设置"这个item后,会直接跳转到MasterClear.java这个Fragment子类内:
先来看onCreateView()方法,这个方法是来加载布局文件的:

[java]  view plain  copy
  1. @Override  
  2. public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  3.         Bundle savedInstanceState) {  
  4.     if (!Process.myUserHandle().isOwner()  
  5.             || UserManager.get(getActivity()).hasUserRestriction(  
  6.             UserManager.DISALLOW_FACTORY_RESET)) {  
  7.         return inflater.inflate(R.layout.master_clear_disallowed_screen, null);  
  8.     }  
  9.   
  10.     mContentView = inflater.inflate(R.layout.master_clear, null);  
  11.   
  12.     establishInitialState();  
  13.     return mContentView;  
  14. }  
判断用户是否具有"恢复出厂设置"的权限,根据判断结果来加载布局;由于分析的是"恢复出厂设置"流程,所有直接默认它加载的是master_clear.xml布局。
接下来再来看establishInitialState()方法,这个方法主要是作为初始化布局控件并未其设置相应的点击事件的作用;

[java]  view plain  copy
  1. /** 
  2.  * In its initial state, the activity presents a button for the user to 
  3.  * click in order to initiate a confirmation sequence.  This method is 
  4.  * called from various other points in the code to reset the activity to 
  5.  * this base state. 
  6.  * 
  7.  * <p>Reinflating views from resources is expensive and prevents us from 
  8.  * caching widget pointers, so we use a single-inflate pattern:  we lazy- 
  9.  * inflate each view, caching all of the widget pointers we'll need at the 
  10.  * time, then simply reuse the inflated views directly whenever we need 
  11.  * to change contents. 
  12.  */  
  13. private void establishInitialState() {  
  14.     mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_master_clear);  
  15.     mInitiateButton.setOnClickListener(mInitiateListener);  
  16.     mExternalStorageContainer = mContentView.findViewById(R.id.erase_external_container);  
  17.     mExternalStorage = (CheckBox) mContentView.findViewById(R.id.erase_external);  
  18.   
  19.     /* 
  20.      * If the external storage is emulated, it will be erased with a factory 
  21.      * reset at any rate. There is no need to have a separate option until 
  22.      * we have a factory reset that only erases some directories and not 
  23.      * others. Likewise, if it's non-removable storage, it could potentially have been 
  24.      * encrypted, and will also need to be wiped. 
  25.      */  
  26.     boolean isExtStorageEmulated = false;  
  27.     StorageHelpUtil mStorageHelpUtil = new StorageHelpUtil();  
  28.     if (mStorageHelpUtil.getSdCardPath(getActivity()) != null) {  
  29.         isExtStorageEmulated = true;  
  30.     }  
  31.     if (isExtStorageEmulated) {  
  32.         mExternalStorageContainer.setVisibility(View.VISIBLE);  
  33.   
  34.         final View externalOption = mContentView.findViewById(R.id.erase_external_option_text);  
  35.         externalOption.setVisibility(View.VISIBLE);  
  36.   
  37.         final View externalAlsoErased = mContentView.findViewById(R.id.also_erases_external);  
  38.         externalAlsoErased.setVisibility(View.VISIBLE);  
  39.   
  40.         // If it's not emulated, it is on a separate partition but it means we're doing  
  41.         // a force wipe due to encryption.  
  42.         mExternalStorage.setChecked(!isExtStorageEmulated);  
  43.     } else {  
  44.             mExternalStorageContainer.setVisibility(View.GONE);  
  45.             final View externalOption = mContentView.findViewById(R.id.erase_external_option_text);  
  46.             externalOption.setVisibility(View.GONE);  
  47.         mExternalStorageContainer.setOnClickListener(new View.OnClickListener() {  
  48.   
  49.             @Override  
  50.             public void onClick(View v) {  
  51.                 mExternalStorage.toggle();  
  52.             }  
  53.         });  
  54.     }  
  55.   
  56.     final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);  
  57.     loadAccountList(um);  
  58.     StringBuffer contentDescription = new StringBuffer();  
  59.     View masterClearContainer = mContentView.findViewById(R.id.master_clear_container);  
  60.     getContentDescription(masterClearContainer, contentDescription);  
  61.     masterClearContainer.setContentDescription(contentDescription);  
  62. }  
1、初始化"恢复出厂设置"按钮并未其设置点击事件监听mInitiateListener;
2、判断是否插入SDcard,如果插入则显示是否清空SDcard的提醒消息,实例化选择框CheckBox并设置点击事件;
3、加载用户列表,loadAccountList();
4、实例化提醒信息区域,并显示提醒信息到界面上;

接下来再来看mInitiateListener监听器:

[java]  view plain  copy
  1. /** 
  2.  * If the user clicks to begin the reset sequence, we next require a 
  3.  * keyguard confirmation if the user has currently enabled one.  If there 
  4.  * is no keyguard available, we simply go to the final confirmation prompt. 
  5.  */  
  6. private final Button.OnClickListener mInitiateListener = new Button.OnClickListener() {  
  7.   
  8.     public void onClick(View v) {  
  9.         if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) {  
  10.             Intent intent = new Intent("android.settings.PASSWORD_MANAGER");  
  11.             startActivityForResult(intent,PASSWORD_MANAGER);  
  12.         }  
  13.     }  
  14. };  
startActivityForResult()方法进入密码界面,并返回结果,直接来看onActivityResult()方法:

[java]  view plain  copy
  1. @Override  
  2. public void onActivityResult(int requestCode, int resultCode, Intent data) {  
  3.     super.onActivityResult(requestCode, resultCode, data);  
  4.   
  5.     if (requestCode != KEYGUARD_REQUEST && requestCode != PASSWORD_MANAGER) {  
  6.         return;  
  7.     } else if (requestCode == PASSWORD_MANAGER) {  
  8.          if (resultCode == Activity.RESULT_OK) {  
  9.              validatePassword = data.getExtras().getBoolean("password");  
  10.              if (validatePassword) {  
  11.                 showFinalConfirmation();  
  12.              }  
  13.          }  
  14.          return;  
  15.     }  
  16.   
  17.     // If the user entered a valid keyguard trace, present the final  
  18.     // confirmation prompt; otherwise, go back to the initial state.  
  19.     if (resultCode == Activity.RESULT_OK) {  
  20.         showFinalConfirmation();  
  21.     } else {  
  22.         establishInitialState();  
  23.     }  
  24. }  
如果密码确认,调用showFinalConfirmation()方法:
[java]  view plain  copy
  1. private void showFinalConfirmation() {  
  2.     Bundle args = new Bundle();  
  3.     args.putBoolean(ERASE_EXTERNAL_EXTRA, mExternalStorage.isChecked());  
  4.     ((SettingsActivity) getActivity()).startPreferencePanel(MasterClearConfirm.class.getName(),  
  5.             args, R.string.master_clear_confirm_title, nullnull0);  
  6. }  
进入MasterClearConfirm.java内,并传入是否勾选清除sd卡数据的参数;
MasterClearConfirm.java类是用来确认是否开始"恢复出厂设置",在这个类里面我们只需要关心最重要的doMasterClear()方法即可

[java]  view plain  copy
  1. private void doMasterClear() {  
  2.     Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);  
  3.     intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);  
  4.     intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");  
  5.     intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);  
  6.     getActivity().sendBroadcast(intent);  
  7.     // Intent handling is asynchronous -- assume it will happen soon.  
  8. }  
这个方法主要是用来发送广播,通知android系统开始"恢复出厂设置",最终的接收方法广播的地方是在
frameworks/base/services/core/java/com/android/server/MasterClearReceiver.java类里面;

onReceiver()方法:

[java]  view plain  copy
  1. @Override  
  2. public void onReceive(final Context context, final Intent intent) {  
  3.     if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {  
  4.         if (!"google.com".equals(intent.getStringExtra("from"))) {  
  5.             Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");  
  6.             return;  
  7.         }  
  8.     }  
  9.   
  10.     final boolean shutdown = intent.getBooleanExtra("shutdown"false);  
  11.     final String reason = intent.getStringExtra(Intent.EXTRA_REASON);  
  12.     final boolean wipeExternalStorage = intent.getBooleanExtra(  
  13.             Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);  
  14.   
  15.     Slog.w(TAG, "!!! FACTORY RESET !!!");  
  16.     // The reboot call is blocking, so we need to do it on another thread.  
  17.     Thread thr = new Thread("Reboot") {  
  18.         @Override  
  19.         public void run() {  
  20.             try {  
  21.                 RecoverySystem.rebootWipeUserData(context, shutdown, reason);  
  22.                 Log.wtf(TAG, "Still running after master clear?!");  
  23.             } catch (IOException e) {  
  24.                 Slog.e(TAG, "Can't perform master clear/factory reset", e);  
  25.             } catch (SecurityException e) {  
  26.                 Slog.e(TAG, "Can't perform master clear/factory reset", e);  
  27.             }  
  28.         }  
  29.     };  
  30.     if (wipeExternalStorage) {  
  31.         // thr will be started at the end of this task.  
  32.         new WipeAdoptableDisksTask(context, thr).execute();  
  33.     } else {  
  34.         thr.start();  
  35.     }  
  36. }  
通过对比发送广播的地方,只需要关心两个参数reason和wipeExternalStorage;

1、wipeExternalStorage为true时,表示需要清除sdcard的数据;开启一个异步任务:

[java]  view plain  copy
  1. new WipeAdoptableDisksTask(context, thr).execute();  
[java]  view plain  copy
  1. private class WipeAdoptableDisksTask extends AsyncTask<Void, Void, Void> {  
  2.     private final Thread mChainedTask;  
  3.     private final Context mContext;  
  4.     private final ProgressDialog mProgressDialog;  
  5.   
  6.     public WipeAdoptableDisksTask(Context context, Thread chainedTask) {  
  7.         mContext = context;  
  8.         mChainedTask = chainedTask;  
  9.         mProgressDialog = new ProgressDialog(context);  
  10.     }  
  11.   
  12.     @Override  
  13.     protected void onPreExecute() {  
  14.         mProgressDialog.setIndeterminate(true);  
  15.         mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
  16.         mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));  
  17.         mProgressDialog.show();  
  18.     }  
  19.   
  20.     @Override  
  21.     protected Void doInBackground(Void... params) {  
  22.         Slog.w(TAG, "Wiping adoptable disks");  
  23.         StorageManager sm = (StorageManager) mContext.getSystemService(  
  24.                 Context.STORAGE_SERVICE);  
  25.         sm.wipeAdoptableDisks();  
  26.         return null;  
  27.     }  
  28.   
  29.     @Override  
  30.     protected void onPostExecute(Void result) {  
  31.         mProgressDialog.dismiss();  
  32.         mChainedTask.start();  
  33.     }  
  34.   
  35. }  
1)首先执行onPreExecute()方法,此时是在UI Thread内;设置Dialog,提醒用户;
2)执行doInBackground()方法,这个方法一般是用来执行耗时操作;使用StorageManager去清除sdcard内容;
3)最后执行onPostExecute()方法,这个方法是当我们的异步任务执行完之后,就会将结果返回给这个方法;开启一个thr线程,这个线程也是wipeExternalStorage为false需要执行的,后面一起分析。

2、wipeExternalStorage为false时,表示只需要清除用户数据;此时只要需要开启执行thr线程即可;

[java]  view plain  copy
  1. thr.start();  
[java]  view plain  copy
  1. Thread thr = new Thread("Reboot") {  
  2.     @Override  
  3.     public void run() {  
  4.         try {  
  5.             RecoverySystem.rebootWipeUserData(context, shutdown, reason);  
  6.             Log.wtf(TAG, "Still running after master clear?!");  
  7.         } catch (IOException e) {  
  8.             Slog.e(TAG, "Can't perform master clear/factory reset", e);  
  9.         } catch (SecurityException e) {  
  10.             Slog.e(TAG, "Can't perform master clear/factory reset", e);  
  11.         }  
  12.     }  
  13. };  
调用RecoverySystem.java的rebootWipeUserData()方法:

[java]  view plain  copy
  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.     Intent intent = 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.             }, null0nullnull);  
  37.   
  38.     // Block until the ordered broadcast has completed.  
  39.     condition.block();  
  40.   
  41.     String shutdownArg = null;  
  42.     if (shutdown) {  
  43.         shutdownArg = "--shutdown_after";  
  44.     }  
  45.   
  46.     String reasonArg = null;  
  47.     if (!TextUtils.isEmpty(reason)) {  
  48.         reasonArg = "--reason=" + sanitizeArg(reason);  
  49.     }  
  50.   
  51.     final String localeArg = "--locale=" + Locale.getDefault().toString();  
  52.     bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);  
  53. }  
1、查看当前用户是否有权限清除数据;
2、发送有序广播并中断线程,直到有序广播完成;
3、封装命令参数,执行bootCommand()方法;

[java]  view plain  copy
  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.     RECOVERY_DIR.mkdirs();  // In case we need it  
  8.     COMMAND_FILE.delete();  // In case it's not writable  
  9.     LOG_FILE.delete();  
  10.   
  11.     FileWriter command = new FileWriter(COMMAND_FILE);  
  12.     try {  
  13.         for (String arg : args) {  
  14.             if (!TextUtils.isEmpty(arg)) {  
  15.                 command.write(arg);  
  16.                 command.write("\n");  
  17.             }  
  18.         }  
  19.     } finally {  
  20.         command.close();  
  21.     }  
  22.   
  23.     // Having written the command file, go ahead and reboot  
  24.     PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);  
  25.     pm.reboot(PowerManager.REBOOT_RECOVERY);  
  26.   
  27.     throw new IOException("Reboot failed (no permissions?)");  
  28. }  
1、将传入的命令参数写入到"/cache/recovery/command"文件中去;
2、调用PowerManager.java的reboot()方法进行重启操作;

[java]  view plain  copy
  1. public void reboot(String reason) {  
  2.     try {  
  3.         mService.reboot(false, reason, true);  
  4.     } catch (RemoteException e) {  
  5.     }  
  6. }  
aidl调入到PowerManagerService.java的reboot()方法:

[java]  view plain  copy
  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.     mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);  
  11.     if (PowerManager.REBOOT_RECOVERY.equals(reason)) {  
  12.         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);  
  13.     }  
  14.   
  15.     final long ident = Binder.clearCallingIdentity();  
  16.     try {  
  17.         shutdownOrRebootInternal(false, confirm, reason, wait);  
  18.     } finally {  
  19.         Binder.restoreCallingIdentity(ident);  
  20.     }  
  21. }  
首先检查是否有REBOOT和RECOVERY权限,然后调用shutdownOrRebootInternal()方法:

[java]  view plain  copy
  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.     Runnable runnable = 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.     msg.setAsynchronous(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. }  
这个方法最主要的还是Runnable内的run()方法实现,由于之前传进来的shutdown为false,于是继续调用:

[java]  view plain  copy
  1. ShutdownThread.reboot(mContext, reason, confirm);  
[java]  view plain  copy
  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.     mReboot = true;  
  12.     mRebootSafeMode = false;  
  13.     mRebootUpdate = false;  
  14.     mRebootReason = reason;  
  15.     shutdownInner(context, confirm);  
  16. }  
继续调用shutdownInner()方法,此时confirm为false;
shutdownInner()这个方法很长,主要的作用是创建弹出框,根据传入参数confirm来判断是否显示需要用户确认关机或者重启的Dialog,然后开线程执行关机或者重启操作。后续的流程分析可以去查看另外一篇文章: Android6.0 关机shutdown & 重启reboot流程分析


设备重启后,会自动进入recovery mode模式,读取/cache/recovery/command, 內容为"--wipe_data",开始清除data和cache分区,清除成功后,系统重启,然后进入正常开机流程,重新使用system分区的内容完成开机初始化,此过程跟我们第一次烧写软件过程一致。
至此完成恢复出厂设置。


恢复出厂设置总结:
1、在MasterClearConfirm.java中确认开始执行恢复出厂设置操作,并发出"恢复出厂设置"的广播;
2、在MasterClearReceiver.java接收MasterClearConfirm.java发出的广播,根据是否清除sdcard选项来执行相应的操作;
3、调用RecoverySystem.rebootWipeUserData()方法来清除用户数据并重启设备;这个方法执行过程中会发出"android.intent.action.MASTER_CLEAR_NOTIFICATION"广播、写"/cache/recovery/command"文件(内容包含"--wipe_data"),然后重启设备;
4、设备重启后进入recovery mode之后,读取/cache/recovery/command, 內容为"--wipe_data";
5.按照读取的command,进行wipe data清除数据操作;
6.清除成功后,系统重启,然后进入正常开机流程。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值