admin管理员组文章数量:1597471
最近在学习Android系统源码,这一节主要是了解恢复出厂设置。实现恢复出厂一般是通过发广播操作,如下:
//恢复出厂设置
Intent recovery = new Intent("android.intent.action.MASTER_CLEAR");
recovery.setPackage("android");
sendBroadcast(recovery);
清单文件需要加:
1 android:shareUserId = "android.uid.system" //表明系统应用
2 <uses-permission android:name="android.permission.MASTER_CLEAR" />
它的主要流程如下:
1 点击按钮,系统发送恢复出厂设置广播
2 系统MasterClearReceiver接收广播,并进行android层的相关处理最后重启
3 在/cache/recovery/command文件中写入命令字段
4 重启系统,进入Recovery模式
5 根据/cache/recovery/command中的命令字段清楚用户数据
6 重新启动系统,恢复出厂完成
简单来看一下源码:
/frameworks/base/services/core/java/com/android/server/MasterClearReceiver.java
public class MasterClearReceiver extends BroadcastReceiver {
private static final String TAG = "MasterClear";
private boolean mWipeExternalStorage;
private boolean mWipeEsims;
@Override
public void onReceive(final Context context, final Intent intent) {
...
final String factoryResetPackage = context
.getString(com.android.internal.R.string.config_factoryResetPackage);
if (Intent.ACTION_FACTORY_RESET.equals(intent.getAction())
&& !TextUtils.isEmpty(factoryResetPackage)) {
intent.setPackage(factoryResetPackage).setComponent(null);
context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
return;
}
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) {
// thr will be started at the end of this task.
new WipeDataTask(context, thr).execute();
} else {
thr.start();
}
}
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();
}
}
}
<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>
/frameworks/base/core/java/android/os/RecoverySystem.java
public class RecoverySystem {
private final IRecoverySystem mService;
public RecoverySystem(IRecoverySystem service) {
mService = service;
}
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();
EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
if (wipeEuicc) {
wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
} else {
removeEuiccInvisibleSubs(context, euiccManager);
}
String shutdownArg = null;
if (shutdown) {
shutdownArg = "--shutdown_after";
}
String reasonArg = null;
if (!TextUtils.isEmpty(reason)) {
String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
}
final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
}
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.
// 将命令写到bootloader control block
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
rs.rebootRecoveryWithCommand(command.toString());
throw new IOException("Reboot failed (no permissions?)");
}
// 通过Binder与RecoverySystemService对话以设置BCB
private void rebootRecoveryWithCommand(String command) {
try {
mService.rebootRecoveryWithCommand(command);
} catch (RemoteException ignored) {
}
}
来看一下服务端:
/frameworks/base/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@Override // Binder call
public void rebootRecoveryWithCommand(String command) {
if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
synchronized (sRequestLock) {
if (!setupOrClearBcb(true, command)) {
return;
}
// 设置完CBC,调用电源管理重启系统,进入恢复模式
PowerManager pm = mInjector.getPowerManager();
pm.reboot(PowerManager.REBOOT_RECOVERY);
}
}
private boolean setupOrClearBcb(boolean isSetup, String command) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
final boolean available = checkAndWaitForUncryptService();
if (!available) {
Slog.e(TAG, "uncrypt service is unavailable.");
return false;
}
if (isSetup) {
mInjector.systemPropertiesSet("ctl.start", "setup-bcb");
} else {
mInjector.systemPropertiesSet("ctl.start", "clear-bcb");
}
// Connect to the uncrypt service socket.
UncryptSocket socket = mInjector.connectService();
if (socket == null) {
Slog.e(TAG, "Failed to connect to uncrypt socket");
return false;
}
try {
// Send the BCB commands if it's to setup BCB.
if (isSetup) {
socket.sendCommand(command);
}
// Read the status from the socket.
int status = socket.getPercentageUncrypted();
// Ack receipt of the status code. uncrypt waits for the ack so
// the socket won't be destroyed before we receive the code.
socket.sendAck();
if (status == 100) {
Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear")
+ " bcb successfully finished.");
} else {
// Error in /system/bin/uncrypt.
Slog.e(TAG, "uncrypt failed with status: " + status);
return false;
}
} catch (IOException e) {
Slog.e(TAG, "IOException when communicating with uncrypt:", e);
return false;
} finally {
socket.close();
}
return true;
}
/frameworks/base/core/java/android/os/PowerManager.java
public final class PowerManager {
@UnsupportedAppUsage
final IPowerManager mService;
/**
* {@hide}
*/
public PowerManager(Context context, IPowerManager service, IThermalService thermalService,
Handler handler) {
mContext = context;
mService = service;
mThermalService = thermalService;
mHandler = handler;
}
public void reboot(@Nullable String reason) {
if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
throw new UnsupportedOperationException(
"Attempted userspace reboot on a device that doesn't support it");
}
try {
mService.reboot(false, reason, true);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/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, @Nullable 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,
@Nullable final String reason, boolean wait) {
if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
throw new UnsupportedOperationException(
"Attempted userspace reboot on a device that doesn't support it");
}
UserspaceRebootLogger.noteUserspaceRebootWasRequested();
}
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);
}
}
}
};
// ShutdownThread must run on a looper capable of displaying the UI.
Message msg = Message.obtain(UiThread.getHandler(), runnable);
msg.setAsynchronous(true);
UiThread.getHandler().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) {
}
}
}
}
}
/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
private static final ShutdownThread sInstance = new ShutdownThread();
public static void rebootSafeMode(final Context context, boolean confirm) {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
return;
}
mReboot = true;
mRebootSafeMode = true;
mRebootHasProgressBar = false;
mReason = null;
shutdownInner(context, confirm);
}
public static void reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
mRebootSafeMode = false;
mRebootHasProgressBar = false;
mReason = reason;
shutdownInner(context, confirm);
}
public static void shutdown(final Context context, String reason, boolean confirm) {
mReboot = false;
mRebootSafeMode = 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);
}
}
/frameworks/base/core/java/android/os/storage/StorageManager.java
private final IStorageManager mStorageManager;
@UnsupportedAppUsage
public StorageManager(Context context, Looper looper) throws ServiceNotFoundException {
mContext = context;
mResolver = context.getContentResolver();
mLooper = looper;
mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount"));
mAppOps = mContext.getSystemService(AppOpsManager.class);
}
/** {@hide} */
public void wipeAdoptableDisks() {
// We only wipe devices in "adoptable" locations, which are in a
// long-term stable slot/location on the device, where apps have a
// reasonable chance of storing sensitive data. (Apps need to go through
// SAF to write to transient volumes.)
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 {
// TODO: switch to explicit wipe command when we have it,
// for now rely on the fact that vfat format does a wipe
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());
}
}
}
/** {@hide} */
@UnsupportedAppUsage
public void partitionPublic(String diskId) {
try {
mStorageManager.partitionPublic(diskId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
版权声明:本文标题:Android源码笔记--恢复出厂设置 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1728284966a1152184.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论