Android 恢复出厂设置基本流程

(1)遥控器/按键板后门键触发,或者应用里面从系统设置里面恢复出厂选项也可触发; // 后面以系统设置的应用触发为例 (2)选择恢复出厂设置之后,就会发送广播“android.intent.action.MASTER_CLEAR” ;// framework/base/core/res/AndroidManifest.xml (3)MasterClearReceiver 捕获广播 ,并进行android 层的相关处理最后重启 ; (4)往 /cache/recovery/command 文件中写入命令字段; (5)重启系统;

recovery 进入方式

(1) 通过读取 /cache 分区中文件 /cache/recovery/command 内容进入
(2)通过按键操作进入 (G1 通过同时按 HOME 和 挂断键)
以上两种方式进入都需要 blob的支持

进入recovery 的条件

(1) blob 必须能从 recovery 分区中装载内核和文件系统
2 )flash 必须有 cache 分区 和 recovery 分区
3 )必须编译提供 recovery.img 烧录到 recovery 分区

Android 的处理流程

广播接收 framework/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>
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 )信息时看到 “Command: "/sbin/recovery" "--wipe_data" "--locale=zh_CN" ,其实这应该也就是bootCommand 执行的命令
/** * 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 " , 将上面封装的参数信息 写入改文件,待重启之后读取该文件时进入recovery模式,另外我们看到写完文件之后,调用PowerManager 来reboot,注意参数PowerManager.REBOOT_RECOVERY
// Having written the command file, go ahead and rebootPowerManager 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 callpublic 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重启。

recovery进入流程

进入recovery有几种途径: (1)进入recovery前先写misc分区,重启时发现变化就直接进入recovery模式; (2)写文件 /cache/recovery/command 文件,重启时进去recovery模式; // 此种模式暂未找到启动recovery的地方,只是在启动recovery后有看到读 // 取/cache/recovery/command 文件数据再做后续操作

MISC分区内容

Bootloader Control Block(BCB) 存放recovery bootloader message,结构如下:
struct bootloader_message {
char command[32];
char status[32];
char recovery[768];


// The 'recovery' field used to be 1024 bytes. It has only ever
// been used to store the recovery command line, so 768 bytes
// should be plenty. We carve off the last 256 bytes to store the
// stage string (for multistage packages) and possible future
// expansion.
char stage[32];
char reserved[224];
};
command可以有以下两个值
“boot-recovery”:表示recovery正在进行或者指示bootloader应该进入recovery mode
“update--hboot/radio”:标志bootloader更新fireware

recovery内容

“recovery\n
<recovery command>\n
<recovery command>\n”其中 recovery command为CACHE:/recovery/command命令


Recovery Case

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. 重启系统

OTA INSTALL (OTA升级)

1. 升级系统系在OTA包包/cache/some-filename.zip
2. 升级系统写入recovery命令 “--update_package=CACHE:some-filename.zip”
3. 重启系统,进入recovery模式
4. get_args()将“boot-recovery”和“--wipe_packkage=...”写入BCB
5. install_package 做升级
6. finish_recovery() 擦除BCB
7. **如果安装包失败**prompt_and_wait()等待用户操作,选择ALT+S或者ALT+W升级或回复出厂设置
8. main() 里面调用maybe_install_firmware_update()
1.如果包里含有hboot/radio的fireware则继续,否则返回
2.将"boot-recovery"和"--wipe_cache"写入BCB
3.将fireware image写入cache分区
4.将"update-readio/hboot"和“--wipe_date”写入BCB
5.重启系统
6.bootloader自身更新fireware
7.bootloader将"boot-recovery"写入BCB
8.erase_root擦除CACHE分区
9.清除BCB
9. main 调用reboot重启系统




Recovery代码位置:bootable/recovery/ ,主文件recovery.cpp


后续再分析 recovery流程。
Android Recovery 模式学习!









更多相关文章

  1. Android下Excel的操作
  2. Android欢迎页面2秒钟后自动跳转到主页面
  3. android 当系统存在多个Launcher时,如何设置开机自动进入默认的La
  4. android 命令(adb shell)进入指定模拟器或设备
  5. Android(安卓)的 Recovery 分析
  6. Android(安卓)网络操作(上传下载等)
  7. Android(安卓)读取本地txt文件和写入txt文件到本地
  8. android 操作文件
  9. android修改进入工程模式

随机推荐

  1. Android面试简录——组件
  2. Android系统架构概况
  3. repost:Android shell 下 busybox,clear,tc
  4. 收集的android开源项目,android学习必备
  5. Android的JNI用法
  6. android 2048游戏实现
  7. Android类装载机制
  8. 如果让我重新设计一款Android App
  9. Android Studio中使用com.android.suppor
  10. Android应用程序启动Binder线程源码分析