赞
踩
如果我们想让系统恢复出厂设置,一般有一下三种方式。
1、在【系统设置页面】进入【恢复出厂设置页面】,点击【恢复出厂设置】按钮。
2、直接通过adb发送恢复出厂设置的广播
adb shell am broadcast -a android.intent.action.MASTER_CLEAR
3、使用adb命令修改recovery的command文件,并重启系统进入recovery模式
adb shell 'echo "--wipe_data\n--locale=en_US" > /cache/recovery/command'
adb shell setprop sys.powerctl reboot,recovery
上面提到的第2种和第3种方式都是借助adb命令实现的,这里我们不过多讨论,本篇文章我们主要来分析第1种方式的实现流程。
我们在【系统设置页面】进入【恢复出厂设置页面】,点击【恢复出厂设置】按钮,进入【二次确认页面】,然后再次点击【恢复出厂设置】按钮。在确定【要恢复出厂设置吗】页面点击确定【清除全部内容】按钮,系统会发送恢复出厂设置的广播:
packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java
public class MasterClearConfirm extends OptionsMenuFragment { private View mContentView; private boolean mEraseSdCard; private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() { public void onClick(View v) { ... //发送恢复出厂设置的广播事件 doMasterClear(); ... } ... }; //发送恢复出厂设置的广播事件 private void doMasterClear() { //ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR" Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm"); //该参数表示是否擦除SD卡,默认为False intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard); //发送恢复出厂设置的广播事件 getActivity().sendBroadcast(intent); } }
点击确定【清除全部内容】按钮,最终会触发doMasterClear方法,然后在该方法中发送了一个恢复出厂设置的广播。
1、MasterClearReceiver广播组件的配置信息如下所示。
framework/base/core/res/AndroidManifest.xml
<receiver android:name="com.android.server.MasterClearReceiver"
android:permission="android.permission.MASTER_CLEAR">
<intent-filter
android:priority="100" >
<!-- For Checkin, Settings, etc.: action=MASTER_CLEAR -->
<action android:name="android.intent.action.MASTER_CLEAR" />
<!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="android.intent.category.MASTER_CLEAR" />
</intent-filter>
</receiver>
2、MasterClearReceiver广播组件的所对应的源码信息如下所示。
frameworks/base/services/core/java/com/android/server/MasterClearReceiver.java
public class MasterClearReceiver extends BroadcastReceiver { private static final String TAG = "MasterClear"; private boolean mWipeExternalStorage;//是否擦除外部存储卡 private boolean mWipeEsims;//是否擦除sim卡 @Override public void onReceive(final Context context, final Intent intent) { if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) { if (!"google.com".equals(intent.getStringExtra("from"))) { Slog.w(TAG, "Ignoring master clear request -- not from trusted server."); return; } } if (Intent.ACTION_MASTER_CLEAR.equals(intent.getAction())) { Slog.w(TAG, "The request uses the deprecated Intent#ACTION_MASTER_CLEAR, " + "Intent#ACTION_FACTORY_RESET should be used instead."); } if (intent.hasExtra(Intent.EXTRA_FORCE_MASTER_CLEAR)) { Slog.w(TAG, "The request uses the deprecated Intent#EXTRA_FORCE_MASTER_CLEAR, " + "Intent#EXTRA_FORCE_FACTORY_RESET should be used instead."); } 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); final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false) || intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false); Slog.w(TAG, "!!! FACTORY RESET !!!"); //重启将会阻塞线程,所以我们需要另外开启一个线程 Thread thr = new Thread("Reboot") { @Override public void run() { try { //重启并擦除用户信息 RecoverySystem .rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims); 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) { //线程将会在下面的任务执行完毕以后开始执行 new WipeDataTask(context, thr).execute(); } else { thr.start(); } } //继承自AsyncTask类,此处擦除的SdCard指的是Android M(6.0)新加入的 //内置存储设备的功能中的存储位置SD卡或者USB存储设备,不是传统data分区下的SdCard //手机进入Recovery模式后,直接擦除data分区,SdCard目录包含在内,这样SdCard必然会被擦除,无需再做多余操作 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); } @Override protected void onPreExecute() { mProgressDialog.setIndeterminate(true); mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing)); mProgressDialog.show(); } @Override protected Void doInBackground(Void... params) { Slog.w(TAG, "Wiping adoptable disks"); if (mWipeExternalStorage) { //调用StorageManager的wipeAdoptableDisks方法 StorageManager sm = (StorageManager) mContext.getSystemService( Context.STORAGE_SERVICE); sm.wipeAdoptableDisks(); } return null; } @Override protected void onPostExecute(Void result) { mProgressDialog.dismiss(); mChainedTask.start();//启动重启并擦除用户数据的线程 } } }
MasterClearReceiver在收到恢复出厂设置广播之后,首先会创建一个新线程thr,该线程内部会重启并擦除用户数据,但是thr线程并不会立刻执行,而是会判断是否需要擦除外置存储卡或者sim卡中的数据,如果需要则会创建WipeDataTask对象,该对象内部会调用StorageManager的wipeAdoptableDisks方法清除外置存储卡的数据。
3、StorageManager的wipeAdoptableDisks方法如下所示。
frameworks/base/core/java/android/os/storage/StorageManager.java
public class StorageManager { //清除外置存储卡中的数据,高版本Android系统只有部分设备才有外置存储卡 public void wipeAdoptableDisks() { // We only wipe devices in "adoptable" locations, which are in a // long-term stable slot/location on the device, where apps have a // reasonable chance of storing sensitive data. (Apps need to go through // SAF to write to transient volumes.) 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 wipe //进行格式化 mStorageManager.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()); } } } }
在调用StorageManager的wipeAdoptableDisks清除完外置存储卡中的数据之后,会执行thr线程的start方法,触发run方法,调用RecoverySystem的rebootWipeUserData方法。
4、RecoverySystem的rebootWipeUserData方法如下所示。
frameworks/base/core/java/android/os/RecoverySystem.java
public class RecoverySystem { 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."); } 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() { @Override public 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方法 bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg); } /** * 重启进入恢复(recovery)模式 */ private static void bootCommand(Context context, String... args) throws IOException { synchronized (sRequestLock) { LOG_FILE.delete();//删除日志信息文件 StringBuilder command = new StringBuilder(); for (String arg : args) { if (!TextUtils.isEmpty(arg)) { command.append(arg); command.append("\n"); } } RecoverySystem rs = (RecoverySystem) context.getSystemService( Context.RECOVERY_SERVICE); //通过Binder把命令写入到BCB(bootloader control block)。 rs.setupBcb(command.toString()); //设置完BCB,调用电源管理重启系统,进入恢复模式 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); pm.reboot(PowerManager.REBOOT_RECOVERY); throw new IOException("Reboot failed (no permissions?)"); } } /** * 通过Binder与RecoverySystemService对话以设置BCB。 */ private boolean setupBcb(String command) { try { return mService.setupBcb(command); } catch (RemoteException unused) { } return false; } }
rebootWipeUserData方法会封装参数 --wipe_data,–locale,然后调用bootCommand方法,这些命令可以在recovery log(/cache/recovery/*.log)信息中看到,bootCommand方法会进一步调用RecoverySystemService的setupBcb方法将封装的参数写入到BCB中。
5、RecoverySystemService的setupBcb方法如下所示。
/frameworks/base/services/core/java/com/android/server/RecoverySystemService.java
public final class RecoverySystemService extends SystemService { @Override // Binder call public boolean setupBcb(String command) { if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]"); synchronized (sRequestLock) { return setupOrClearBcb(true, command); } } 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; } if (isSetup) { SystemProperties.set("ctl.start", "setup-bcb"); } else { SystemProperties.set("ctl.start", "clear-bcb"); } // Connect to the uncrypt service socket. 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; } } }
1、重新回到RecoverySystem的bootCommand方法中,在调用RecoverySystemService的setupBcb方法将参数写入BCB之后,会调用PowerManage的reboot方法来重启系统。
frameworks/base/core/java/android/os/PowerManager.java
public final class PowerManager {
final IPowerManager mService;
public void reboot(String reason) {
try {
//进一步调用PowerManagerService的reboot,第一个参数如果为true,会显示一个确认弹窗,这里设置为false表示不需要确认弹窗。
mService.reboot(false, reason, true);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
PowerManage的reboot会进一步调用PowerManagerService的reboot,第一个参数如果为true,会显示一个确认弹窗,这里设置为false表示不需要确认弹窗。
2、PowerManagerService的reboot方法如下所示。
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
public final class PowerManagerService extends SystemService{ /** * 重启系统 * @param confirm如果为true,会显示一个确认弹窗 * @param reason 重启的原因 * @param wait If true, this call waits for the reboot to complete and does not return. */ @Override // Binder call public void reboot(boolean confirm, String reason, boolean wait) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); if (PowerManager.REBOOT_RECOVERY.equals(reason) || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); } final long ident = Binder.clearCallingIdentity(); try { shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait); } finally { Binder.restoreCallingIdentity(ident); } } //关机重启 private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm, final String reason, boolean wait) { if (mHandler == null || !mSystemReady) { if (RescueParty.isAttemptingFactoryReset()) { // If we're stuck in a really low-level reboot loop, and a // rescue party is trying to prompt the user for a factory data // reset, we must GET TO DA CHOPPA! PowerManagerService.lowLevelReboot(reason); } else { throw new IllegalStateException("Too early to call shutdown() or reboot()"); } } Runnable runnable = new Runnable() { @Override public void run() { synchronized (this) { if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) { ShutdownThread.rebootSafeMode(getUiContext(), confirm); } else if (haltMode == HALT_MODE_REBOOT) { //调用ShutdownThread线程对象的reboot方法来重启设备 ShutdownThread.reboot(getUiContext(), reason, confirm); } else { ShutdownThread.shutdown(getUiContext(), reason, confirm); } } } }; // ShutdownThread must run on a looper capable of displaying the UI. Message msg = Message.obtain(UiThread.getHandler(), runnable); msg.setAsynchronous(true); UiThread.getHandler().sendMessage(msg); // PowerManager.reboot() is documented not to return so just wait for the inevitable. if (wait) { synchronized (runnable) { while (true) { try { runnable.wait(); } catch (InterruptedException e) { } } } } } }
PowerManagerService的reboot方法会进一步调用shutdownOrRebootInternal方法,最后会触发ShutdownThread的reboot方法。
3、ShutdownThread的reboot方法如下所示。
frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
public final class ShutdownThread extends Thread { /** * Request a clean shutdown, waiting for subsystems to clean up their * state etc. Must be called from a Looper thread in which its UI * is shown. * * @param context Context used to display the shutdown progress dialog. This must be a context * suitable for displaying UI (aka Themable). * @param reason code to pass to the kernel (e.g. "recovery"), or null. * @param confirm true if user confirmation is needed before shutting down. */ public static void reboot(final Context context, String reason, boolean confirm) { mReboot = true; mRebootSafeMode = false; mRebootHasProgressBar = false; mReason = reason; //调用shutdownInner方法 shutdownInner(context, confirm); } private static void shutdownInner(final Context context, boolean confirm) { // ShutdownThread is called from many places, so best to verify here that the context passed // in is themed. context.assertRuntimeOverlayThemable(); // ensure that only one thread is trying to power down. // any additional calls are just returned synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Request to shutdown already running, returning."); return; } } final int longPressBehavior = context.getResources().getInteger( com.android.internal.R.integer.config_longPressOnPowerBehavior); final int resourceId = mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_confirm : (longPressBehavior == 2 ? com.android.internal.R.string.shutdown_confirm_question : com.android.internal.R.string.shutdown_confirm); Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); if (confirm) {//上面提到过的确认弹窗 final CloseDialogReceiver closer = new CloseDialogReceiver(context); if (sConfirmDialog != null) { sConfirmDialog.dismiss(); } sConfirmDialog = new AlertDialog.Builder(context) .setTitle(mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_title : com.android.internal.R.string.power_off) .setMessage(resourceId) .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { beginShutdownSequence(context); } }) .setNegativeButton(com.android.internal.R.string.no, null) .create(); closer.dialog = sConfirmDialog; sConfirmDialog.setOnDismissListener(closer); sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); sConfirmDialog.show(); } else { //调用shutdownInner方法 beginShutdownSequence(context); } } }
reboot方法进一步调用shutdownInner方法,shutdownInner首先判断是否需要显示确认弹窗,由于时恢复出厂设置,这里为false表示不需要显示确认弹窗,会直接调用beginShutdownSequence方法。
4、ShutdownThread的beginShutdownSequence方法如下所示。
public final class ShutdownThread extends Thread { private static void beginShutdownSequence(Context context) { synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Shutdown sequence already running, returning."); return; } sIsStarted = true; } sInstance.mProgressDialog = showShutdownDialog(context); sInstance.mContext = context; sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); // make sure we never fall asleep again sInstance.mCpuWakeLock = null; try { sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); sInstance.mCpuWakeLock.setReferenceCounted(false); sInstance.mCpuWakeLock.acquire(); } catch (SecurityException e) { Log.w(TAG, "No permission to acquire wake lock", e); sInstance.mCpuWakeLock = null; } // also make sure the screen stays on for better user experience sInstance.mScreenWakeLock = null; if (sInstance.mPowerManager.isScreenOn()) { try { sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); sInstance.mScreenWakeLock.setReferenceCounted(false); sInstance.mScreenWakeLock.acquire(); } catch (SecurityException e) { Log.w(TAG, "No permission to acquire wake lock", e); sInstance.mScreenWakeLock = null; } } if (SecurityLog.isLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN); } // start the thread that initiates shutdown sInstance.mHandler = new Handler() { }; sInstance.start(); } }
1、点击按钮,系统发送恢复出厂设置广播
2、系统MasterClearReceiver接收广播,并进行android层的相关处理最后重启
3、在/cache/recovery/command文件中写入命令字段
4、设置Recovery模式,recovery get_args() 将"boot-recovery"和"–wipe_data"写入BCB,重启Recovery模式会根据/cache/recovery/command中的命令字段来清除用户对应的数据
1)erase_root 格式化Data分区
2)erase_root 格式化Case分区
3)finish_recovery擦除BCB分区
5、重新启动系统,恢复出厂设置成功
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。