MasterResetの流れ

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

#ざっくりの流れ
ここに書いてある。
http://androidxref.com/6.0.0_r1/xref/bootable/recovery/recovery.cpp

```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はひとまず省略。
- 最後のボタン押すと下記の処理が走る

```java: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

```xml: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する用？
- メインの処理は下記。

```java: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を見る

```java: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する

```java: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。

#おわりに
- けっこうわかりやすい。
- recoveryはどこから呼ばれてるのかと。init.rcか？ これか？   
http://androidxref.com/6.0.0_r1/xref/bootable/recovery/etc/init.rc