android 系统复位(Reset 恢复出厂设置)9.0源码分析

论坛 期权论坛 脚本     
匿名网站用户   2020-12-20 18:00   26   0

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

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

在这里插入图片描述
因为我这里面有TV项目,也有手机项目。这里就看手机项目,即“master_clear_final_button_text”字段。

packages\apps\Settings\res\layout\master_clear_confirm.xml
在这里插入图片描述
接下来找这个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 进行重启,重启流程可参见android系统关机源码(9.0)流程分析

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

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.清除成功后,系统重启,然后进入正常开机流程。

参考博客:
Android6.0 Reset恢复出厂设置流程分析

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP