开篇先啰嗦几句:
Android系统原生 "设置" 应用都会有恢复出厂设置这一功能。此功能的主要意图是为了擦除用户在使用手机的时候产生的用户数据,让手机生态系统恢复到出厂时的STATUS。
刚开始准备分析这个功能的时候,一时半会还不知道如何入手,因为我是做系统的,意识里面就想着从Frameworks入手,仔细一想,不对啊!茫茫Framewroks大海,入口在哪儿?很快地,我的想法就不攻自破,是行不通的。
开篇不是说了 "设置" 应用有这个 FUNCTION吗?咱们就找到这个突破口,各个击破。额,好像我废话有点多,时间是宝贵的,为了节省大家的时间,我就不兜圈子了,直奔主题吧。
上天总是眷顾有心的人的,果不其然,"ERASE EVERYTHING" BUTTON被我找着了。那咱们就从搜索字符串 "ERASE EVERYTHING" 开始我们的旅途吧。
小蝌蚪找妈妈,找呀找呀...
咦!找到了。
通过查找 "ERASE EVERYTHING" 字符串找到master_clear_confirm.xml文件,离成功又近了一步,咱们继续看看XML这货都有啥戏法。
仔细的同学会发现,在这个XML文件里面深藏着BUTTON的ID:execute_master_clear。
接着,
通过此ID找到了JAVA文件MasterClearConfirm.java(Settings),仿佛看到黎明的曙光了。
/**
- Configure the UI for the final confirmation interaction
*/
private void establishFinalConfirmationState() {
mContentView.findViewById(R.id.execute_master_clear)
.setOnClickListener(mFinalClickListener);
}
JAVA文件MasterClearConfirm.java这段代码碎片是我以ID换回来的,发现其实这里就是简单设置了一个监听事件,自然我们要看看mFinalClickListener具体的实现了。
private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
public void onClick(View v) {
if (Utils.isMonkeyRunning()) {
return;
}
...
}else{
mhandler.sendEmptyMessage(0);
}
}
分析得知,这里做了一个跳转,好吧,古人云 "天将降大任于斯人也,必先劳其筋骨,饿其体肤!...",我忍,接着继续看看mhandler异步消息这厮里面都干了那些活?
从mhandler.sendEmptyMessage(0);看到传的what参数为0,看看handleMessage方法case 0部分代码:
private Handler mhandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0:
final PersistentDataBlockManager pdbManager =
(PersistentDataBlockManager)
getActivity().getSystemService(
Context.PERSISTENT_DATA_BLOCK_SERVICE);
if (pdbManager != null && !pdbManager.getOemUnlockEnabled()) {
// if OEM unlock is enabled, this will be wiped during FR process.
final ProgressDialog progressDialog = getProgressDialog();
progressDialog.show();
// need to prevent orientation changes as we're about to go into
// a long IO request, so we won't be able to access inflate resources
//on flash
final int oldOrientation = getActivity().getRequestedOrientation();
getActivity().setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_LOCKED);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
pdbManager.wipe();
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
progressDialog.hide();
getActivity().setRequestedOrientation(oldOrientation);
doMasterClear();
}
}.execute();
} else {
doMasterClear();
}
从代码看到,无论是走if这条道还是else这条路,都会掉入doMasterClear()[上述代码标注下划线方法,主角总是会突出,自带光环的]这个坑。
虽说是坑,来都来了,看看吧。作为程序员,我们要时刻保持着不到长城不罢休,不到黄河心不死的决心。代码虐我千百遍,我却待她如初恋。
private void doMasterClear() {
if (mEraseSdCard) {
Intent intent = new
Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
Log.d(TAG, "startService FORMAT_AND_FACTORY_RESET");
getActivity().startService(intent);
} else {
Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
Log.d(TAG, "send broadcast ACTION_MASTER_CLEAR");
getActivity().sendBroadcast(intent);
// Intent handling is asynchronous -- assume it will happen soon.
}
}
好在这个不是深坑,道路很清晰,正如代码告诉我们的,如果mEraseSdCard为真,则发送外部存储卡格式化相关等广播,ELSE就发送擦除广播走正常的恢复出厂设置。
既然有广播发送,那么总得有接受者吧?看看ACTION_MASTER_CLEAR这个在哪儿定义?
老套路,搜索字符串!
发现是定义在:frameworks/base/core/java/android/content/Intent.java
public static final String ACTION_MASTER_CLEAR =
"android.intent.action.MASTER_CLEAR";
<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>
通过ACTION_MASTER_CLEAR找到Framewroks层MasterClearReceiver.java,
// The reboot call is blocking, so we need to do it on another thread.
Thread thr = new Thread("Reboot") {
@Override
public void run() {
try {
Slog.d(TAG, "Call mtehod: rebootWipeUserData");
RecoverySystem.rebootWipeUserData(context, shutdown, reason);
Slog.e(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方法真身。_!
图R-1
这个方法里面代码还不算多,意图也明确,就是对reason等入参做数据封装进一步的处理,然后丢给bootCommand方法就完事了。
rebootWipeUserData完事了,但我们的脚步还不能停下来,砂锅还没有打破。
private static void bootCommand(Context context, String... args) throws IOException
{
synchronized (sRequestLock) {
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).
RecoverySystem rs = (RecoverySystem) context.getSystemService(
Context.RECOVERY_SERVICE);
rs.setupBcb(command.toString());
// Having set up the BCB, go ahead and reboot.
PowerManager pm = (PowerManager)
context.getSystemService(Context.POWER_SERVICE);
pm.reboot(PowerManager.REBOOT_RECOVERY);
throw new IOException("Reboot failed (no permissions?)");
}
}
到这里,可能同学疑问就来了,通过bootCommand传进来的长参到哪儿去了?这个其实不用太关心,既然问到了,在这里简单说下,不然留下心结不好,容易憋出病,嘎嘎!我们只需要知道,这个参数最终会存放在系统的cache/recovery/command,当系统重启起来后,会去检测这个地方的值来决定下一步的操作,一切命令听指挥嘛!
好了言归正传,接着说下PowerManager中的reboot方法。
图R-2
final IPowerManager mService;看到这里,流程要走到C++实现层了。到这里应用框架层流程就分析得差不多了。
打卡上岸!
总结
pm.reboot实现了重启的功能。根据正文所讲,重启之后首先擦除用户操作过程产生的数据,甚至擦除用户安装的三方APK,之后又回到Android系统。