Android4.4.2恢复出厂设置(二)
Android4.4.2恢复出厂设置(二)
根据前文所述,UI层最终发送Intent消息MASTER_CLEAR
进行恢复出厂设置的操作,这部分的流程如下图所示:
MASTER_CLEAR接收器的设定
经过搜索AndroidManifest.xml
文件可知,对MASTER_CLEAR
的最终的处理在文件frameworks/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/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;
}
}
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);
Log.wtf(TAG, "Still running after master clear?!");
} catch (IOException e) {
Slog.e(TAG, "Can't perform master clear/factory reset", e);
}
}
};
thr.start();
}
这部分的主要工作就是校验请求来源字符串是否为google.com
,如果不是则不能操作,如果是,则调用函数RecoverySystem.rebootWipeUserData(context)
执行重启并清理数据的操作。
rebootWipeUserData
函数rebootWipeUserData
实现在文件frameworks/base/core/java/android/os/RecoverySystem.java
中:
public static void rebootWipeUserData(Context context) throws IOException {
final ConditionVariable condition = new ConditionVariable();
Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
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();
bootCommand(context, "--wipe_data\n--locale=" + Locale.getDefault().toString());
}
这部分的内容很直接,可以理解为是调用函数bootCommand(context, "--wipe_data\n--locale=en_US")
进行重启即可。
bootCommand
函数bootCommand
实现在文件frameworks/base/core/java/android/os/RecoverySystem.java
中:
private static void bootCommand(Context context, String arg) 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 {
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("recovery");
throw new IOException("Reboot failed (no permissions?)");
}
即将传入参数写入文件COMMAND_FILE
然后通通过电源管理重启进入recovery
模式。而COMMAND_FILE
在该文件中的定义如下:
private static File RECOVERY_DIR = new File("/cache/recovery");
private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
也就是说,最终会在文件/cache/recovery/command
中写入如下信息:
--wipe_data
--locale=en_US
而调用的函数为pm.reboot("recovery")
。
reboot
接下来的函数reboot实现在文件frameworks/base/core/java/android/os/PowerManager.java
中,内容如下:
public void reboot(String reason) {
try {
mService.reboot(false, reason, true);
} catch (RemoteException e) {
}
}
而其调用到的PowerManagerService
中的函数位于frameworks/base/services/java/com/android/server/power/PowerManagerService.java
中:
@Override // Binder call
public void reboot(boolean confirm, String reason, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(false, confirm, reason, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
在进行了REBOOT
权限校验后,调用到了函数shutdownOrRebootInternal(false, false, "recovery", true)
继续重启操作。
shutdownOrRebootInternal
函数shutdownOrRebootInternal
实现在文件frameworks/base/services/java/com/android/server/power/PowerManagerService.java
中:
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) {
}
}
}
}
}
其传入参数分别表示:
- shutdown: 为false表示重启;
- confirm: 为false表示直接重启,无需弹出询问框再次确认;
- reason: 重启操作类型,如果是恢复出厂设置,则该值为
recovery
; - wait: 为true表示阻塞等待重启操作完成;
根据传入参数,这里最终是调用到函数ShutdownThread.reboot(mContext, "recovery", false)
,另外需要注意的是这里需要运行在具有UI显示的循环中。
reboot
ShutdownThread.reboot(mContext, "recovery", false)
函数实现在文件frameworks/base/services/java/com/android/server/power/ShutdownThread.java
中:
public static void reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
mRebootSafeMode = false;
mRebootReason = reason;
shutdownInner(context, confirm);
}
调用到的函数为shutdownInner(context, false)
,另外这里设置的mReboot为true,则表示为重启操作,否则表示关机操作,而mRebootReason的取值为recovery
字符串。
shutdownInner
函数shutdownInner
依然实现在文件frameworks/base/services/java/com/android/server/power/ShutdownThread.java
中:
static void shutdownInner(final Context context, boolean confirm) {
// 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);
}
}
这里的处理,其实主要是针对通过长按power键进入重启或模式的处理,对于通过Settings进入的,相当于直接调用到beginShutdownSequence(context)
。
beginShutdownSequence
函数beginShutdownSequence
实现在文件frameworks/base/services/java/com/android/server/power/ShutdownThread.java
中:
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
return;
}
sIsStarted = true;
}
// throw up an indeterminate system dialog to indicate radio is
// shutting down.
ProgressDialog pd = new ProgressDialog(context);
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
pd.show();
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;
}
}
// start the thread that initiates shutdown
sInstance.mHandler = new Handler() {
};
sInstance.start();
}
这部分主要是重启前关闭屏幕的操作,最下方的sInstance.start()
其实启动的是当前文件中的run
函数,具体实现如下:
public void run() {
BroadcastReceiver br = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
// We don't allow apps to cancel this, so ignore the result.
actionDone();
}
};
/*
* Write a system property in case the system_server reboots before we
* get to the actual hardware restart. If that happens, we'll retry at
* the beginning of the SystemServer startup.
*/
{
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
}
/*
* If we are rebooting into safe mode, write a system property
* indicating so.
*/
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast.
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) {
while (!mActionDone) {
long delay = endTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown broadcast timed out");
break;
}
try {
mActionDoneSync.wait(delay);
} catch (InterruptedException e) {
}
}
}
Log.i(TAG, "Shutting down activity manager...");
final IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
try {
am.shutdown(MAX_BROADCAST_TIME);
} catch (RemoteException e) {
}
}
// Shutdown radios.
shutdownRadios(MAX_RADIO_WAIT_TIME);
// Shutdown MountService to ensure media is in a safe state
IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
public void onShutDownComplete(int statusCode) throws RemoteException {
Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
actionDone();
}
};
Log.i(TAG, "Shutting down MountService");
// Set initial variables and time out time.
mActionDone = false;
final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
synchronized (mActionDoneSync) {
try {
final IMountService mount = IMountService.Stub.asInterface(
ServiceManager.checkService("mount"));
if (mount != null) {
mount.shutdown(observer);
} else {
Log.w(TAG, "MountService unavailable for shutdown");
}
} catch (Exception e) {
Log.e(TAG, "Exception during MountService shutdown", e);
}
while (!mActionDone) {
long delay = endShutTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown wait timed out");
break;
}
try {
mActionDoneSync.wait(delay);
} catch (InterruptedException e) {
}
}
}
rebootOrShutdown(mReboot, mRebootReason);
}
这里主要执行了如下这些操作:
- 写入系统属性
sys.shutdown.requested
,对于恢复系统设置,则写入值为recovery
; - 发送关机广播,默认等待超时时间为10秒;
- 依次关闭AMS、Radio、MountService等;
- 调用
rebootOrShutdown
进入重启或关机流程;
rebootOrShutdown
函数rebootOrShutdown
也实现在文件frameworks/base/services/java/com/android/server/power/ShutdownThread.java
中:
public static void rebootOrShutdown(boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
Log.e(TAG, "Reboot failed, will attempt shutdown instead");
} else if (SHUTDOWN_VIBRATE_MS > 0) {
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator();
try {
vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
} catch (Exception e) {
// Failure to vibrate shouldn't interrupt shutdown. Just log it.
Log.w(TAG, "Failed to vibrate during shutdown.", e);
}
// vibrator is asynchronous so we need to wait to avoid shutting down too soon.
try {
Thread.sleep(SHUTDOWN_VIBRATE_MS);
} catch (InterruptedException unused) {
}
}
// Shutdown power
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown();
}
其实这里主要就是调用到函数PowerManagerService.lowLevelReboot("recovery")
进行重启操作。
lowLevelReboot
函数lowLevelReboot
实现在文件frameworks/base/services/java/com/android/server/power/PowerManagerService.java
中:
public static void lowLevelReboot(String reason) {
if (reason == null) {
reason = "";
}
SystemProperties.set("sys.powerctl", "reboot," + reason);
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
很明显,这里是设置了属性值sys.powerctl
的值为reboot recovery
。其效用类似于命令adb shell setprop sys.powerctl reboot,recovery
。
总结
综上所述,恢复出厂设置,本质上可以总括为如下两条命令:
adb shell 'echo "--wipe_data\n--locale=en_US" > /cache/recovery/command'
adb shell setprop sys.powerctl reboot,recovery