• Android源码笔记--恢复出厂设置


           最近在学习Android系统源码,这一节主要是了解恢复出厂设置。实现恢复出厂一般是通过发广播操作,如下:

    1. //恢复出厂设置
    2. Intent recovery = new Intent("android.intent.action.MASTER_CLEAR");
    3. recovery.setPackage("android");
    4. sendBroadcast(recovery);
    5. 清单文件需要加:
    6. 1 android:shareUserId = "android.uid.system" //表明系统应用
    7. 2 "android.permission.MASTER_CLEAR" />

    它的主要流程如下:

    1. 1 点击按钮,系统发送恢复出厂设置广播
    2. 2 系统MasterClearReceiver接收广播,并进行android层的相关处理最后重启
    3. 3 在/cache/recovery/command文件中写入命令字段
    4. 4 重启系统,进入Recovery模式
    5. 5 根据/cache/recovery/command中的命令字段清楚用户数据
    6. 6 重新启动系统,恢复出厂完成

    简单来看一下源码:

    1. /frameworks/base/services/core/java/com/android/server/MasterClearReceiver.java
    2. public class MasterClearReceiver extends BroadcastReceiver {
    3. private static final String TAG = "MasterClear";
    4. private boolean mWipeExternalStorage;
    5. private boolean mWipeEsims;
    6. @Override
    7. public void onReceive(final Context context, final Intent intent) {
    8. ...
    9. final String factoryResetPackage = context
    10. .getString(com.android.internal.R.string.config_factoryResetPackage);
    11. if (Intent.ACTION_FACTORY_RESET.equals(intent.getAction())
    12. && !TextUtils.isEmpty(factoryResetPackage)) {
    13. intent.setPackage(factoryResetPackage).setComponent(null);
    14. context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
    15. return;
    16. }
    17. final boolean shutdown = intent.getBooleanExtra("shutdown", false);
    18. final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
    19. mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
    20. mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
    21. final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
    22. || intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);
    23. Slog.w(TAG, "!!! FACTORY RESET !!!");
    24. // The reboot call is blocking, so we need to do it on another thread.
    25. Thread thr = new Thread("Reboot") {
    26. @Override
    27. public void run() {
    28. try {
    29. RecoverySystem
    30. .rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims);
    31. Log.wtf(TAG, "Still running after master clear?!");
    32. } catch (IOException e) {
    33. Slog.e(TAG, "Can't perform master clear/factory reset", e);
    34. } catch (SecurityException e) {
    35. Slog.e(TAG, "Can't perform master clear/factory reset", e);
    36. }
    37. }
    38. };
    39. if (mWipeExternalStorage) {
    40. // thr will be started at the end of this task.
    41. new WipeDataTask(context, thr).execute();
    42. } else {
    43. thr.start();
    44. }
    45. }
    46. private class WipeDataTask extends AsyncTask {
    47. private final Thread mChainedTask;
    48. private final Context mContext;
    49. private final ProgressDialog mProgressDialog;
    50. public WipeDataTask(Context context, Thread chainedTask) {
    51. mContext = context;
    52. mChainedTask = chainedTask;
    53. mProgressDialog = new ProgressDialog(context);
    54. }
    55. @Override
    56. protected void onPreExecute() {
    57. mProgressDialog.setIndeterminate(true);
    58. mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    59. mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));
    60. mProgressDialog.show();
    61. }
    62. @Override
    63. protected Void doInBackground(Void... params) {
    64. Slog.w(TAG, "Wiping adoptable disks");
    65. if (mWipeExternalStorage) {
    66. StorageManager sm = (StorageManager) mContext.getSystemService(
    67. Context.STORAGE_SERVICE);
    68. sm.wipeAdoptableDisks();
    69. }
    70. return null;
    71. }
    72. @Override
    73. protected void onPostExecute(Void result) {
    74. mProgressDialog.dismiss();
    75. mChainedTask.start();
    76. }
    77. }
    78. }
    1. "com.android.server.MasterClearReceiver"
    2. android:permission="android.permission.MASTER_CLEAR">
    3. android:priority="100" >
    4. "android.intent.action.FACTORY_RESET" />
    5. "android.intent.action.MASTER_CLEAR" />
    6. "com.google.android.c2dm.intent.RECEIVE" />
    7. "android.intent.category.MASTER_CLEAR" />
    1. /frameworks/base/core/java/android/os/RecoverySystem.java
    2. public class RecoverySystem {
    3. private final IRecoverySystem mService;
    4. public RecoverySystem(IRecoverySystem service) {
    5. mService = service;
    6. }
    7. public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
    8. boolean force, boolean wipeEuicc) throws IOException {
    9. UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    10. if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
    11. throw new SecurityException("Wiping data is not allowed for this user.");
    12. }
    13. final ConditionVariable condition = new ConditionVariable();
    14. Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
    15. intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
    16. | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
    17. context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,
    18. android.Manifest.permission.MASTER_CLEAR,
    19. new BroadcastReceiver() {
    20. @Override
    21. public void onReceive(Context context, Intent intent) {
    22. condition.open();
    23. }
    24. }, null, 0, null, null);
    25. // Block until the ordered broadcast has completed.
    26. condition.block();
    27. EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
    28. if (wipeEuicc) {
    29. wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
    30. } else {
    31. removeEuiccInvisibleSubs(context, euiccManager);
    32. }
    33. String shutdownArg = null;
    34. if (shutdown) {
    35. shutdownArg = "--shutdown_after";
    36. }
    37. String reasonArg = null;
    38. if (!TextUtils.isEmpty(reason)) {
    39. String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
    40. reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
    41. }
    42. final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
    43. bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
    44. }
    1. private static void bootCommand(Context context, String... args) throws IOException {
    2. //删除日志信息
    3. LOG_FILE.delete();
    4. StringBuilder command = new StringBuilder();
    5. for (String arg : args) {
    6. if (!TextUtils.isEmpty(arg)) {
    7. command.append(arg);
    8. command.append("\n");
    9. }
    10. }
    11. // Write the command into BCB (bootloader control block) and boot from
    12. // there. Will not return unless failed.
    13. // 将命令写到bootloader control block
    14. RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
    15. rs.rebootRecoveryWithCommand(command.toString());
    16. throw new IOException("Reboot failed (no permissions?)");
    17. }
    18. // 通过Binder与RecoverySystemService对话以设置BCB
    19. private void rebootRecoveryWithCommand(String command) {
    20. try {
    21. mService.rebootRecoveryWithCommand(command);
    22. } catch (RemoteException ignored) {
    23. }
    24. }

    来看一下服务端:

    1. /frameworks/base/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
    2. @Override // Binder call
    3. public void rebootRecoveryWithCommand(String command) {
    4. if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
    5. synchronized (sRequestLock) {
    6. if (!setupOrClearBcb(true, command)) {
    7. return;
    8. }
    9. // 设置完CBC,调用电源管理重启系统,进入恢复模式
    10. PowerManager pm = mInjector.getPowerManager();
    11. pm.reboot(PowerManager.REBOOT_RECOVERY);
    12. }
    13. }
    14. private boolean setupOrClearBcb(boolean isSetup, String command) {
    15. mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
    16. final boolean available = checkAndWaitForUncryptService();
    17. if (!available) {
    18. Slog.e(TAG, "uncrypt service is unavailable.");
    19. return false;
    20. }
    21. if (isSetup) {
    22. mInjector.systemPropertiesSet("ctl.start", "setup-bcb");
    23. } else {
    24. mInjector.systemPropertiesSet("ctl.start", "clear-bcb");
    25. }
    26. // Connect to the uncrypt service socket.
    27. UncryptSocket socket = mInjector.connectService();
    28. if (socket == null) {
    29. Slog.e(TAG, "Failed to connect to uncrypt socket");
    30. return false;
    31. }
    32. try {
    33. // Send the BCB commands if it's to setup BCB.
    34. if (isSetup) {
    35. socket.sendCommand(command);
    36. }
    37. // Read the status from the socket.
    38. int status = socket.getPercentageUncrypted();
    39. // Ack receipt of the status code. uncrypt waits for the ack so
    40. // the socket won't be destroyed before we receive the code.
    41. socket.sendAck();
    42. if (status == 100) {
    43. Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear")
    44. + " bcb successfully finished.");
    45. } else {
    46. // Error in /system/bin/uncrypt.
    47. Slog.e(TAG, "uncrypt failed with status: " + status);
    48. return false;
    49. }
    50. } catch (IOException e) {
    51. Slog.e(TAG, "IOException when communicating with uncrypt:", e);
    52. return false;
    53. } finally {
    54. socket.close();
    55. }
    56. return true;
    57. }
    1. /frameworks/base/core/java/android/os/PowerManager.java
    2. public final class PowerManager {
    3. @UnsupportedAppUsage
    4. final IPowerManager mService;
    5. /**
    6. * {@hide}
    7. */
    8. public PowerManager(Context context, IPowerManager service, IThermalService thermalService,
    9. Handler handler) {
    10. mContext = context;
    11. mService = service;
    12. mThermalService = thermalService;
    13. mHandler = handler;
    14. }
    15. public void reboot(@Nullable String reason) {
    16. if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
    17. throw new UnsupportedOperationException(
    18. "Attempted userspace reboot on a device that doesn't support it");
    19. }
    20. try {
    21. mService.reboot(false, reason, true);
    22. } catch (RemoteException e) {
    23. throw e.rethrowFromSystemServer();
    24. }
    25. }
    26. }
    1. /frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
    2. /**
    3. * Reboots the device.
    4. *
    5. * @param confirm If true, shows a reboot confirmation dialog.
    6. * @param reason The reason for the reboot, or null if none.
    7. * @param wait If true, this call waits for the reboot to complete and does not return.
    8. */
    9. @Override // Binder call
    10. public void reboot(boolean confirm, @Nullable String reason, boolean wait) {
    11. mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
    12. if (PowerManager.REBOOT_RECOVERY.equals(reason)
    13. || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
    14. mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
    15. }
    16. final long ident = Binder.clearCallingIdentity();
    17. try {
    18. shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
    19. } finally {
    20. Binder.restoreCallingIdentity(ident);
    21. }
    22. }
    23. private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
    24. @Nullable final String reason, boolean wait) {
    25. if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
    26. if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
    27. throw new UnsupportedOperationException(
    28. "Attempted userspace reboot on a device that doesn't support it");
    29. }
    30. UserspaceRebootLogger.noteUserspaceRebootWasRequested();
    31. }
    32. if (mHandler == null || !mSystemReady) {
    33. if (RescueParty.isAttemptingFactoryReset()) {
    34. // If we're stuck in a really low-level reboot loop, and a
    35. // rescue party is trying to prompt the user for a factory data
    36. // reset, we must GET TO DA CHOPPA!
    37. PowerManagerService.lowLevelReboot(reason);
    38. } else {
    39. throw new IllegalStateException("Too early to call shutdown() or reboot()");
    40. }
    41. }
    42. Runnable runnable = new Runnable() {
    43. @Override
    44. public void run() {
    45. synchronized (this) {
    46. if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
    47. ShutdownThread.rebootSafeMode(getUiContext(), confirm);
    48. } else if (haltMode == HALT_MODE_REBOOT) {
    49. ShutdownThread.reboot(getUiContext(), reason, confirm);
    50. } else {
    51. ShutdownThread.shutdown(getUiContext(), reason, confirm);
    52. }
    53. }
    54. }
    55. };
    56. // ShutdownThread must run on a looper capable of displaying the UI.
    57. Message msg = Message.obtain(UiThread.getHandler(), runnable);
    58. msg.setAsynchronous(true);
    59. UiThread.getHandler().sendMessage(msg);
    60. // PowerManager.reboot() is documented not to return so just wait for the inevitable.
    61. if (wait) {
    62. synchronized (runnable) {
    63. while (true) {
    64. try {
    65. runnable.wait();
    66. } catch (InterruptedException e) {
    67. }
    68. }
    69. }
    70. }
    71. }
    1. /frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
    2. private static final ShutdownThread sInstance = new ShutdownThread();
    3. public static void rebootSafeMode(final Context context, boolean confirm) {
    4. UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    5. if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
    6. return;
    7. }
    8. mReboot = true;
    9. mRebootSafeMode = true;
    10. mRebootHasProgressBar = false;
    11. mReason = null;
    12. shutdownInner(context, confirm);
    13. }
    14. public static void reboot(final Context context, String reason, boolean confirm) {
    15. mReboot = true;
    16. mRebootSafeMode = false;
    17. mRebootHasProgressBar = false;
    18. mReason = reason;
    19. shutdownInner(context, confirm);
    20. }
    21. public static void shutdown(final Context context, String reason, boolean confirm) {
    22. mReboot = false;
    23. mRebootSafeMode = false;
    24. mReason = reason;
    25. shutdownInner(context, confirm);
    26. }
    27. private static void shutdownInner(final Context context, boolean confirm) {
    28. // ShutdownThread is called from many places, so best to verify here that the context passed
    29. // in is themed.
    30. context.assertRuntimeOverlayThemable();
    31. // ensure that only one thread is trying to power down.
    32. // any additional calls are just returned
    33. synchronized (sIsStartedGuard) {
    34. if (sIsStarted) {
    35. Log.d(TAG, "Request to shutdown already running, returning.");
    36. return;
    37. }
    38. }
    39. final int longPressBehavior = context.getResources().getInteger(
    40. com.android.internal.R.integer.config_longPressOnPowerBehavior);
    41. final int resourceId = mRebootSafeMode
    42. ? com.android.internal.R.string.reboot_safemode_confirm
    43. : (longPressBehavior == 2
    44. ? com.android.internal.R.string.shutdown_confirm_question
    45. : com.android.internal.R.string.shutdown_confirm);
    46. Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
    47. if (confirm) {
    48. final CloseDialogReceiver closer = new CloseDialogReceiver(context);
    49. if (sConfirmDialog != null) {
    50. sConfirmDialog.dismiss();
    51. }
    52. sConfirmDialog = new AlertDialog.Builder(context)
    53. .setTitle(mRebootSafeMode
    54. ? com.android.internal.R.string.reboot_safemode_title
    55. : com.android.internal.R.string.power_off)
    56. .setMessage(resourceId)
    57. .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
    58. public void onClick(DialogInterface dialog, int which) {
    59. beginShutdownSequence(context);
    60. }
    61. })
    62. .setNegativeButton(com.android.internal.R.string.no, null)
    63. .create();
    64. closer.dialog = sConfirmDialog;
    65. sConfirmDialog.setOnDismissListener(closer);
    66. sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
    67. sConfirmDialog.show();
    68. } else {
    69. beginShutdownSequence(context);
    70. }
    71. }
    1. /frameworks/base/core/java/android/os/storage/StorageManager.java
    2. private final IStorageManager mStorageManager;
    3. @UnsupportedAppUsage
    4. public StorageManager(Context context, Looper looper) throws ServiceNotFoundException {
    5. mContext = context;
    6. mResolver = context.getContentResolver();
    7. mLooper = looper;
    8. mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount"));
    9. mAppOps = mContext.getSystemService(AppOpsManager.class);
    10. }
    11. /** {@hide} */
    12. public void wipeAdoptableDisks() {
    13. // We only wipe devices in "adoptable" locations, which are in a
    14. // long-term stable slot/location on the device, where apps have a
    15. // reasonable chance of storing sensitive data. (Apps need to go through
    16. // SAF to write to transient volumes.)
    17. final List disks = getDisks();
    18. for (DiskInfo disk : disks) {
    19. final String diskId = disk.getId();
    20. if (disk.isAdoptable()) {
    21. Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
    22. try {
    23. // TODO: switch to explicit wipe command when we have it,
    24. // for now rely on the fact that vfat format does a wipe
    25. mStorageManager.partitionPublic(diskId);
    26. } catch (Exception e) {
    27. Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
    28. }
    29. } else {
    30. Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
    31. }
    32. }
    33. }
    34. /** {@hide} */
    35. @UnsupportedAppUsage
    36. public void partitionPublic(String diskId) {
    37. try {
    38. mStorageManager.partitionPublic(diskId);
    39. } catch (RemoteException e) {
    40. throw e.rethrowFromSystemServer();
    41. }
    42. }

  • 相关阅读:
    kaggle新赛:AI Village夺旗赛挑战
    vsomeip3 双机通信文件配置
    这个零代码神器,业务不懂技术也能用,大大降低IT人员的报表需求
    量子计算(一):量子计算是什么
    学习-Java输入输出之随机IO流之向文件中追加内容
    BC1.2 PD协议
    Http协议以及其中的Post、Get等通信模式
    【2023最新版】Python全栈知识点总结
    Vite + Vue3 使用cdn引入依赖 vite cdn vue3 cdn vite引入cdn
    Java 21 虚拟线程如何限流控制吞吐量
  • 原文地址:https://blog.csdn.net/ljt2724960661/article/details/127817777