先使用grep命令搜索"恢复出厂设置"字符串,找到相应的布局文件:
packages/apps/Settings/res/xml/privacy_settings.xml
在这个节点下我们可以看到这么一句话:
android:fragment="com.android.settings.MasterClear"
这句话表示当点击"恢复出厂设置"这个item后,会直接跳转到MasterClear.java这个Fragment子类内:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (!Process.myUserHandle().isOwner()
|| UserManager.get(getActivity()).hasUserRestriction(
UserManager.DISALLOW_FACTORY_RESET)) {
return inflater.inflate(R.layout.master_clear_disallowed_screen, null);
}
mContentView = inflater.inflate(R.layout.master_clear, null);
establishInitialState();
return mContentView;
}
判断用户是否具有"恢复出厂设置"的权限,根据判断结果来加载布局;由于分析的是"恢复出厂设置"流程,所有直接默认它加载的是master_clear.xml布局。
/**
* In its initial state, the activity presents a button for the user to
* click in order to initiate a confirmation sequence. This method is
* called from various other points in the code to reset the activity to
* this base state.
*
* Reinflating views from resources is expensive and prevents us from
* caching widget pointers, so we use a single-inflate pattern: we lazy-
* inflate each view, caching all of the widget pointers we'll need at the
* time, then simply reuse the inflated views directly whenever we need
* to change contents.
*/
private void establishInitialState() {
mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_master_clear);
mInitiateButton.setOnClickListener(mInitiateListener);
mExternalStorageContainer = mContentView.findViewById(R.id.erase_external_container);
mExternalStorage = (CheckBox) mContentView.findViewById(R.id.erase_external);
/*
* If the external storage is emulated, it will be erased with a factory
* reset at any rate. There is no need to have a separate option until
* we have a factory reset that only erases some directories and not
* others. Likewise, if it's non-removable storage, it could potentially have been
* encrypted, and will also need to be wiped.
*/
boolean isExtStorageEmulated = false;
StorageHelpUtil mStorageHelpUtil = new StorageHelpUtil();
if (mStorageHelpUtil.getSdCardPath(getActivity()) != null) {
isExtStorageEmulated = true;
}
if (isExtStorageEmulated) {
mExternalStorageContainer.setVisibility(View.VISIBLE);
final View externalOption = mContentView.findViewById(R.id.erase_external_option_text);
externalOption.setVisibility(View.VISIBLE);
final View externalAlsoErased = mContentView.findViewById(R.id.also_erases_external);
externalAlsoErased.setVisibility(View.VISIBLE);
// If it's not emulated, it is on a separate partition but it means we're doing
// a force wipe due to encryption.
mExternalStorage.setChecked(!isExtStorageEmulated);
} else {
mExternalStorageContainer.setVisibility(View.GONE);
final View externalOption = mContentView.findViewById(R.id.erase_external_option_text);
externalOption.setVisibility(View.GONE);
mExternalStorageContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mExternalStorage.toggle();
}
});
}
final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
loadAccountList(um);
StringBuffer contentDescription = new StringBuffer();
View masterClearContainer = mContentView.findViewById(R.id.master_clear_container);
getContentDescription(masterClearContainer, contentDescription);
masterClearContainer.setContentDescription(contentDescription);
}
1、初始化"恢复出厂设置"按钮并未其设置点击事件监听mInitiateListener;
接下来再来看mInitiateListener监听器:
/**
* If the user clicks to begin the reset sequence, we next require a
* keyguard confirmation if the user has currently enabled one. If there
* is no keyguard available, we simply go to the final confirmation prompt.
*/
private final Button.OnClickListener mInitiateListener = new Button.OnClickListener() {
public void onClick(View v) {
if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) {
Intent intent = new Intent("android.settings.PASSWORD_MANAGER");
startActivityForResult(intent,PASSWORD_MANAGER);
}
}
};
startActivityForResult()方法进入密码界面,并返回结果,直接来看onActivityResult()方法:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode != KEYGUARD_REQUEST && requestCode != PASSWORD_MANAGER) {
return;
} else if (requestCode == PASSWORD_MANAGER) {
if (resultCode == Activity.RESULT_OK) {
validatePassword = data.getExtras().getBoolean("password");
if (validatePassword) {
showFinalConfirmation();
}
}
return;
}
// If the user entered a valid keyguard trace, present the final
// confirmation prompt; otherwise, go back to the initial state.
if (resultCode == Activity.RESULT_OK) {
showFinalConfirmation();
} else {
establishInitialState();
}
}
如果密码确认,调用showFinalConfirmation()方法:
private void showFinalConfirmation() {
Bundle args = new Bundle();
args.putBoolean(ERASE_EXTERNAL_EXTRA, mExternalStorage.isChecked());
((SettingsActivity) getActivity()).startPreferencePanel(MasterClearConfirm.class.getName(),
args, R.string.master_clear_confirm_title, null, null, 0);
}
进入MasterClearConfirm.java内,并传入是否勾选清除sd卡数据的参数;
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.
}
这个方法主要是用来发送广播,通知android系统开始"恢复出厂设置",最终的接收方法广播的地方是在
onReceiver()方法:
@Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
if (!"google.com".equals(intent.getStringExtra("from"))) {
Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");
return;
}
}
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);
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);
}
}
};
if (wipeExternalStorage) {
// thr will be started at the end of this task.
new WipeAdoptableDisksTask(context, thr).execute();
} else {
thr.start();
}
}
通过对比发送广播的地方,只需要关心两个参数reason和wipeExternalStorage;
1、wipeExternalStorage为true时,表示需要清除sdcard的数据;开启一个异步任务:
new WipeAdoptableDisksTask(context, thr).execute();
private class WipeAdoptableDisksTask extends AsyncTask {
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();
}
}
1)首先执行onPreExecute()方法,此时是在UI Thread内;设置Dialog,提醒用户;
2、wipeExternalStorage为false时,表示只需要清除用户数据;此时只要需要开启执行thr线程即可;
thr.start();
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.java的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);
}
1、查看当前用户是否有权限清除数据;
/**
* 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?)");
}
1、将传入的命令参数写入到"/cache/recovery/command"文件中去;
public void reboot(String reason) {
try {
mService.reboot(false, reason, true);
} catch (RemoteException e) {
}
}
aidl调入到PowerManagerService.java的reboot()方法:
/**
* Reboots the device.
*
* @param confirm If true, shows a reboot confirmation dialog.
* @param reason The reason for the reboot, or null if none.
* @param wait If true, this call waits for the reboot to complete and does not return.
*/
@Override // Binder call
public void reboot(boolean confirm, String reason, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
if (PowerManager.REBOOT_RECOVERY.equals(reason)) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
}
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(false, confirm, reason, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
首先检查是否有REBOOT和RECOVERY权限,然后调用shutdownOrRebootInternal()方法:
private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
final String reason, boolean wait) {
if (mHandler == null || !mSystemReady) {
throw new IllegalStateException("Too early to call shutdown() or reboot()");
}
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
if (shutdown) {
ShutdownThread.shutdown(mContext, confirm);
} else {
ShutdownThread.reboot(mContext, reason, confirm);
}
}
}
};
// ShutdownThread must run on a looper capable of displaying the UI.
Message msg = Message.obtain(mHandler, runnable);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
// PowerManager.reboot() is documented not to return so just wait for the inevitable.
if (wait) {
synchronized (runnable) {
while (true) {
try {
runnable.wait();
} catch (InterruptedException e) {
}
}
}
}
}
这个方法最主要的还是Runnable内的run()方法实现,由于之前传进来的shutdown为false,于是继续调用:
ShutdownThread.reboot(mContext, reason, confirm);
/**
* Request a clean shutdown, waiting for subsystems to clean up their
* state etc. Must be called from a Looper thread in which its UI
* is shown.
*
* @param context Context used to display the shutdown progress dialog.
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
mRebootSafeMode = false;
mRebootUpdate = false;
mRebootReason = reason;
shutdownInner(context, confirm);
}
继续调用shutdownInner()方法,此时confirm为false;
设备重启后,会自动进入recovery mode模式,读取/cache/recovery/command, 內容为"--wipe_data",开始清除data和cache分区,清除成功后,系统重启,然后进入正常开机流程,重新使用system分区的内容完成开机初始化,此过程跟我们第一次烧写软件过程一致。
至此完成恢复出厂设置。
恢复出厂设置总结:
1、在MasterClearConfirm.java中确认开始执行恢复出厂设置操作,并发出"恢复出厂设置"的广播;
2、在MasterClearReceiver.java接收MasterClearConfirm.java发出的广播,根据是否清除sdcard选项来执行相应的操作;
3、调用RecoverySystem.rebootWipeUserData()方法来清除用户数据并重启设备;这个方法执行过程中会发出"android.intent.action.MASTER_CLEAR_NOTIFICATION"广播、写"/cache/recovery/command"文件(内容包含"--wipe_data"),然后重启设备;
4、设备重启后进入recovery mode之后,读取/cache/recovery/command, 內容为"--wipe_data";
5.按照读取的command,进行wipe data清除数据操作;
6.清除成功后,系统重启,然后进入正常开机流程。