一、Android恢复出厂设置的三种方式
1【系统设置页面】进入【恢复出厂设置页面】,点击【恢复出厂设置】按钮。
2 直接通过adb发送恢复出厂设置的广播
adb shell am broadcast -a android.intent.action.MASTER_CLEAR
3 使用adb命令修改recovery的command文件,并重启系统进入recovery模式
adb shell 'echo "--wipe_data\n--locale=en_US" > /cache/recovery/command'
adb shell setprop sys.powerctl reboot,recovery
二、在【恢复出厂设置】页面点击【恢复出厂设置】按钮,触发的流程主要有以下几步:
- 点击按钮触发恢复出厂设置事件
- 发送"android.intent.action.MASTER_CLEAR"广播
- 系统MasterClearReceiver接收广播,并进行android层的相关处理最后重启
- 在/cache/recovery/command文件中写入命令字段
- 重启系统,进入Recovery模式
- 根据/cache/recovery/command中的命令字段清楚用户数据
- 重新启动系统,恢复出厂设置成功
三、点击【恢复出厂设置】按钮,系统源码流程分析
3.1 点击按钮,系统发送恢复出厂设置的广播
在确定【要恢复出厂设置吗】页面点击确定【清除全部内容】按钮,系统会发送恢复出厂设置的广播:
/packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java
package com.android.settings;
...
public class MasterClearConfirm extends OptionsMenuFragment {
private View mContentView;
private boolean mEraseSdCard;
private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
public void onClick(View v) {
...
doMasterClear();
...
}
...
};
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);
}
private void establishFinalConfirmationState() {
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方法,然后在该方法中发送了一个恢复出厂设置的广播事件。
二、接受恢复出厂设置的广播
3.2 系统MasterClearReceiver接收广播,并进行android层的相关处理
- MasterClearReceiver广播组件的配置信息:
/framework/base/core/res/AndroidManifest.xml
<receiver android:name="com.android.server.MasterClearReceiver"
android:permission="android.permission.MASTER_CLEAR">
<intent-filter
android:priority="100" >
<action android:name="android.intent.action.MASTER_CLEAR" />
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="android.intent.category.MASTER_CLEAR" />
</intent-filter>
</receiver>
- MasterClearReceiver广播组件的源码信息:
/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();
}
}
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);
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() {
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 {
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());
}
}
}
...
}
3.3 RecoverySystem来重启系统,启动擦除用户数据的操作
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);
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方法
2.4 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");
}
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
rs.rebootRecoveryWithCommand(command.toString());
throw new IOException("Reboot failed (no permissions?)");
}
|