どこにも需要がないと思いますがMasterResetについて。
#ざっくりの流れ
ここに書いてある。
http://androidxref.com/6.0.0_r1/xref/bootable/recovery/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はひとまず省略。
- 最後のボタン押すと下記の処理が走る
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
<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する用?
- メインの処理は下記。
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を見る
/**
* 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する
/**
* 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