Rk3399 Android9.0 恢复出厂设置流程

5 篇文章 0 订阅
4 篇文章 0 订阅

Reset 流程 :

1.原生设置中响应 恢复出厂设置 功能,继而发出重置广播(Intent.ACTION_FACTORY_RESET) .

2.frameWork层 接收到此广播,根据广播所携带的参数执行Android层的Reset设定.

3.Android层执行到最后会将Reset配置信息写入 /cache/recovery/command 中,最终进入Recover.

Reset 逻辑流程图:

 图片来源 : https://blog.csdn.net/qq_34787560/article/details/89226050

Reset 代码追踪:

1.UI界面点击:

packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java

    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.
    }

在此注意,广播需要携带的参数.

2.framework接收 Reset 广播 (Intent.ACTION_FACTORY_RESET)

在Android 源码中,有对Intent的封装如下.

/**
     * A broadcast action to trigger a factory reset.
     *
     * <p>The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission. The
     * reason for the factory reset should be specified as {@link #EXTRA_REASON}.
     *
     * <p>Not for use by third-party applications.
     *
     * @see #EXTRA_FORCE_FACTORY_RESET
     *
     * {@hide}
     */
    @SystemApi
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";

在 framework/base/core/res/AndroidManifest.xml 中声明 MasterClearReceiver .java静态接收 Reset 广播.

        <receiver android:name="com.android.server.MasterClearReceiver"
            android:permission="android.permission.MASTER_CLEAR">
            <intent-filter
                    android:priority="100" >
                <!-- For Checkin, Settings, etc.: action=FACTORY_RESET -->
                <action android:name="android.intent.action.FACTORY_RESET" />
                <!-- As above until all the references to the deprecated MASTER_CLEAR get updated to
                     FACTORY_RESET. -->
                <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>

3.在 MasterClearReceiver .java 执行 onReceive() 方法

framework/base/services/core/java/com/android/server/MasterClearReceiver .java

    @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) {
            // thr will be started at the end of this task.
            new WipeDataTask(context, thr).execute();
        } else {
            thr.start();
        }
    }

重点有 run() 方法里的 

1.RecoverySystem .rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims);

2.new WipeDataTask(context, thr).execute();

第一个方法是百分百会被执行到的,是 Reset 的必经之地,稍后分析.

第二个是在发广播时 携带进来的参数.

        intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
        intent.putExtra(Intent.EXTRA_WIPE_ESIMS, mEraseEsims);

当任意一个参数 为 true 时,即会执行以下代码,清空 data 分区.

    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 sm = (StorageManager) mContext.getSystemService(
                        Context.STORAGE_SERVICE);
                sm.wipeAdoptableDisks();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            mProgressDialog.dismiss();
            mChainedTask.start();
        }

    }

4. 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);
    }

final ConditionVariable condition = new ConditionVariable();

Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");

 在 rebootWipeUserData() 中 开启了 ConditionVariable 锁,并且发送了 android.intent.action.MASTER_CLEAR_NOTIFICATION 这条广播,是为了在 Reset 时,可以将系统内APP的数据清除,在清除完APP数据后,再将锁关闭.

最后针对传入的不同的值,拟定好 Reset 用到的 string 变量,最终将所有参数传给当前类中的

bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);

    /**
     * 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?)");
    }

此处将所有的 Reset 参数拼接起来.调用 rebootRecoveryWithCommand();

    /**
     * Talks to RecoverySystemService via Binder to set up the BCB command and
     * reboot into recovery accordingly.
     */
    private void rebootRecoveryWithCommand(String command) {
        try {
            mService.rebootRecoveryWithCommand(command);
        } catch (RemoteException ignored) {
        }
    }

    private final IRecoverySystem mService;

 在 RecoverySystem.java 文件中, mService 使用的是 IRecoverySystem.java 的实现类.

5. RecoverySystemService.java 继承 IRecoverySystem

 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);
            }
        }

 frameworks\base\core\java\android\os\PowerManager.java

    /**
     * Reboot the device.  Will not return if the reboot is successful.
     * <p>
     * Requires the {@link android.Manifest.permission#REBOOT} permission.
     * </p>
     *
     * @param reason code to pass to the kernel (e.g., "recovery") to
     *               request special boot modes, or null.
     */
    public void reboot(String reason) {
        try {
            mService.reboot(false, reason, true);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

final IPowerManager mService;

 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, 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(getUiContext(), reason, confirm);
                    } else {
                        ShutdownThread.shutdown(getUiContext(), reason, confirm);
                    }
                }
            }
        };


      
        // 省略部分代码,以下是代码调用的地方
        



    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(getUiContext(), reason, confirm);
                    } else {
                        ShutdownThread.shutdown(getUiContext(), reason, confirm);
                    }
                }
            }
        };

 frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java

    /**
     * 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(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);
        }
    }


    // 省略部分代码,以下为调用部分


    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();
    }

三:Factory Reset 恢复出厂设置

1. 用户选择“恢复出厂设置”
2. 设置系统将“--wipe_data”命令写入 /cache/recovery/command
3. 系统重启,并进入recovery模式 (sbin/recovery  or /system/bin/recovery)
4. recovery get_args() 将“boot-recovery”和“--wipe_data”写入BCB
5. erase_root 格式化DATA 分区
6. erase_root 格式化CACHE 分区
7. finish_recovery 擦除BCB分区
8. 重启系统

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在RK3399 Android 10.0设备中设置静态IP,可以按照以下步骤操作: 1. 首先,在设备上找到并打开“设置”应用程序。 2. 在设置界面中,向下滚动直到找到“网络和互联网”选项,并点击进入。 3. 在网络和互联网菜单中,选择“以太网”选项。如果设备没有物理以太网接口,可能显示为“有线连接”或类似选项。 4. 进入以太网设置后,点击右上角的菜单按钮(通常是三个竖排的点或线),然后选择“高级”或“高级选项”。 5. 在高级设置中,寻找一个类似“IP设置”、“IP地址设置”或“连接类型”等选项,点击进入。 6. 在IP设置菜单中,通常默认为“DHCP”(动态IP分配)。如果要切换为静态IP,请选择“静态”或“手动”选项。 7. 在静态IP设置中,输入您想要使用的IP地址、子网掩码、默认网关和DNS服务器。确保这些信息准确无误,一般可以从网络管理员或路由器配置中获取。 8. 输入完毕后,点击“保存”或“应用”按钮,以应用并保存新的静态IP设置。 9. 返回网络设置主菜单,重新启动RK3399设备,以使新的静态IP设置生效。 在完成以上步骤后,您的RK3399 Android 10.0设备将会使用您所提供的静态IP地址进行以太网连接。请注意,静态IP设置通常用于特定网络环境中,如企业网络或技术调试,一般家庭用户使用动态IP更为常见。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Brave & Real

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值