最近在学习Android系统源码,这一节主要是了解恢复出厂设置。实现恢复出厂一般是通过发广播操作,如下:
- //恢复出厂设置
- Intent recovery = new Intent("android.intent.action.MASTER_CLEAR");
- recovery.setPackage("android");
- sendBroadcast(recovery);
-
- 清单文件需要加:
- 1 android:shareUserId = "android.uid.system" //表明系统应用
- 2
"android.permission.MASTER_CLEAR" />
它的主要流程如下:
- 1 点击按钮,系统发送恢复出厂设置广播
- 2 系统MasterClearReceiver接收广播,并进行android层的相关处理最后重启
- 3 在/cache/recovery/command文件中写入命令字段
- 4 重启系统,进入Recovery模式
- 5 根据/cache/recovery/command中的命令字段清楚用户数据
- 6 重新启动系统,恢复出厂完成
简单来看一下源码:
- /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;
-
- @Override
- public void onReceive(final Context context, final Intent intent) {
- ...
-
- final String factoryResetPackage = context
- .getString(com.android.internal.R.string.config_factoryResetPackage);
- if (Intent.ACTION_FACTORY_RESET.equals(intent.getAction())
- && !TextUtils.isEmpty(factoryResetPackage)) {
- intent.setPackage(factoryResetPackage).setComponent(null);
- context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
- return;
- }
-
- 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 !!!");
- // The reboot call is blocking, so we need to do it on another thread.
- 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) {
- // thr will be started at the end of this task.
- new WipeDataTask(context, thr).execute();
- } else {
- thr.start();
- }
- }
-
- private class WipeDataTask extends AsyncTask
{ - 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 sm = (StorageManager) mContext.getSystemService(
- Context.STORAGE_SERVICE);
- sm.wipeAdoptableDisks();
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- mProgressDialog.dismiss();
- mChainedTask.start();
- }
- }
- }
"com.android.server.MasterClearReceiver" - android:permission="android.permission.MASTER_CLEAR">
-
- android:priority="100" >
-
-
"android.intent.action.FACTORY_RESET" /> -
-
"android.intent.action.MASTER_CLEAR" /> -
-
-
"com.google.android.c2dm.intent.RECEIVE" /> -
"android.intent.category.MASTER_CLEAR" /> -
-
- /frameworks/base/core/java/android/os/RecoverySystem.java
-
-
- public class RecoverySystem {
-
- private final IRecoverySystem mService;
-
- public RecoverySystem(IRecoverySystem service) {
- mService = service;
- }
-
- 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();
-
- EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
- if (wipeEuicc) {
- wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
- } else {
- removeEuiccInvisibleSubs(context, euiccManager);
- }
-
- String shutdownArg = null;
- if (shutdown) {
- shutdownArg = "--shutdown_after";
- }
-
- String reasonArg = null;
- if (!TextUtils.isEmpty(reason)) {
- String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
- reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
- }
-
- final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
- bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
- }
- 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.
- // 将命令写到bootloader control block
- RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
- rs.rebootRecoveryWithCommand(command.toString());
-
- throw new IOException("Reboot failed (no permissions?)");
- }
-
- // 通过Binder与RecoverySystemService对话以设置BCB
- private void rebootRecoveryWithCommand(String command) {
- try {
- mService.rebootRecoveryWithCommand(command);
- } catch (RemoteException ignored) {
- }
- }
来看一下服务端:
- /frameworks/base/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
-
- @Override // Binder call
- public void rebootRecoveryWithCommand(String command) {
- if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
- synchronized (sRequestLock) {
- if (!setupOrClearBcb(true, command)) {
- return;
- }
-
- // 设置完CBC,调用电源管理重启系统,进入恢复模式
- PowerManager pm = mInjector.getPowerManager();
- pm.reboot(PowerManager.REBOOT_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;
- }
-
- if (isSetup) {
- mInjector.systemPropertiesSet("ctl.start", "setup-bcb");
- } else {
- mInjector.systemPropertiesSet("ctl.start", "clear-bcb");
- }
-
- // Connect to the uncrypt service socket.
- UncryptSocket socket = mInjector.connectService();
- if (socket == null) {
- Slog.e(TAG, "Failed to connect to uncrypt socket");
- return false;
- }
-
- try {
- // Send the BCB commands if it's to setup BCB.
- if (isSetup) {
- socket.sendCommand(command);
- }
-
- // Read the status from the socket.
- int status = socket.getPercentageUncrypted();
-
- // Ack receipt of the status code. uncrypt waits for the ack so
- // the socket won't be destroyed before we receive the code.
- socket.sendAck();
-
- 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 {
- socket.close();
- }
-
- return true;
- }
- /frameworks/base/core/java/android/os/PowerManager.java
- public final class PowerManager {
-
- @UnsupportedAppUsage
- final IPowerManager mService;
-
- /**
- * {@hide}
- */
- public PowerManager(Context context, IPowerManager service, IThermalService thermalService,
- Handler handler) {
- mContext = context;
- mService = service;
- mThermalService = thermalService;
- mHandler = handler;
- }
-
- public void reboot(@Nullable String reason) {
- if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
- throw new UnsupportedOperationException(
- "Attempted userspace reboot on a device that doesn't support it");
- }
- try {
- mService.reboot(false, reason, true);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
- /frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
-
- /**
- * Reboots the device.
- *
- * @param confirm If true, shows a reboot confirmation dialog.
- * @param reason The reason for the reboot, or null if none.
- * @param wait If true, this call waits for the reboot to complete and does not return.
- */
- @Override // Binder call
- public void reboot(boolean confirm, @Nullable 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,
- @Nullable final String reason, boolean wait) {
- if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
- if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
- throw new UnsupportedOperationException(
- "Attempted userspace reboot on a device that doesn't support it");
- }
- UserspaceRebootLogger.noteUserspaceRebootWasRequested();
- }
- 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(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) {
- }
- }
- }
- }
- }
- /frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
-
-
- private static final ShutdownThread sInstance = new ShutdownThread();
-
- public static void rebootSafeMode(final Context context, boolean confirm) {
- UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
- if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
- return;
- }
-
- mReboot = true;
- mRebootSafeMode = true;
- mRebootHasProgressBar = false;
- mReason = null;
- shutdownInner(context, confirm);
- }
-
-
- public static void reboot(final Context context, String reason, boolean confirm) {
- mReboot = true;
- mRebootSafeMode = false;
- mRebootHasProgressBar = false;
- mReason = reason;
- shutdownInner(context, confirm);
- }
-
-
- public static void shutdown(final Context context, String reason, boolean confirm) {
- mReboot = false;
- mRebootSafeMode = false;
- mReason = reason;
- 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 {
- beginShutdownSequence(context);
- }
- }
- /frameworks/base/core/java/android/os/storage/StorageManager.java
-
- private final IStorageManager mStorageManager;
-
- @UnsupportedAppUsage
- public StorageManager(Context context, Looper looper) throws ServiceNotFoundException {
- mContext = context;
- mResolver = context.getContentResolver();
- mLooper = looper;
- mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount"));
- mAppOps = mContext.getSystemService(AppOpsManager.class);
- }
-
- /** {@hide} */
- 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
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());
- }
- }
- }
-
- /** {@hide} */
- @UnsupportedAppUsage
- public void partitionPublic(String diskId) {
- try {
- mStorageManager.partitionPublic(diskId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }