<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>MasterClearReceiver接收广播android.intent.action.MASTER_CLEAR,创建一个县城作一下处理
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); } } }; thr.start();RecoverySystem 来重启,启动擦除用户数据的操作
/** * 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); }我们可以注意到在启动bootCommand传递命令时,封装参数 --wipe_data , --locale , 这些命令我们可以在查看recovery log ( /cache/recovery/*.log )信息时看到
/** * 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)) { // MStar Android Patch Begin String cmd = arg; String label = null; String uuid = null; if (cmd.startsWith("--update_package")) { cmd = arg.substring(17, 23); if (cmd.equals("/cache")) { command.write("--uuid=mstar-cache"); command.write("\n"); command.write("--label=mstar-cache"); command.write("\n"); } else { cmd = arg.substring(17, 28); if (cmd.equals("/mnt/usb/sd")) { cmd = arg.substring(17, 30); uuid = "--uuid=" + getVolumeUUID(cmd).toString(); label = "--label=" + getVolumeLabel(cmd).toString(); command.write(uuid); command.write("\n"); command.write(label); command.write("\n"); } else { if (cmd.equals("/mnt/sdcard")) { uuid = "--uuid=" + getVolumeUUID(cmd).toString(); label = "--label=" + getVolumeLabel(cmd).toString(); command.write(uuid); command.write("\n"); command.write(label); command.write("\n"); } else { cmd = arg.substring(17, 32); if (cmd.equals("/mnt/usb/mmcblk")) { cmd = arg.substring(17, 35); uuid = "--uuid=" + getVolumeUUID(cmd).toString(); label = "--label=" + getVolumeLabel(cmd).toString(); command.write(uuid); command.write("\n"); command.write(label); command.write("\n"); } } } } } // MStar Android Patch End 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?)"); }从以上代码分析,bootCommand 主要工作就是重启进入recovery,此处可以看到COMMAND_FILE 就是文件 “ /cache/recovery/command " , 将上面封装的参数信息
// Having written the command file, go ahead and reboot PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); pm.reboot(PowerManager.REBOOT_RECOVERY); /** * 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) { } }最后又进入PowerManagerService 的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); } }接着进入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) { } } } } }ShutdownThread来负责重启动作
/** * 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; mRebootReason = reason; shutdownInner(context, confirm); } 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); } }beginShutdownSequence进入主要的关机流程, 接着启动ShutdownThread.run() , 发送光机广播,关闭核心服务,最后进入rebootOrShutdown重启。