对于回复出厂设置,应用层能做的就是发一个广播或者直接调用RecoverySystem的一个方法,剩下的都是系统完成的。通过发广播的方式最终也是调用RecoverySystem的方法来回复出厂设置,这里以发广播的方式来恢复出厂设置

一, 发送恢复出厂设置的广播,发送广播需要相应的权限 android.permission.MASTER_CLEAR

 Intent intent = new Intent("android.intent.action.FACTORY_RESET");intent.setPackage("android");intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);intent.putExtra("android.intent.extra.REASON", "MasterClearConfirm");intent.putExtra("android.intent.extra.WIPE_EXTERNAL_STORAGE", false);intent.putExtra("com.android.internal.intent.extra.WIPE_ESIMS", false);context.sendBroadcast(intent);

二,广播的处理

广播的处理在./base/services/core/java/com/android/server/MasterClearReceiver.java中实现,它会根据传进来的东西去进行不同的逻辑处理final boolean shutdown = intent.getBooleanExtra("shutdown", false); //是否关机final String reason = intent.getStringExtra(Intent.EXTRA_REASON);//回复出厂设置的原因mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false); //是否清楚外部存储mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false); //是否清楚ESIMS数据,嵌入式sim开,直接嵌入到设备芯片上final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)|| intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);Slog.w(TAG, "!!! FACTORY RESET !!!");// The reboot call is blocking, so we need to do it on another thread.Thread thr = new Thread("Reboot") {@Overridepublic void run() {try {RecoverySystem.rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims); //重启之后启动到Recovery系统去删除数据Log.wtf(TAG, "Still running after master clear?!");} catch (IOException e) {Slog.e(TAG, "Can't perform master clear/factory reset", e);} catch (SecurityException e) {Slog.e(TAG, "Can't perform master clear/factory reset", e);}}};if (mWipeExternalStorage || mWipeEsims) {// thr will be started at the end of this task. //如果广播中携带的数据中需要清楚外部存储和ESIMS数据,则先执行这些数据的清除之后再重启删除userData数据new WipeDataTask(context, thr).execute();} else {thr.start();}private class WipeDataTask extends AsyncTask<Void, Void, Void> {private final Thread mChainedTask;private final Context mContext;private final ProgressDialog mProgressDialog;public WipeDataTask(Context context, Thread chainedTask) {mContext = context;mChainedTask = chainedTask;mProgressDialog = new ProgressDialog(context);}@Overrideprotected void onPreExecute() {mProgressDialog.setIndeterminate(true);mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));mProgressDialog.show();}@Overrideprotected Void doInBackground(Void... params) {Slog.w(TAG, "Wiping adoptable disks");if (mWipeExternalStorage) {//调用 StorageManager 清除外部存储StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);sm.wipeAdoptableDisks();}return null;}@Overrideprotected void onPostExecute(Void result) {mProgressDialog.dismiss();mChainedTask.start();}}

三,清除外部存储是获取StorageManager#wipeAdoptableDisks 来执行清除的操作。它是一个hide的方法,用户调用不了,只能系统调用或者用户通过反射的方式方调用。他会先找出

final List<DiskInfo> disks = getDisks();for (DiskInfo disk : disks) {final String diskId = disk.getId();if (disk.isAdoptable()) {Slog.d(TAG, "Found adoptable " + diskId + "; wiping");try {// TODO: switch to explicit wipe command when we have it,// for now rely on the fact that vfat format does a wipemStorageManager.partitionPublic(diskId);} catch (Exception e) {Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);}} else {Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());}}

1,获取系统的磁盘信息 getDisks,StorageManagerService对应系统服务中的名字为“mount”

1> 源代码逻辑
try {return Arrays.asList(mStorageManager.getDisks());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}

StorageManagerService中存储了系统disk的所有信息列表,这些信息是在SMS启动的时候,获取了系统中的vold系统服务,然后设置监听,当vold进行挂载磁盘的时候,会回调到SMS中,然后存储

    @Overridepublic DiskInfo[] getDisks() {synchronized (mLock) {final DiskInfo[] res = new DiskInfo[mDisks.size()];for (int i = 0; i < mmListenermListenerDisks.size(); i++) {res[i] = mDisks.valueAt(i);}return res;}}

IVoldListener 中的其中一个方法,

@Overridepublic void onDiskCreated(String diskId, int flags) {synchronized (mLock) {final String value = SystemProperties.get(StorageManager.PROP_ADOPTABLE);switch (value) {case "force_on":flags |= DiskInfo.FLAG_ADOPTABLE;break;case "force_off":flags &= ~DiskInfo.FLAG_ADOPTABLE;break;}mDisks.put(diskId, new DiskInfo(diskId, flags));}}
2> vold的简介

Vold是用于管理和控制Android外部存储介质的后台进程,这里说的管控,主要包括SD卡的插拔、挂载/卸载和格式化等;它是Android平台外部存储系统的管控枢纽。
Vold的整个控制模块主要由三个类模块构成:NetlinkManager、VolumeManager和CommandListener,它们的功能划分大概是:

NetlinkManager:用于从kernel中获取SD卡插拔的Uevnet消息
VolumeManager:管理模块,对NetlinkManager转发的消息做一些处理,并通过CommandListener发送给framework(MountService.java);接着framework会通过套接字下发命令,指引VolumeManager对存储设备做下一步的操作,如挂载/卸载等
CommandListener:通过socket,实现MountService.java与Vold之间的消息交换

2,循环删除磁盘信息,调用SMS中的partitionPublic方法

@Overridepublic void partitionPublic(String diskId) {//首先需要如下的权限,才能通过 aidl的方式调用这个方法,如果是发广播,只需要广播的权限,这里的权限检查就是检查framework,如果是自己调用,那么这里必须什么如下的权限enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);try {//调用vold进行磁盘的格式化mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1);//等待磁盘格式化完成waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);} catch (Exception e) {Slog.wtf(TAG, e);}}

waitForLatch会等待三分之用的时间,所以CountDownLatch每等待5秒钟醒来一次,如果超时就直接退出循环等待,如果Vold的格式化完成,调用了countDown方法,会直接醒来,然后执行退出循环结束。

private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis)throws TimeoutException {final long startMillis = SystemClock.elapsedRealtime();while (true) {try {if (latch.await(5000, TimeUnit.MILLISECONDS)) {return;} else {Slog.w(TAG, "Thread " + Thread.currentThread().getName()+ " still waiting for " + condition + "...");}} catch (InterruptedException e) {Slog.w(TAG, "Interrupt while waiting for " + condition);}if (timeoutMillis > 0 && SystemClock.elapsedRealtime() > startMillis + timeoutMillis) {throw new TimeoutException("Thread " + Thread.currentThread().getName()+ " gave up waiting for " + condition + " after " + timeoutMillis + "ms");}}
}

这里用到的CountDownLatch,会在Vold的回调中进行减1的操作,如下是IVoldListener的另一个方法

@Overridepublic void onDiskScanned(String diskId) {synchronized (mLock) {final DiskInfo disk = mDisks.get(diskId);if (disk != null) {onDiskScannedLocked(disk);}}}
@GuardedBy("mLock")
private void onDiskScannedLocked(DiskInfo disk) {int volumeCount = 0;for (int i = 0; i < mVolumes.size(); i++) {final VolumeInfo vol = mVolumes.valueAt(i);if (Objects.equals(disk.id, vol.getDiskId())) {volumeCount++;}}final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();final CountDownLatch latch = mDiskScanLatches.remove(disk.id); if (latch != null) {latch.countDown(); //这里回去唤醒最开始阻塞的线程,让前面格式化的aidl线程返回}disk.volumeCount = volumeCount;mCallbacks.notifyDiskScanned(disk, volumeCount);
}

四,重启清除用户数据的逻辑,它是由RecoverySystem#rebootWipeUserData方法为入口完成的

1. RecoverySystem#rebootWipeUserData

public static void rebootWipeUserData(Context context, boolean shutdown, String reason,boolean force, boolean wipeEuicc) throws IOException {UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {throw new SecurityException("Wiping data is not allowed for this user.");}//如下会进行MASTER_CLEAR_NOTIFICATION,清除Notification,它会发送一个有序广播,需要等待广播发送完成之后再进行后续的操作,此处ConditionVariable 会在再里面的mCondition为false的时候,需要block,也会在为false的时候允许open(notify),应该算是对象await的一种加强优化版本final ConditionVariable condition = new ConditionVariable();Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,android.Manifest.permission.MASTER_CLEAR,new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {condition.open();}}, null, 0, null, null);// Block until the ordered broadcast has completed.condition.block();//清除什么嵌入式通用集成电路卡if (wipeEuicc) {wipeEuiccData(context, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK);}String shutdownArg = null;//是否需要关系的参数if (shutdown) {shutdownArg = "--shutdown_after";}String reasonArg = null;if (!TextUtils.isEmpty(reason)) {reasonArg = "--reason=" + sanitizeArg(reason);}final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);}

2. 连接RecoverySystemService进行写入命令,然后重启

private static void bootCommand(Context context, String... args) throws IOException {LOG_FILE.delete();StringBuilder command = new StringBuilder();for (String arg : args) {if (!TextUtils.isEmpty(arg)) {command.append(arg);command.append("\n");}}// Write the command into BCB (bootloader control block) and boot from// there. Will not return unless failed.RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);rs.rebootRecoveryWithCommand(command.toString());throw new IOException("Reboot failed (no permissions?)");
}

3. RecoverySystemService#rebootRecoveryWithCommand,它里面通过先写入bcb命令,然后重启设备。

@Override // Binder callpublic void rebootRecoveryWithCommand(String command) {if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");synchronized (sRequestLock) {//写入bcb命令if (!setupOrClearBcb(true, command)) {return;}// Having set up the BCB, go ahead and reboot.PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);pm.reboot(PowerManager.REBOOT_RECOVERY);}}
1> 把命令写入到/cache/recovery/command中,然后往BCB(bootloaderControlBlock)中写入“boot-recovery”
private boolean setupOrClearBcb(boolean isSetup, String command) {mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);final boolean available = checkAndWaitForUncryptService();if (!available) {Slog.e(TAG, "uncrypt service is unavailable.");return false;}//先设置系统属性,这个应该设计到最终连写的系统服务socket的写block操作,具体不知道干啥用if (isSetup) {SystemProperties.set("ctl.start", "setup-bcb");} else {SystemProperties.set("ctl.start", "clear-bcb");}// Connect to the uncrypt service socket.//获取uncrypt的socket,然后给uncrypt发送指令,LocalSocket socket = connectService();if (socket == null) {Slog.e(TAG, "Failed to connect to uncrypt socket");return false;}DataInputStream dis = null;DataOutputStream dos = null;try {dis = new DataInputStream(socket.getInputStream());dos = new DataOutputStream(socket.getOutputStream());// Send the BCB commands if it's to setup BCB.if (isSetup) {byte[] cmdUtf8 = command.getBytes("UTF-8");dos.writeInt(cmdUtf8.length);dos.write(cmdUtf8, 0, cmdUtf8.length);dos.flush();}// Read the status from the socket.int status = dis.readInt();// Ack receipt of the status code. uncrypt waits for the ack so// the socket won't be destroyed before we receive the code.dos.writeInt(0);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 {IoUtils.closeQuietly(dis);IoUtils.closeQuietly(dos);IoUtils.closeQuietly(socket);}return true;}
}
2. 重启设备
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
pm.reboot(PowerManager.REBOOT_RECOVERY);

五,重启

系统重启之后,bootloader会读取BCB中的数据,如果存在就按照里面的指令执行,如果不存在,就正常启动系统。前面写入的是boot-recovery,所以系统会启动到Recovery系统中,启动之后会读取BCB中的Command字段的信息,没有的话继续读上面的/cache/recovery/command里面的内容(按照前面的内容,为清除数据"–wipe_data"),然后执行清除用户数据的操作(/data分区下面的数据)

android恢复出厂设置的源码解析相关推荐

  1. Android多线程之ArrayBlockingQueue源码解析

    阻塞队列系列 Android多线程之LinkedBlockingQueue源码解析 Android多线程之SynchronousQueue源码解析 Andorid多线程之DelayQueue源码分析 ...

  2. Android多线程之IntentService源码解析

    想要了解 IntentService 的工作原理需要先对 Android 系统中以 Handler.Looper.MessageQueue 组成的异步消息处理机制以及 HandlerThread 有所 ...

  3. Android Glide 3.7.0 源码解析(八) , RecyclableBufferedInputStream 的 mark/reset 实现

    个人博客传送门 一.mark / reset 的作用 Android Glide 3.7.0 源码解析(七) , 细说图形变换和解码有提到过RecyclableBufferedInputStream ...

  4. android 恢复出厂设置 时间,Android 恢复出厂设置后,时间不能恢复替:2013年1月1日...

    Android 恢复出厂设置后,时间不能恢复为:2013年1月1日 前言         欢迎大家我分享和推荐好用的代码段~~声明         欢迎转载,但请保留文章原始出处: CSDN:http ...

  5. Android 恢复出厂设置(recovery)

    Android 恢复出厂设置基本流程 (1)遥控器/按键板后门键触发,或者应用里面从系统设置里面恢复出厂选项也可触发: // 后面以系统设置的应用触发为例 (2)选择恢复出厂设置之后,就会发送广播&q ...

  6. Android 恢复出厂设置(系统时间不修改)

    Android恢复出厂设置时,只会将/data和/cache分区进行清除,时间和其他分区不会清除, 时间由rtc硬件模块来进行维护的,时间更新后会将时间信息写入此硬件模块,在系统启动时,RTC硬件驱动 ...

  7. Android恢复出厂设置流程分析【Android源码解析十】

    最近看恢复出厂的一个问题,以前也查过这方面的流程,所以这里整理一些AP+framework层的流程: 在setting-->备份与重置--->恢复出厂设置--->重置手机---> ...

  8. android 恢复出厂 自动恢复文件夹,Android恢复出厂设置

    恢复出厂设置核心代码:sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR")); 即发送一个广播,需要在And ...

  9. 【Android应用开发】EasyDialog 源码解析

    示例源码下载 : http://download.csdn.net/detail/han1202012/9115227 EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; ...

最新文章

  1. Mycat 读写分离 数据库分库分表 中间件 安装部署,及简单使用
  2. 弹窗页面PHP代码不执行,PHP代码没有被执行,而是代码显示在页面上
  3. python“ with”语句的用途是什么?
  4. post传参部分数据丢失
  5. Oracle- 数据库的备份
  6. 线性筛法 与 线性求欧拉函数 的计算模板
  7. Beginning SDL 2.0(4) YUV加载及渲染
  8. c语言兔子洞,数据结构水题选讲 - osc_y08db3kb的个人空间 - OSCHINA - 中文开源技术交流社区...
  9. windows docker常用命令
  10. 应用挂载beegfs指定目录_BeeGFS源码分析1-元数据服务概要分析
  11. 11.Axis客户端接收不同参数类型
  12. SQLServer2008安装教程
  13. 语言模型——深度学习入门动手
  14. win7录屏_win7/win10电脑屏幕录像工具哪款比较好?--QVE屏幕录制
  15. html添加好友界面,添加好友.html
  16. 人工智能在日常生活中的十大应用
  17. 红米手机4X获得Root权限的流程
  18. uniapp跳转外部链接
  19. 这黑科技从 B站 火到 GitHub
  20. ROS中usb摄像头的使用_(usb_cam)

热门文章

  1. arm qt mysql插件,关于移植QT的Sqlite数据库到ARM板运行的问题
  2. 专业论文翻译/全文翻译
  3. 618最值得买的数码好物有哪些、全网数码产品推荐指数最高清单
  4. Android ImageView宽高等比缩放
  5. 图文在线翻译-文本翻译
  6. asp.NET中 treeview 控件的使用
  7. e:Cannot generate ORC metadata for CONFIG_UNWINDER_ORC=y, please install libelf-dev, libelf-devel
  8. 中国地图绘制-点击事件
  9. arduino机械狗子
  10. 4-20mA电路转换