LoginSignup
3

More than 5 years have passed since last update.

MasterResetの流れ

Posted at

どこにも需要がないと思いますがMasterResetについて。

ざっくりの流れ

ここに書いてある。
http://androidxref.com/6.0.0_r1/xref/bootable/recovery/recovery.cpp

recovery.cpp
 * FACTORY RESET
 * 1. user selects "factory reset"
 * 2. main system writes "--wipe_data" to /cache/recovery/command
 * 3. main system reboots into recovery
 * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
 *    -- after this, rebooting will restart the erase --
 * 5. erase_volume() reformats /data
 * 6. erase_volume() reformats /cache
 * 7. finish_recovery() erases BCB
 *    -- after this, rebooting will restart the main system --
 * 8. main() calls reboot() to boot main system

MasterResetすると/cache/recovery/commandというファイルに"--wipe_data"という文字列を書き込む。
起動時に/cache/recovery/commandに"--wipe_data"が書き込まれていれば/dataと/cacheの中身を削除する。それだけ。

もうすこし詳細な流れ

MasterClear(SettingsのUI)
http://androidxref.com/6.0.0_r1/xref/packages/apps/Settings/src/com/android/settings/MasterClear.java

MasterClearConfirm(SettingsのUI)
http://androidxref.com/6.0.0_r1/xref/packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java

MasterClearReceiver
http://androidxref.com/6.0.0_r1/xref/frameworks/base/services/core/java/com/android/server/MasterClearReceiver.java

RecoverySystem
http://androidxref.com/6.0.0_r1/xref/frameworks/base/core/java/android/os/RecoverySystem.java

再起動

recovery.cpp
http://androidxref.com/6.0.0_r1/xref/bootable/recovery/recovery.cpp

/cache/recovery/commandに"--wipe_data"と書き込まれていれば/data, /cacheをフォーマット

再起動

さらに詳細な流れ

  • UIはひとまず省略。
  • 最後のボタン押すと下記の処理が走る
packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java
    private void doMasterClear() {
        Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
        intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
        getActivity().sendBroadcast(intent);
        // Intent handling is asynchronous -- assume it will happen soon.
    }
  • Intent.ACTION_MASTER_CLEARをBroadcastしてる。
  • それをMasterClearReceiverが受け取る。
  • Manifest
frameworks/base/core/res/AndroidManifest.xml
        <receiver android:name="com.android.server.MasterClearReceiver"
            android:permission="android.permission.MASTER_CLEAR">
            <intent-filter
                    android:priority="100" >
                <!-- For Checkin, Settings, etc.: action=MASTER_CLEAR -->
                <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>
  • MASTER_CLEAR以外のactionはリモートでMasterResetする用?
  • メインの処理は下記。
frameworks/base/services/core/java/com/android/server/MasterClearReceiver.java

        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);
                    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);
                }
            }
        };
  • RecoverySystem#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.
     *
     * @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)
            throws IOException {
        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
        if (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);
        context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
                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();

        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().toString();
        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
    }
  • RecoverySystem#bootCommandで/cache/recovery/commandにコマンドを書き込んでrebootする
frameworks/base/core/java/android/os/RecoverySystem.java
    /**
     * 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 {
        RECOVERY_DIR.mkdirs();  // In case we need it
        COMMAND_FILE.delete();  // In case it's not writable
        LOG_FILE.delete();

        FileWriter command = new FileWriter(COMMAND_FILE);
        try {
            for (String arg : args) {
                if (!TextUtils.isEmpty(arg)) {
                    command.write(arg);
                    command.write("\n");
                }
            }
        } finally {
            command.close();
        }

        // Having written the command file, go ahead and reboot
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        pm.reboot(PowerManager.REBOOT_RECOVERY);

        throw new IOException("Reboot failed (no permissions?)");
    }
  • PowerManager#rebootを呼んだら処理が返ってこないのでpm.rebootのあとにthrow new IOExceptionしてる。へえ。
  • 再起動後の話はrecovery.cppのmain()読めばOK。

おわりに

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3