今天来从源码的角度 分析一下android 系统复位的流程

1. 从点击 Reset 看起。点击Reset后,会弹出Erase everything。故而从在Setting中搜索此字段开始。

android rest接口调用 android reshare.c_工厂复位


因为我这里面有TV项目,也有手机项目。这里就看手机项目,即“master_clear_final_button_text”字段。packages\apps\Settings\res\layout\master_clear_confirm.xml

android rest接口调用 android reshare.c_android rest接口调用_02


接下来找这个button 的点击事件。

packages\apps\Settings\src\com\android\settings\MasterClearConfirm.java
public class MasterClearConfirm extends InstrumentedFragment {

    private View mContentView;
    private boolean mEraseSdCard;
    private boolean mEraseEsims;

    /**
     * The user has gone through the multiple confirmation, so now we go ahead
     * and invoke the Checkin Service to reset the device to its factory-default
     * state (rebooting in the process).
     */
    private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {

        public void onClick(View v) {
            if (Utils.isMonkeyRunning()) {
                return;
            }

            final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
                    getActivity().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
            final OemLockManager oemLockManager = (OemLockManager)
                    getActivity().getSystemService(Context.OEM_LOCK_SERVICE);

            if (pdbManager != null && !oemLockManager.isOemUnlockAllowed() &&
                    Utils.isDeviceProvisioned(getActivity())) {
                // if OEM unlock is allowed, the persistent data block will be wiped during FR
                // process. If disabled, it will be wiped here, unless the device is still being
                // provisioned, in which case the persistent data block will be preserved.
                new AsyncTask<Void, Void, Void>() {
                    int mOldOrientation;
                    ProgressDialog mProgressDialog;

                    @Override
                    protected Void doInBackground(Void... params) {
                        pdbManager.wipe();
                        return null;
                    }

                    @Override
                    protected void onPostExecute(Void aVoid) {
                        mProgressDialog.hide();
                        if (getActivity() != null) {
                            getActivity().setRequestedOrientation(mOldOrientation);
                            doMasterClear();    //  点击后此方法
                        }
                    }

                    @Override
                    protected void onPreExecute() {
                        mProgressDialog = getProgressDialog();
                        mProgressDialog.show();   // 执行时 转圈等待

                        // need to prevent orientation changes as we're about to go into
                        // a long IO request, so we won't be able to access inflate resources on flash
                        mOldOrientation = getActivity().getRequestedOrientation();
                        getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
                    }
                }.execute();
            } else {
                doMasterClear();
            }
        }

        private ProgressDialog getProgressDialog() {
            final ProgressDialog progressDialog = new ProgressDialog(getActivity());
            progressDialog.setIndeterminate(true);
            progressDialog.setCancelable(false);
            progressDialog.setTitle(
                    getActivity().getString(R.string.master_clear_progress_title));
            progressDialog.setMessage(
                    getActivity().getString(R.string.master_clear_progress_text));
            return progressDialog;
        }
    };

    private void doMasterClear() {   //  重点是这个方法,其实是发送了一个广播。
        Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);//
        intent.setPackage("android");
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
        intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
        intent.putExtra(Intent.EXTRA_WIPE_ESIMS, mEraseEsims);
        getActivity().sendBroadcast(intent);  
        // Intent handling is asynchronous -- assume it will happen soon.
    }

    /**
     * Configure the UI for the final confirmation interaction
     */
    private void establishFinalConfirmationState() {
        mContentView.findViewById(R.id.execute_master_clear)
                .setOnClickListener(mFinalClickListener);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(
                getActivity(), UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId());
        if (RestrictedLockUtils.hasBaseUserRestriction(getActivity(),
                UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId())) {
            return inflater.inflate(R.layout.master_clear_disallowed_screen, null);
        } else if (admin != null) {
            new ActionDisabledByAdminDialogHelper(getActivity())
                    .prepareDialogBuilder(UserManager.DISALLOW_FACTORY_RESET, admin)
                    .setOnDismissListener(__ -> getActivity().finish())
                    .show();
            return new View(getActivity());
        }
        mContentView = inflater.inflate(R.layout.master_clear_confirm, null);
        establishFinalConfirmationState();
        setAccessibilityTitle();
        return mContentView;
    }

    private void setAccessibilityTitle() {
        CharSequence currentTitle = getActivity().getTitle();
        TextView confirmationMessage =
                (TextView) mContentView.findViewById(R.id.master_clear_confirm);
        if (confirmationMessage != null) {
            String accessibleText = new StringBuilder(currentTitle).append(",").append(
                    confirmationMessage.getText()).toString();
            getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibleText));
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Bundle args = getArguments();
        mEraseSdCard = args != null
                && args.getBoolean(MasterClear.ERASE_EXTERNAL_EXTRA);
        mEraseEsims = args != null
                && args.getBoolean(MasterClear.ERASE_ESIMS_EXTRA);
    }

    @Override
    public int getMetricsCategory() {
        return MetricsEvent.MASTER_CLEAR_CONFIRM;
    }
}

从上面可以看出,用户点击reset 后,其实是发送了一个名为ACTION_FACTORY_RESET(android.intent.action.FACTORY_RESET)的广播。这里值得注意的是:以前的广播为android.intent.action.MASTER_CLEAR,但是已经被ACTION_FACTORY_RESET 替换

/**
 * Deprecated - use ACTION_FACTORY_RESET instead.
 * @hide
 * @removed
 */
@Deprecated
@SystemApi
public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
2. 接下来查找接受这个广播的地方,通过全局grep 搜索 。得到:
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) {
            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 !!!");
            // 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 || mWipeEsims) { // 如果传过来 这个参数为true的话,则进行清除SD的操作。
                // thr will be started at the end of this task.
                new WipeDataTask(context, thr).execute();
            } else {
                thr.start();
            }
        }
    
        private class WipeDataTask extends AsyncTask<Void, Void, Void> { // 利用AsyncTask 清除SD数据。默认不清除
            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();
            }
    
        }
    }
3.RecoverySystem.java 中rebootWipeUserData 方法,擦除用户数据
frameworks\base\core\java\android\os\RecoverySystem.java
     /**
     * Reboots the device and wipes the user data and cache
     * partitions.  This is sometimes called a "factory reset", which
     * is something of a misnomer because the system partition is not
     * restored to its factory state.  Requires the
     * {@link android.Manifest.permission#REBOOT} permission.
     *
     * @param context   the Context to use
     * @param shutdown  if true, the device will be powered down after
     *                  the wipe completes, rather than being rebooted
     *                  back to the regular system.
     * @param reason    the reason for the wipe that is visible in the logs
     * @param force     whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction
     *                  should be ignored
     * @param wipeEuicc whether wipe the euicc data
     *
     * @throws IOException  if writing the recovery command file
     * fails, or if the reboot itself fails.
     * @throws SecurityException if the current user is not allowed to wipe data.
     *
     * @hide
     */
    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(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
        //重点是这个方法,将一些重要的字段信息保存起来
    }

bootCommand方法

/**
     * Reboot into the recovery system with the supplied argument.
     * @param args to pass to the recovery utility.
     * @throws IOException if something goes wrong.
     */
    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?)");
    }
4. 调用系统服务 RecoverySystemService.java
frameworks\base\services\core\java\com\android\server\RecoverySystemService.java
        @Override // Binder call
        public void rebootRecoveryWithCommand(String command) {
            if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
            synchronized (sRequestLock) {
                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);
            }
        }

	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;
    }
5.通过PowerManager 进行重启

这里有非常重要的一点:
在执行重启的时候,最终会走到以下的方法中:

frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
    /**
     * Low-level function to reboot the device. On success, this
     * function doesn't return. If more than 20 seconds passes from
     * the time a reboot is requested, this method returns.
     *
     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
     */
    public static void lowLevelReboot(String reason) {
        if (reason == null) {
            reason = "";
        }

        // If the reason is "quiescent", it means that the boot process should proceed
        // without turning on the screen/lights.
        // The "quiescent" property is sticky, meaning that any number
        // of subsequent reboots should honor the property until it is reset.
        if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
            sQuiescent = true;
            reason = "";
        } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
            sQuiescent = true;
            reason = reason.substring(0,
                    reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
        }

        if (reason.equals(PowerManager.REBOOT_RECOVERY)
                || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
            reason = "recovery";   //走这个,reason为 recovery
        }

        if (sQuiescent) {
            // Pass the optional "quiescent" argument to the bootloader to let it know
            // that it should not turn the screen/lights on.
            reason = reason + ",quiescent";
        }

        SystemProperties.set("sys.powerctl", "reboot," + reason);
        //通过设置reason的属性,则重启开机后,会进入到recovery 模式,进行清除数据
        try {
            Thread.sleep(20 * 1000L);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
    }

到此,android 系统复位的流程就分析完毕。总结一下:

恢复出厂设置总结:
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.清除成功后,系统重启,然后进入正常开机流程。