Reset 流程 :
1.原生设置中响应 恢复出厂设置 功能,继而发出重置广播(Intent.ACTION_FACTORY_RESET) .
2.frameWork层 接收到此广播,根据广播所携带的参数执行Android层的Reset设定.
3.Android层执行到最后会将Reset配置信息写入 /cache/recovery/command 中,最终进入Recover.
Reset 逻辑流程图:
?图片来源 :?https://blog.csdn.net/qq_34787560/article/details/89226050
Reset 代码追踪:
1.UI界面点击:
packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java
private void doMasterClear() {
Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
intent.setPackage("android");
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
intent.putExtra(Intent.EXTRA_WIPE_ESIMS, mEraseEsims);
getActivity().sendBroadcast(intent);
// Intent handling is asynchronous -- assume it will happen soon.
}
在此注意,广播需要携带的参数.
2.framework接收 Reset 广播 (Intent.ACTION_FACTORY_RESET)
在Android 源码中,有对Intent的封装如下.
/**
* A broadcast action to trigger a factory reset.
*
* <p>The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission. The
* reason for the factory reset should be specified as {@link #EXTRA_REASON}.
*
* <p>Not for use by third-party applications.
*
* @see #EXTRA_FORCE_FACTORY_RESET
*
* {@hide}
*/
@SystemApi
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
在 framework/base/core/res/AndroidManifest.xml 中声明 MasterClearReceiver .java静态接收 Reset 广播.
<receiver android:name="com.android.server.MasterClearReceiver"
android:permission="android.permission.MASTER_CLEAR">
<intent-filter
android:priority="100" >
<!-- For Checkin, Settings, etc.: action=FACTORY_RESET -->
<action android:name="android.intent.action.FACTORY_RESET" />
<!-- As above until all the references to the deprecated MASTER_CLEAR get updated to
FACTORY_RESET. -->
<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>
3.在 MasterClearReceiver .java 执行?onReceive() 方法
framework/base/services/core/java/com/android/server/MasterClearReceiver .java
@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;
}
}
if (Intent.ACTION_MASTER_CLEAR.equals(intent.getAction())) {
Slog.w(TAG, "The request uses the deprecated Intent#ACTION_MASTER_CLEAR, "
+ "Intent#ACTION_FACTORY_RESET should be used instead.");
}
if (intent.hasExtra(Intent.EXTRA_FORCE_MASTER_CLEAR)) {
Slog.w(TAG, "The request uses the deprecated Intent#EXTRA_FORCE_MASTER_CLEAR, "
+ "Intent#EXTRA_FORCE_FACTORY_RESET should be used instead.");
}
final boolean shutdown = intent.getBooleanExtra("shutdown", false);
final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, 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 !!!");
// 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, forceWipe, mWipeEsims);
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 (mWipeExternalStorage || mWipeEsims) {
// thr will be started at the end of this task.
new WipeDataTask(context, thr).execute();
} else {
thr.start();
}
}
重点有 run() 方法里的?
1.RecoverySystem?.rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims);
2.new WipeDataTask(context, thr).execute();
第一个方法是百分百会被执行到的,是 Reset 的必经之地,稍后分析.
第二个是在发广播时 携带进来的参数.
? ? ? ? intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard); ? ? ? ? intent.putExtra(Intent.EXTRA_WIPE_ESIMS, mEraseEsims);
当任意一个参数 为 true 时,即会执行以下代码,清空 data 分区.
private class WipeDataTask extends AsyncTask<Void, Void, Void> {
private final Thread mChainedTask;
private final Context mContext;
private final ProgressDialog mProgressDialog;
public WipeDataTask(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");
if (mWipeExternalStorage) {
StorageManager sm = (StorageManager) mContext.getSystemService(
Context.STORAGE_SERVICE);
sm.wipeAdoptableDisks();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
mProgressDialog.dismiss();
mChainedTask.start();
}
}
4. RecoverySystem.java 中的 rebootWipeUserData() 方法
frameworks/base/core/java/android/os/RecoverySystem.java
/**
* 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.
* @param reason the reason for the wipe that is visible in the logs
* @param force whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction
* should be ignored
* @param wipeEuicc whether wipe the euicc data
*
* @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,
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);
}
final ConditionVariable condition = new ConditionVariable();
Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
?在?rebootWipeUserData() 中 开启了 ConditionVariable 锁,并且发送了?android.intent.action.MASTER_CLEAR_NOTIFICATION 这条广播,是为了在 Reset 时,可以将系统内APP的数据清除,在清除完APP数据后,再将锁关闭.
最后针对传入的不同的值,拟定好 Reset 用到的 string 变量,最终将所有参数传给当前类中的
bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
/**
* 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 {
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?)");
}
此处将所有的 Reset 参数拼接起来.调用?rebootRecoveryWithCommand();
/**
* Talks to RecoverySystemService via Binder to set up the BCB command and
* reboot into recovery accordingly.
*/
private void rebootRecoveryWithCommand(String command) {
try {
mService.rebootRecoveryWithCommand(command);
} catch (RemoteException ignored) {
}
}
? ? private final IRecoverySystem mService;
?在 RecoverySystem.java 文件中, mService 使用的是?IRecoverySystem.java 的实现类.
5. RecoverySystemService.java 继承 IRecoverySystem
?frameworks/base/services/core/java/com/android/server/RecoverySystemService.java
@Override // Binder call
public void rebootRecoveryWithCommand(String command) {
if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
synchronized (sRequestLock) {
if (!setupOrClearBcb(true, command)) {
return;
}
// Having set up the BCB, go ahead and reboot.
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
pm.reboot(PowerManager.REBOOT_RECOVERY);
}
}
?frameworks\base\core\java\android\os\PowerManager.java
/**
* Reboot the device. Will not return if the reboot is successful.
* <p>
* Requires the {@link android.Manifest.permission#REBOOT} permission.
* </p>
*
* @param reason code to pass to the kernel (e.g., "recovery") to
* request special boot modes, or null.
*/
public void reboot(String reason) {
try {
mService.reboot(false, reason, true);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
final IPowerManager mService;
?frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
/**
* 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)
|| PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
}
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
final String reason, boolean wait) {
if (mHandler == null || !mSystemReady) {
if (RescueParty.isAttemptingFactoryReset()) {
// If we're stuck in a really low-level reboot loop, and a
// rescue party is trying to prompt the user for a factory data
// reset, we must GET TO DA CHOPPA!
PowerManagerService.lowLevelReboot(reason);
} else {
throw new IllegalStateException("Too early to call shutdown() or reboot()");
}
}
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
ShutdownThread.rebootSafeMode(getUiContext(), confirm);
} else if (haltMode == HALT_MODE_REBOOT) {
ShutdownThread.reboot(getUiContext(), reason, confirm);
} else {
ShutdownThread.shutdown(getUiContext(), reason, confirm);
}
}
}
};
// 省略部分代码,以下是代码调用的地方
private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
final String reason, boolean wait) {
if (mHandler == null || !mSystemReady) {
if (RescueParty.isAttemptingFactoryReset()) {
// If we're stuck in a really low-level reboot loop, and a
// rescue party is trying to prompt the user for a factory data
// reset, we must GET TO DA CHOPPA!
PowerManagerService.lowLevelReboot(reason);
} else {
throw new IllegalStateException("Too early to call shutdown() or reboot()");
}
}
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
ShutdownThread.rebootSafeMode(getUiContext(), confirm);
} else if (haltMode == HALT_MODE_REBOOT) {
ShutdownThread.reboot(getUiContext(), reason, confirm);
} else {
ShutdownThread.shutdown(getUiContext(), reason, confirm);
}
}
}
};
?frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
/**
* 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. This must be a context
* suitable for displaying UI (aka Themable).
* @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;
mRebootHasProgressBar = false;
mReason = reason;
shutdownInner(context, confirm);
}
// 省略部分代码,以下为调用部分
private static void shutdownInner(final Context context, boolean confirm) {
// ShutdownThread is called from many places, so best to verify here that the context passed
// in is themed.
context.assertRuntimeOverlayThemable();
// ensure that only one thread is trying to power down.
// any additional calls are just returned
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Request to shutdown already running, returning.");
return;
}
}
final int longPressBehavior = context.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
final int resourceId = mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_confirm
: (longPressBehavior == 2
? com.android.internal.R.string.shutdown_confirm_question
: com.android.internal.R.string.shutdown_confirm);
Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
if (sConfirmDialog != null) {
sConfirmDialog.dismiss();
}
sConfirmDialog = new AlertDialog.Builder(context)
.setTitle(mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_title
: com.android.internal.R.string.power_off)
.setMessage(resourceId)
.setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
beginShutdownSequence(context);
}
})
.setNegativeButton(com.android.internal.R.string.no, null)
.create();
closer.dialog = sConfirmDialog;
sConfirmDialog.setOnDismissListener(closer);
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
sConfirmDialog.show();
} else {
beginShutdownSequence(context);
}
}
// 省略部分代码,以下为调用部分
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
return;
}
sIsStarted = true;
}
sInstance.mProgressDialog = showShutdownDialog(context);
sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
// make sure we never fall asleep again
sInstance.mCpuWakeLock = null;
try {
sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
sInstance.mCpuWakeLock.setReferenceCounted(false);
sInstance.mCpuWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
sInstance.mCpuWakeLock = null;
}
// also make sure the screen stays on for better user experience
sInstance.mScreenWakeLock = null;
if (sInstance.mPowerManager.isScreenOn()) {
try {
sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
sInstance.mScreenWakeLock.setReferenceCounted(false);
sInstance.mScreenWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
sInstance.mScreenWakeLock = null;
}
}
if (SecurityLog.isLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
}
// start the thread that initiates shutdown
sInstance.mHandler = new Handler() {
};
sInstance.start();
}
三:Factory Reset 恢复出厂设置
1. 用户选择“恢复出厂设置” 2. 设置系统将“--wipe_data”命令写入 /cache/recovery/command 3. 系统重启,并进入recovery模式 (sbin/recovery ?or /system/bin/recovery) 4. recovery get_args() 将“boot-recovery”和“--wipe_data”写入BCB 5. erase_root 格式化DATA 分区 6. erase_root 格式化CACHE 分区 7. finish_recovery 擦除BCB分区 8. 重启系统
|