adb shell am broadcast -a android.intent.action.MASTER_CLEAR
adb shell 'echo "--wipe_data\n--locale=en_US" > /cache/recovery/command'
adb shell setprop sys.powerctl reboot,recovery
在确定【要恢复出厂设置吗】页面点击确定【清除全部内容】按钮,系统会发送恢复出厂设置的广播:
/packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java
package com.android.settings;
...
public class MasterClearConfirm extends OptionsMenuFragment {
private View mContentView;
private boolean mEraseSdCard;
//02用户点击恢复出厂设置按钮触发的点击事件
private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
public void onClick(View v) {
...
//03发送恢复出厂设置的广播事件
doMasterClear();
...
}
...
};
//04发送恢复出厂设置的广播事件
private void doMasterClear() {
//ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR"
Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
//该参数表示是否擦除SD卡,默认为False
intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
//发送恢复出厂设置的广播事件
getActivity().sendBroadcast(intent);
}
/**
* Configure the UI for the final confirmation interaction
*/
private void establishFinalConfirmationState() {
//01为恢复出厂设置按钮添加点击事件
mContentView.findViewById(R.id.execute_master_clear)
.setOnClickListener(mFinalClickListener);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
//设置恢复出厂设置确认页面的布局文件
mContentView = inflater.inflate(R.layout.master_clear_confirm, null);
...
return mContentView;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
mEraseSdCard = args != null && args.getBoolean(MasterClear.ERASE_EXTERNAL_EXTRA);
}
@Override
protected int getMetricsCategory() {
return MetricsEvent.MASTER_CLEAR_CONFIRM;
}
}
从上面的代码我们可以知道点击恢复出厂按钮最终是调用了doMasterClear方法,然后在该方法中发送了一个恢复出厂设置的广播事件。
/framework/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>
/frameworks/base/services/core/java/com/android/server/MasterClearReceiver.java
package com.android.server;
...
public class MasterClearReceiver extends BroadcastReceiver {
private static final String TAG = "MasterClear";
@Override
public void onReceive(final Context context, final Intent intent) {
...
final boolean shutdown = intent.getBooleanExtra("shutdown", false);
final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
final boolean wipeExternalStorage = intent.getBooleanExtra( Intent.EXTRA_WIPE_EXTERNAL_STORAGE, 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 !!!");
// 创建一个线程处理重启任务
Thread thr = new Thread("Reboot") {
@Override
public void run() {
try {
//重启并擦除用户信息
RecoverySystem.rebootWipeUserData(context, shutdown, reason, forceWipe);
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 (wipeExternalStorage) {
// 线程将会在下面的任务执行完毕以后开始执行
new WipeAdoptableDisksTask(context, thr).execute();
} else {
thr.start();
}
}
//继承自AsyncTask类,此处擦除的SdCard指的是Android M(6.0)新加入的
//内置存储设备的功能中的存储位置SD卡或者USB存储设备,不是传统data分区下的SdCard
//手机进入Recovery模式后,直接擦除data分区,SdCard目录包含在内,这样SdCard必然会被擦除,无需再做多余操作
private class WipeAdoptableDisksTask extends AsyncTask<Void, Void, Void> {
private final Thread mChainedTask;
private final Context mContext;
private final ProgressDialog mProgressDialog;
public WipeAdoptableDisksTask(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");
StorageManager sm = (StorageManager) mContext.getSystemService(
Context.STORAGE_SERVICE);
//具体逻辑请看下面的StorageManager.java对象
sm.wipeAdoptableDisks();
return null;
}
@Override
protected void onPostExecute(Void result) {
mProgressDialog.dismiss();
mChainedTask.start();//启动重启并擦除用户数据的线程
}
}
}
/frameworks/base/core/java/android/os/storage/StorageManager.java
public class StorageManager {
...
public void wipeAdoptableDisks() {
// We only wipe devices in "adoptable" locations, which are in a
// long-term stable slot/location on the device, where apps have a
// reasonable chance of storing sensitive data. (Apps need to go through
// SAF to write to transient volumes.)
final List<DiskInfo> disks = getDisks();
for (DiskInfo disk : disks) {
final String diskId = disk.getId();
if (disk.isAdoptable()) {
Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
try {
// TODO: switch to explicit wipe command when we have it,
// for now rely on the fact that vfat format does a wipe
mStorageManager.partitionPublic(diskId);
} catch (Exception e) {
Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
}
} else {
Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
}
}
}
...
}
frameworks/base/core/java/android/os/RecoverySystem.java
public class RecoverySystem {
...
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传递命令时,封装参数 --wipe_data,–locale,这些命令可以在recovery log(/cache/recovery/*.log)信息中看到
Command: “recovery” “–wipe_data” “–locale=zh_CN”
判断是否可以执行恢复出厂设置操作,可以,发送系统广播,设置command参数,随后进入bootCommand方法
frameworks/base/core/java/android/os/RecoverySystem.java
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?)");
}
