Android------recovery模式启动分析

Recovery启动流程(1)--- 应用层到开机进入recovery详解


转载请注明来源:cuixiaolei的技术博客

 

 进入recovery有两种方式,一种是通过组合键进入recovery,另一种是上层应用设置中执行安装/重置/清除缓存等操作进行recovery。这篇文档主要讲解上层应用是如何进入到recovery的。本文以高通平台为例。

  

1.app执行安装/重置/清楚缓存操作调用代码文件frameworks/base/core/java/android/os/RecoverySystem.java

不同的操作使用不同的方法:

安装升级包  --------  installPackage

清除用户数据------  rebootWipeUserData

清楚缓存  -----------  rebootWipeCache

上面的所有操作都是往/cache/recovery/command文件中写入不同的命令,在进入recovery后(recovery.cpp)对command的关键字进行判断,执行相应的操作,下文会详细讲解,这里先简单提及。

 

复制代码
bootable/recovery/recovery.app
static
const struct option OPTIONS[] = { { "send_intent", required_argument, NULL, 'i' }, { "update_package", required_argument, NULL, 'u' }, #ifdef TARGET_USE_REDBEND_FOTA { "omadm_package", required_argument, NULL, 'o' }, #endif { "wipe_data", no_argument, NULL, 'w' }, { "wipe_cache", no_argument, NULL, 'c' }, { "show_text", no_argument, NULL, 't' }, { "sideload", no_argument, NULL, 's' }, { "sideload_auto_reboot", no_argument, NULL, 'a' }, { "just_exit", no_argument, NULL, 'x' }, { "locale", required_argument, NULL, 'l' }, { "stages", required_argument, NULL, 'g' }, { "shutdown_after", no_argument, NULL, 'p' }, { "reason", required_argument, NULL, 'r' }, { NULL, 0, NULL, 0 }, };

..................

int
main(int argc, char **argv) {
....
get_args(&argc, &argv); //提取cache/recovery/command中的信息
....
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {      //解析cache/recovery/command文件中的信息
        printf("***** xiaolei 2\n");
        switch (arg) {
        case 'i': send_intent = optarg; break;
        case 'u': update_package = optarg; break;


        case 'w': should_wipe_data = true; break;
        case 'c': should_wipe_cache = true; break;
        case 't': show_text = true; break;
        case 's': sideload = true; break;
        case 'a': sideload = true; sideload_auto_reboot = true; break;
        case 'x': just_exit = true; break;
        case 'l': locale = optarg; break;
        case 'g': {
            if (stage == NULL || *stage == '\0') {
                char buffer[20] = "1/";
                strncat(buffer, optarg, sizeof(buffer)-3);
                stage = strdup(buffer);
            }
            break;
        }
        case 'p': shutdown_after = true; break;
        case 'r': reason = optarg; break;
        case '?':
            LOGE("Invalid command argument\n");
            continue;
        }
    }
}
复制代码

 

 

本文以installPackage为例:

 

复制代码
frameworks/base/core/java/android/os/RecoverySystem.java
public
static void installPackage(Context context, File packageFile) throws IOException { String filename = packageFile.getCanonicalPath(); //获取升级包路径 String internalPath = Environment.maybeTranslateEmulatedPathToInternal(new File(filename)).getPath(); FileWriter uncryptFile = new FileWriter(UNCRYPT_FILE); try { uncryptFile.write(internalPath + "\n"); } finally { uncryptFile.close(); } Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); Log.w(TAG, "!!! REBOOTING TO INSTALL REALPATH " + internalPath + " !!!"); // If the package is on the /data partition, write the block map file // If the package is on internal storage sdcard,write the block map file // into COMMAND_FILE instead. if (filename.startsWith("/data/")                           //加密处理,block.map是解密的映射文件 ||filename.startsWith("/storage/emulated/0/")) { filename = "@/cache/recovery/block.map"; } final String filenameArg = "--update_package=" + filename;     //把“--update_package=path” 通过bootCommand方法写入到cache/recovery/command文件中 final String localeArg = "--locale=" + Locale.getDefault().toString();  //recovery显示语言 bootCommand(context, filenameArg, localeArg); }

复制代码

 

installPackage方法会嗲用bootCommand()方法,此时参数为(context,向cache/recovery/command文件中写入的信息,语言信息)
复制代码
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);     //向/cache/recovery/command中写入--update_package=path
        try {
            for (String arg : args) {
                if (!TextUtils.isEmpty(arg)) {
                    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);  //PowerManager.REBOOT_RECOVERY的值为字符串"recovery"

        throw new IOException("Reboot failed (no permissions?)");
    }
复制代码
pm.reboot(PowerManager.REBOOT_RECOVERY); 参数为字符串("recovery")
复制代码
frameworks/base/core/java/android/os/PowerMangager.java

public void reboot(String reason) {
        try {
            mService.reboot(false, reason, true);
        } catch (RemoteException e) {
        }
    }
复制代码
 
 
 mService.reboot(false, reason, true);    参数为(false,"recovery",true)
复制代码
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

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(false, confirm, reason, wait);  参数为(false,false,"recovery",true)
复制代码
 
  
frameworks/base/services/core/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) {                   //此处shutdown=false 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.reboot(mContext, reason, confirm); 参数为(mContex,"recovery",false)
此处开了一个线程处理reboot
复制代码
frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

public static void reboot(final Context context, String reason, boolean confirm) {        //方法中的变量为全局变量
mReboot
= true; mRebootSafeMode = false; mRebootUpdate = false; mRebootReason = reason; shutdownInner(context, confirm);       //此方法是在手机界面上弹出一个确认框,是否重启,此处的代码不再追了 }

//程序一定会执行run()方法

/***********************
mReboot = true;
mRebootSafeMode = false;
mRebootUpdate = false; mRebootReason = "recovery";
***********************/
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 : "");     //reason的值为"recovery"
            SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);  //设置系统属性sys.shutdown.requested = "recovery"
        }

        /*
         * 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...");
        setBootValue("silent_mode",mAudioManager.isSilentMode() ? "1" : "0");
        if(checkAnimationFileExist() && mContext.getResources().
                getBoolean(com.android.internal.R.bool.feature_tctfw_shutdown_animation_on) &&
                !mRebootUpdate) {
            lockDevice();
            showShutdownAnimation();
        }


        // 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;
                } else if (mRebootUpdate) {
                    int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
                            BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
                    sInstance.setRebootProgress(status, null);
                }
                try {
                    mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
                } catch (InterruptedException e) {
                }
            }
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
        }
        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) {
            }
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
        }

        Log.i(TAG, "Shutting down package manager...");

        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService("package");
        if (pm != null) {
            pm.shutdown();
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
        }

        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);
        if (mRebootUpdate) {
            sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
        }

        // Mod-by-yanxi.liu, for sdcard upgrade uncrypt, Defect-1357392
        if (mRebootUpdate) {
            sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
            // If it's to reboot to install update, invoke uncrypt via init service.
            uncrypt();
        }
        // Mod-by-yanxi.liu, for sdcard upgrade uncrypt, Defect-1357392

        // 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;
                } else if (mRebootUpdate) {
                    int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
                            (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
                            MAX_SHUTDOWN_WAIT_TIME);
                    status += RADIO_STOP_PERCENT;
                    sInstance.setRebootProgress(status, null);
                }
                try {
                    mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
                } catch (InterruptedException e) {
                }
            }
        }
        // add by feikuang for defect 1453123 start
        isVibrate = mContext.getResources().getBoolean(com.android.internal.R.bool.config_isVibrate);
        Log.i(TAG,"isVibrate " + isVibrate);
        // add by feikuang for defect 1453123 end
        waitShutDownAnimationCompleted();
        rebootOrShutdown(mContext, mReboot, mRebootReason);        //程序执行到这里
    }


复制代码
rebootOrShutdown(mContext, mReboot, mRebootReason); 参数为(mContext,true,"recovery")
复制代码
frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
        deviceRebootOrShutdown(reboot, reason);
        //[FEATURE]-Add-BEGIN by TSNJ.shu.wang,11/06/2015,TASK-871146
        String bootAlarms = SystemProperties.get("sys.boot.alarm");
        boolean isBootAlarms = bootAlarms != null && bootAlarms.equals("1");
        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 && context != null && !isBootAlarms && isVibrate) {
        //[FEATURE]-Add-END by TSNJ.shu.wang,11/06/2015,TASK-871146
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator(context);
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
            } 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(reason);  参数为"recovery"
复制代码
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
public static void lowLevelReboot(String reason) {
        if (reason == null) {
            reason = "";
        }
        if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
            // If we are rebooting to go into recovery, instead of
            // setting sys.powerctl directly we'll start the
            // pre-recovery service which will do some preparation for
            // recovery and then reboot for us.
            SystemProperties.set("ctl.start", "pre-recovery");    //到这里可以知道,一个新的进程会被enable
        } else {
            SystemProperties.set("sys.powerctl", "reboot," + reason);
        }
        try {
            Thread.sleep(20 * 1000L);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
    }
复制代码
SystemProperties.set("ctl.start", "pre-recovery");    //到这里可以知道,一个新的进程会被enable
system/core/rootdir/init.rc
service pre-recovery /system/bin/uncrypt --reboot   
    class main
    disabled
    oneshot
 
 
service pre-recovery /system/bin/uncrypt --reboot    system/bin/uncrypt 这个程序会被执行,传入的参数是--reboot
uncrypt的源码位于bootable/recovery/uncrypt/uncrypt.cpp,下面对uncrypt进行分析
复制代码
bootable/recovery/uncrypt/uncrypt.cpp
int
main(int argc, char** argv) {          //此处argc为2  argv[1]="--reboot" const char* input_path; const char* map_file; if (argc != 3 && argc != 1 && (argc == 2 && strcmp(argv[1], "--reboot") != 0)) { fprintf(stderr, "usage: %s [--reboot] [<transform_path> <map_file>]\n", argv[0]); return 2; } // When uncrypt is started with "--reboot", it wipes misc and reboots. // Otherwise it uncrypts the package and writes the block map. if (argc == 2) {   //程序执行到此处 if (read_fstab() == NULL) {   return 1; } wipe_misc();        //擦出misc分区内容 reboot_to_recovery();  //重启到recovery } else { // The pipe has been created by the system server. int status_fd = open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); if (status_fd == -1) { ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno)); return 1; } std::string package; if (argc == 3) { // when command-line args are given this binary is being used // for debugging. input_path = argv[1]; map_file = argv[2]; } else { if (!find_uncrypt_package(package)) { android::base::WriteStringToFd("-1\n", status_fd); close(status_fd); return 1; } input_path = package.c_str(); map_file = cache_block_map.c_str(); } int status = uncrypt(input_path, map_file, status_fd); if (status != 0) { android::base::WriteStringToFd("-1\n", status_fd); close(status_fd); return 1; } android::base::WriteStringToFd("100\n", status_fd); close(status_fd); } return 0; }
复制代码
1
<span style= "color: #000000;" >reboot_to_recovery();<br></span>
复制代码
bootable/recovery/uncrpty/uncrpty.cpp
static void reboot_to_recovery() {
    ALOGI("rebooting to recovery");
    property_set("sys.powerctl", "reboot,recovery");      
    sleep(10);
    ALOGE("reboot didn't succeed?");
}
复制代码
1
 
property_set("sys.powerctl", "reboot,recovery");     sys.powerctl属性出发开关在init.rc中
system/core/rootdir/init.rc
on property:sys.powerctl=*
    powerctl ${sys.powerctl}
system/core/init/keywords.h
KEYWORD(powerctl,    COMMAND, 1, do_powerctl
复制代码
system/core/init/builtins.cpp               
int do_powerctl(int nargs, char **args)           //传入的参数为字符串"reboot,recovery"
{
    char command[PROP_VALUE_MAX];
    int res;
    int len = 0;
    int cmd = 0;
    const char *reboot_target;

    res = expand_props(command, args[1], sizeof(command));
    if (res) {
        ERROR("powerctl: cannot expand '%s'\n", args[1]);
        return -EINVAL;
    }

    if (strncmp(command, "shutdown", 8) == 0) {
        cmd = ANDROID_RB_POWEROFF;
        len = 8;
    } else if (strncmp(command, "reboot", 6) == 0) {    //程序走到这,cmd=ANDROID_RB_RESTART2
        cmd = ANDROID_RB_RESTART2;
        len = 6;
    } else {
        ERROR("powerctl: unrecognized command '%s'\n", command);
        return -EINVAL;
    }

    if (command[len] == ',') {
        char prop_value[PROP_VALUE_MAX] = {0};
        reboot_target = &command[len + 1];    //设置reboot_target = recovery

        if ((property_get("init.svc.recovery", prop_value) == 0) &&
            (strncmp(reboot_target, "keys", 4) == 0)) {
            ERROR("powerctl: permission denied\n");
            return -EINVAL;
        }
    } else if (command[len] == '\0') {
        reboot_target = "";
    } else {
        ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
        return -EINVAL;
    }

    return android_reboot(cmd, 0, reboot_target);     
}
复制代码
android_reboot(cmd, 0, reboot_target);    参数为(ANDROID_RB_RESTART2, 0, "recovery")
复制代码
system/core/libcutils/android_reboot.c
int android_reboot(int cmd, int flags UNUSED, const char *arg)
{
    int ret;

    sync();
    remount_ro();

    switch (cmd) {
        case ANDROID_RB_RESTART:
            ret = reboot(RB_AUTOBOOT);
            break;

        case ANDROID_RB_POWEROFF:
            ret = reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2:             //程序跑到这里其中arg="recovery"
            ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;

        default:
            ret = -1;
    }

    return ret;
}
复制代码
ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
kernel/include/uapi/asm-generic/unistd.h
#define __NR_reboot 142
__SYSCALL(__NR_reboot, sys_reboot)

__NR_reboot被映射到sys_reboot上,会执行SYSCALL_DEFINE4(),为什么会这么执行还不清楚。

复制代码
kernel/kernel/sys.c
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
        void __user *, arg)                                                 //对应LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "recovery"
{
    struct pid_namespace *pid_ns = task_active_pid_ns(current);
    char buffer[256];
    int ret = 0;

    /* We only trust the superuser with rebooting the system. */
    if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
        return -EPERM;

    /* For safety, we require "magic" arguments. */
    if (magic1 != LINUX_REBOOT_MAGIC1 ||
        (magic2 != LINUX_REBOOT_MAGIC2 &&
                    magic2 != LINUX_REBOOT_MAGIC2A &&
            magic2 != LINUX_REBOOT_MAGIC2B &&
                    magic2 != LINUX_REBOOT_MAGIC2C))
        return -EINVAL;

    /*
     * If pid namespaces are enabled and the current task is in a child
     * pid_namespace, the command is handled by reboot_pid_ns() which will
     * call do_exit().
     */
    ret = reboot_pid_ns(pid_ns, cmd);
    if (ret)
        return ret;

    /* Instead of trying to make the power_off code look like
     * halt when pm_power_off is not set do it the easy way.
     */
    if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
        cmd = LINUX_REBOOT_CMD_HALT;

    mutex_lock(&reboot_mutex);
    switch (cmd) {
    case LINUX_REBOOT_CMD_RESTART:
        kernel_restart(NULL);
        break;

    case LINUX_REBOOT_CMD_CAD_ON:
        C_A_D = 1;
        break;

    case LINUX_REBOOT_CMD_CAD_OFF:
        C_A_D = 0;
        break;

    case LINUX_REBOOT_CMD_HALT:
        kernel_halt();
        do_exit(0);
        panic("cannot halt");

    case LINUX_REBOOT_CMD_POWER_OFF:
        kernel_power_off();
        do_exit(0);
        break;

    case LINUX_REBOOT_CMD_RESTART2:
        if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
            ret = -EFAULT;
            break;
        }
        buffer[sizeof(buffer) - 1] = '\0';

        kernel_restart(buffer);              //程序会跑到这里
        break;

#ifdef CONFIG_KEXEC
    case LINUX_REBOOT_CMD_KEXEC:
        ret = kernel_kexec();
        break;
#endif

#ifdef CONFIG_HIBERNATION
    case LINUX_REBOOT_CMD_SW_SUSPEND:
        ret = hibernate();
        break;
#endif

    default:
        ret = -EINVAL;
        break;
    }
    mutex_unlock(&reboot_mutex);
    return ret;
}
复制代码
kernel_restart(buffer); 参数为("recovery")
复制代码
kernel/kernel/sys.c
void kernel_restart(char *cmd)
{    
    kernel_restart_prepare(cmd);       
    migrate_to_reboot_cpu();
    syscore_shutdown();
    if (!cmd)
        printk(KERN_EMERG "Restarting system.\n");
    else
        printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
    kmsg_dump(KMSG_DUMP_RESTART);
    machine_restart(cmd);
}
复制代码

machine_restart(cmd); 参数为("recovery")

复制代码
kernel/arch/arm/kernel/process.c
void machine_restart(char *cmd)
{
    preempt_disable();
    smp_send_stop();

    /* Flush the console to make sure all the relevant messages make it
     * out to the console drivers */
    arm_machine_flush_console();

    arm_pm_restart(reboot_mode, cmd);

    /* Give a grace period for failure to restart of 1s */
    mdelay(1000);

    /* Whoops - the platform was unable to reboot. Tell the user! */
    printk("Reboot failed -- System halted\n");
    local_irq_disable();
    while (1);
}
复制代码
arm_pm_restart(reboot_mode, cmd);

复制代码
kernel/drivers/power/reset/msm-poweroff.c
static int msm_restart_probe(struct platform_device *pdev)
{
 .....
arm_pm_restart = do_msm_restart;
.....       
}
复制代码
复制代码
 
  
kernel/drivers/power/reset/msm-poweroff.c
static
void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd) { int ret; struct scm_desc desc = { .args[0] = 1, .args[1] = 0, .arginfo = SCM_ARGS(2), }; pr_notice("Going down for restart now\n"); msm_restart_prepare(cmd);   //程序走到这里 #ifdef CONFIG_MSM_DLOAD_MODE /* * Trigger a watchdog bite here and if this fails, * device will take the usual restart path. */ if (WDOG_BITE_ON_PANIC && in_panic) msm_trigger_wdog_bite(); #endif /* Needed to bypass debug image on some chips */ if (!is_scm_armv8()) ret = scm_call_atomic2(SCM_SVC_BOOT, SCM_WDOG_DEBUG_BOOT_PART, 1, 0); else ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT, SCM_WDOG_DEBUG_BOOT_PART), &desc); if (ret) pr_err("Failed to disable secure wdog debug: %d\n", ret); halt_spmi_pmic_arbiter(); deassert_ps_hold(); mdelay(10000); }
复制代码
msm_restart_prepare(cmd);  参数为("recovery")
复制代码
kernel/drivers/power/reset/msm-poweroff.c
static void msm_restart_prepare(const char *cmd)
{
#ifdef CONFIG_MSM_SUBSYSTEM_RESTART
    extern char panic_subsystem[];
#endif /* CONFIG_MSM_SUBSYSTEM_RESTART */
    bool need_warm_reset = false;

#ifdef CONFIG_MSM_DLOAD_MODE

    /* Write download mode flags if we're panic'ing
     * Write download mode flags if restart_mode says so
     * Kill download mode if master-kill switch is set
     */

    set_dload_mode(download_mode &&
            (in_panic || restart_mode == RESTART_DLOAD));
#endif

    if (qpnp_pon_check_hard_reset_stored()) {
        /* Set warm reset as true when device is in dload mode
         *  or device doesn't boot up into recovery, bootloader or rtc.
         */
        if (get_dload_mode() ||
            ((cmd != NULL && cmd[0] != '\0') &&
            strcmp(cmd, "recovery") &&
            strcmp(cmd, "bootloader") &&
            strcmp(cmd, "rtc")))
            need_warm_reset = true;
    } else {
        need_warm_reset = (get_dload_mode() ||
                (cmd != NULL && cmd[0] != '\0'));
    }

    /* Hard reset the PMIC unless memory contents must be maintained. */
    if (need_warm_reset || restart_mode == RESTART_DLOAD) {// MODIFIED by xin.peng, 2016-03-21, BUG-1761259
        qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);
    } else {
        qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET);
    }

    if (cmd != NULL) {
        if (!strncmp(cmd, "bootloader", 10)) {
            qpnp_pon_set_restart_reason(
                PON_RESTART_REASON_BOOTLOADER);
            __raw_writel(0x77665500, restart_reason);
        } else if (!strncmp(cmd, "recovery", 8)) {
            qpnp_pon_set_restart_reason(
                PON_RESTART_REASON_RECOVERY);
            __raw_writel(0x77665502, restart_reason);          //********************

  else if (!strcmp(cmd, "rtc")) { qpnp_pon_set_restart_reason( PON_RESTART_REASON_RTC); __raw_writel(0x77665503, restart_reason); } else if (!strncmp(cmd, "oem-", 4)) { unsigned long code; int ret; ret = kstrtoul(cmd + 4, 16, &code); if (!ret) __raw_writel(0x6f656d00 | (code & 0xff), restart_reason); if (download_mode && (code & 0xff) == 0x3a) set_dload_mode(1); } else if (!strncmp(cmd, "edl", 3)) { enable_emergency_dload_mode(); } else { __raw_writel(0x77665501, restart_reason); } } #ifdef CONFIG_MSM_SUBSYSTEM_RESTART if (in_panic) { printk(KERN_ERR "subsystem %s crash\n", panic_subsystem); if (!memcmp(panic_subsystem, "modem", 5)) { __raw_writel(0x6f656dc1, restart_reason); } else if (!memcmp(panic_subsystem, "wcnss", 5)) { __raw_writel(0x6f656dc2, restart_reason); } else if (!memcmp(panic_subsystem, "adsp", 4) || !memcmp(panic_subsystem, "ADSP", 4)) { __raw_writel(0x6f656dc3, restart_reason); } else if (!memcmp(panic_subsystem, "venus", 5)) { __raw_writel(0x6f656dc4, restart_reason); } else { __raw_writel(0x6f656dc0, restart_reason); } if (download_mode) set_dload_mode(1); } #endif /* CONFIG_MSM_SUBSYSTEM_RESTART */ //add begin by liping.gao,Powering on mode switch to 9008 mode,task:665748 if (restart_mode == RESTART_DLOAD) { enable_emergency_dload_mode(); } //add end by liping.gao flush_cache_all(); /*outer_flush_all is not supported by 64bit kernel*/ #ifndef CONFIG_ARM64 outer_flush_all(); #endif }
复制代码
__raw_writel(0x77665502, restart_reason);          //在地址上0x77665502写入"recovery"


应用执行升级/清缓存/重置等操作导致底层在地址0x77665502写入"recovery"

当开机以后,在lk阶段会判断这个地址的值,如果是recovery,会设置boot_into_recovery=1,然后读取recovery.img镜像,把recovery.img的地址和ramdisk等信息作为参数启动kernel,从而进入recovery模式,下面进行简单的分析。

lk阶段:
复制代码
bootable/bootloader/lk/app/aboot/aboot.c
......
#define RECOVERY_MODE        0x77665502
......
reboot_mode = check_reboot_mode();
hard_reboot_mode = check_hard_reboot_mode();
if (reboot_mode == RECOVERY_MODE ||
        hard_reboot_mode == RECOVERY_HARD_RESET_MODE) {
        boot_into_recovery = 1;
    }
.....
复制代码

 

 



 



Recovery启动流程(2)---UI界面

转载请注明来源:cuixiaolei的技术博客

 

Recovery启动流程系列文章把recvoery目录下文件分成小块讲解,最后再以一条主线贯穿所有的内容。这篇文章主要讲解Recovery-UI的相关内容。

 

我们知道,当我们通过按键或者应用进入recovery模式,实质是kernel后加载recovery.img,kernel起来后执行的第一个进程就是init,此进程会读入init.rc启动相应的服务。在recovery模式中,启动的服务是执行recovery可执行文件,此文件是bootable/recovery/recovery.cpp文件生成,我们就从recovery.cpp文件开始分析。此出可参考我的另一篇文章android-ramdisk.img分析、recovery.img&boot.img执行过程

复制代码
bootable/recovery/recovery.cpp
int main(int argc, char **argv) { .... Device* device = make_device(); ui = device->GetUI(); gCurrentUI = ui; ui->SetLocale(locale); ui->Init(); ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); .... if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { prompt_and_wait(device, status); } .... }
复制代码

1.首先新建了一个Device类的对象, Device类封装了一些操作,包括UI的操作

2.调用Device类的GetUI()返回一个RecoveryUI对象

3.调用ui->SetLocale(locale)设置语言,调用SetBackground方法设置背景图片

4.调用Init()进行初始化。

5.这里的Init从代码上看应该是ui.cpp文件中RecoveryUI类的Init()方法,但是经验上走的应该是ScreenRecoveryUI,其中的愿意我还在看,这里我是按照ScreenRecoveryUI::Init追的代码。其中RecoveryUI是ScreenRecoveryUI的父类。

6.显示recovery的主界面,即一个选择菜单

 

初始化

 

复制代码
void ScreenRecoveryUI::Init() {
    gr_init();             //初始化图形设备,分配Pixelflinger库渲染的内存

    gr_font_size(&char_width, &char_height);
    text_rows_ = gr_fb_height() / char_height;
    text_cols_ = gr_fb_width() / char_width;

#ifdef SUPPORT_UTF8_MULTILINGUAL
    int ml_cols_ = 6 * text_cols_; //max is 6 char for 1 utf8 character.
    text_ = Alloc2d(text_rows_, ml_cols_ + 1);
    file_viewer_text_ = Alloc2d(text_rows_, ml_cols_ + 1);
    menu_ = Alloc2d(text_rows_, ml_cols_ + 1);
    menu_headers_wrap = Alloc2d(text_rows_, ml_cols_ + 1);
#else
    text_ = Alloc2d(text_rows_, text_cols_ + 1);
    file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
    menu_ = Alloc2d(text_rows_, text_cols_ + 1);
#endif

    text_col_ = text_row_ = 0;
    text_top_ = 1;

    backgroundIcon[NONE] = nullptr;
    LoadBitmapArray("icon_installing", &installing_frames, &installation);
    backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : nullptr;
    backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
    LoadBitmap("icon_error", &backgroundIcon[ERROR]);                //LoadBitmap()  将png生成surface, 每个png图片对应一个surface, 所有surface存放在一个数组中
    backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];

    LoadBitmap("icon_recovery", &backgroundIcon[RECOVERY]);
    LoadBitmap("progress_empty", &progressBarEmpty);
    LoadBitmap("progress_fill", &progressBarFill);
    LoadBitmap("stage_empty", &stageMarkerEmpty);
    LoadBitmap("stage_fill", &stageMarkerFill);

/* add for AT&T recovery update install UI begin */
#ifdef TARGET_ATT_RECOVERY_UI
    LoadBitmap("icon_attinstalling", &backgroundIcon[ATT_INSTALLING_UPDATE]);
    LoadBitmap("progress_attempty", &progressBarEmpty_ATT);
    LoadBitmap("progress_attfill", &progressBarFill_ATT);
    LoadLocalizedBitmap("installing_atttext", &backgroundText[ATT_INSTALLING_UPDATE]);   //LoadLocalizedBitmap()  将区域文字所在的图片中的text信息根据当前的locale提取出来,生成对应的surface, 所有
 surface也存放在一个数组中
#endif
/* add for AT&T recovery update install UI end */

    LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]);
    LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]);
    LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
    LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);

    pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);   //创建一个线程,在该循环中不停地检测currentIcon以及progressBarType来决定是不是要更新进度条。
    RecoveryUI::Init();  //初始化RecoveryUI类
}
复制代码

 

复制代码
bootable/recovery/minui/ui.cpp

void RecoveryUI::Init() {
    ev_init(InputCallback, this);

    ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));

    pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
}
复制代码
通过RecoveryUI::Init(); 调用events.cpp文件,界面和按键/触摸联系在一起了,后面会用单独的文章介绍recovery按键和触屏的相关内容。

下面介绍几个常用的函数
复制代码
void ScreenRecoveryUI::SetLocale(const char* new_locale) {
    if (new_locale) {
        this->locale = new_locale;
        char* lang = strdup(locale);
        for (char* p = lang; *p; ++p) {
            if (*p == '_') {
                *p = '\0';
                break;
            }
        }

        // A bit cheesy: keep an explicit list of supported languages
        // that are RTL.
        if (strcmp(lang, "ar") == 0 ||   // Arabic
            strcmp(lang, "fa") == 0 ||   // Persian (Farsi)
            strcmp(lang, "he") == 0 ||   // Hebrew (new language code)
            strcmp(lang, "iw") == 0 ||   // Hebrew (old language code)
            strcmp(lang, "ur") == 0) {   // Urdu
            rtl_locale = true;
        }
        free(lang);
    } else {
        new_locale = nullptr;
    }
}
复制代码
 
   

从recovery.cpp main()中可知,进入recovery后会分析/cache/recovery/command文件,根据内容来设定显示的文字语言

SetLocale函数根据locale判断所用的字体是否属于阿拉伯语系,阿拉伯语的书写习惯是从右到左,如果是阿拉伯语系的话,就设置一个标志,后面根据这个标志决定从右到左显示文字或进度条。关于显示文字的语言通过代码即可查看,这里只简单的列出语言设置的几条主线,不贴出具体的代码(太多了)。

 

g_ml_str[] (mi_string.h)-> ml_string_fetch() (multilingual.c)

ml_set_language (multilingual.c) -> ml_select() (recovery.cpp) -> prompt_and_wait() (recovery.cpp) -> main() (recovery.cpp)

 

 

SetBackground函数比较简洁,关键部分在update_screen_locked。
update_screen_locked 和update_progress_locked是recovery的UI部分的关键函数,update_screen_locked用来更新背 景, update_progress_locked用来更新进度条,因为显示的画面会一直在更新,所以这两个函数会在不同的地方被反复调用

 

复制代码
void ScreenRecoveryUI::SetBackground(Icon icon) {
    pthread_mutex_lock(&updateMutex);

    currentIcon = icon;
    update_screen_locked();

    pthread_mutex_unlock(&updateMutex);
}
复制代码
void ScreenRecoveryUI::update_screen_locked() {
    draw_screen_locked();
    gr_flip();
}
复制代码
void ScreenRecoveryUI::draw_screen_locked() {
    if (!show_text) {
        draw_background_locked(currentIcon);               //************   有一个bug因为此行没有,导致SetBackground函数无法更换背景图片
        draw_progress_locked();
    } else {
        gr_color(0, 0, 0, 255);
        gr_clear();
        draw_background_locked(currentIcon);            //************
    .........
} }
复制代码

 

此文章持续更新.......

 


Recovery启动流程(3)--recovery.cpp分析


转载请注明来源:cuixiaolei的技术博客

 

这篇文章主要通过分析高通recovery目录下的recovery.cpp源码,对recovery启动流程有一个宏观的了解。MTK和高通的recovery几乎一样,只是使用自己家的mt_xxx文件。

 

为什么要分析recovery.cpp这个文件?

我们知道,当我们通过按键或者应用进入recovery模式,实质是kernel后加载recovery.img,kernel起来后执行的第一个进程就 是init,此进程会读入init.rc启动相应的服务。在recovery模式中,启动的服务是执行recovery可执行文件,此文件是

bootable/recovery/recovery.cpp文件生成,我们就从recovery.cpp文件开始分析。此出可参考我的另一篇文章android-ramdisk.img分析、recovery.img&boot.img执行过程

下面的代码位于bootable/recovery/etc/init.rc,由此可知,进入recovery模式后会执行sbin /recovery,此文件是bootable/recovery/recovery.cpp生成(可查看对应目录的Android.mk查看),所以recovery.cpp是recovery模式的入口。

service recovery /sbin/recovery
    seclabel u:r:recovery:s0

 

开始主题

复制代码
bootable/recovery/recovery.cpp

int main(int argc, char **argv) { time_t start = time(NULL); redirect_stdio(TEMPORARY_LOG_FILE); // If this binary is started with the single argument "--adbd",        如果二进制文件使用单个参数"--adbd"启动 // instead of being the normal recovery binary, it turns into kind     而不是正常的recovery启动(不带参数即为正常启动) // of a stripped-down version of adbd that only supports the           它变成精简版命令时只支持sideload命令。它必须是一个正确可用的参数 // 'sideload' command. Note this must be a real argument, not         不在/cache/recovery/command中,也不受B2B控制 // anything in the command file or bootloader control block; the       // only way recovery should be run with this argument is when it       是apply_from_adb()的副本 // starts a copy of itself from the apply_from_adb() function. if (argc == 2 && strcmp(argv[1], "--adbd") == 0) { adb_main(0, DEFAULT_ADB_PORT); return 0; } printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); load_volume_table();                //加载并建立分区表 get_args(&argc, &argv);             //从传入的参数或/cache/recovery/command文件中得到相应的命令 const char *send_intent = NULL; const char *update_package = NULL; bool should_wipe_data = false; bool should_wipe_cache = false; bool show_text = false; bool sideload = false; bool sideload_auto_reboot = false; bool just_exit = false; bool shutdown_after = false; int arg; while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {         //while循环解析command或者传入的参数,并把对应的功能设置为true或给相应的变量赋值
switch (arg) { case 'i': send_intent = optarg; break; case 'u': update_package = optarg; break; case 'w': should_wipe_data = true; break; case 'c': should_wipe_cache = true; break; case 't': show_text = true; break; case 's': sideload = true; break; case 'a': sideload = true; sideload_auto_reboot = true; break; case 'x': just_exit = true; break; case 'l': locale = optarg; break; case 'g': { if (stage == NULL || *stage == '\0') { char buffer[20] = "1/"; strncat(buffer, optarg, sizeof(buffer)-3); stage = strdup(buffer); } break; } case 'p': shutdown_after = true; break; case 'r': reason = optarg; break; case '?': LOGE("Invalid command argument\n"); continue; } } if (locale == NULL) {          //设置语言 load_locale_from_cache(); } printf("locale is [%s]\n", locale); printf("stage is [%s]\n", stage); printf("reason is [%s]\n", reason);
  /*初始化UI*/ Device
* device = make_device(); ui = device->GetUI(); gCurrentUI = ui; show_text = true; ui->SetLocale(locale); ui->Init();

int st_cur, st_max; if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) { ui->SetStage(st_cur, st_max); } ui->SetBackground(RecoveryUI::NONE);           //设置recovery界面背景 if (show_text) ui->ShowText(true);             //设置界面上是否能够显示字符,使能ui->print函数开关 struct selinux_opt seopts[] = {                //设置selinux权限,以后会有专门的文章或专题讲解selinux,这里不做讲解     { SELABEL_OPT_PATH, "/file_contexts" } }; sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); if (!sehandle) { ui->Print("Warning: No file_contexts\n"); } device->StartRecovery();       //此函数为空,没做任何事情 printf("Command:");                      //打印/cache/recovery/command的参数 for (arg = 0; arg < argc; arg++) { printf(" \"%s\"", argv[arg]); } printf("\n"); if (update_package) {                          //根据下面的注释可知,对old "root" 路径进行修改,把其放在/cache/文件中 。  当安装包的路径是以CACHE:开头,把其改为/cache/开头                                // For backwards compatibility on the cache partition only, if // we're given an old 'root' path "CACHE:foo", change it to // "/cache/foo". if (strncmp(update_package, "CACHE:", 6) == 0) { int len = strlen(update_package) + 10; char* modified_path = (char*)malloc(len); strlcpy(modified_path, "/cache/", len); strlcat(modified_path, update_package+6, len); printf("(replacing path \"%s\" with \"%s\")\n", update_package, modified_path); update_package = modified_path; } } printf("\n"); property_list(print_property, NULL);              //打印属性列表,其实现没有找到代码在哪里,找到后会更新此文章 printf("\n"); ui->Print("Supported API: %d\n", RECOVERY_API_VERSION); int status = INSTALL_SUCCESS;    //设置标志位,默认为INSTALL_SUCCESS if (update_package != NULL) {     //install package情况 status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, true);     //安装ota升级包 if (status == INSTALL_SUCCESS && should_wipe_cache) {   //如果安装前点击了清楚缓存,执行下面的语句,安装成功后清楚缓存 wipe_cache(false, device);    } if (status != INSTALL_SUCCESS) {                  //安装失败,打印log,并根据is_ro_debuggable()决定是否打开ui->print信息(此信息显示在屏幕上) ui->Print("Installation aborted.\n"); if (is_ro_debuggable()) { ui->ShowText(true); } } } else if (should_wipe_data) {     //只清除用户数据 if (!wipe_data(false, device)) { status = INSTALL_ERROR; } } else if (should_wipe_cache) {    //只清除缓存 if (!wipe_cache(false, device)) { status = INSTALL_ERROR; } } else if (sideload) {       //执行adb reboot sideload命令后会跑到这个代码段 // 'adb reboot sideload' acts the same as user presses key combinations // to enter the sideload mode. When 'sideload-auto-reboot' is used, text // display will NOT be turned on by default. And it will reboot after // sideload finishes even if there are errors. Unless one turns on the // text display during the installation. This is to enable automated // testing. if (!sideload_auto_reboot) { ui->ShowText(true); } status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE); if (status == INSTALL_SUCCESS && should_wipe_cache) { if (!wipe_cache(false, device)) { status = INSTALL_ERROR; } } ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { ui->Print("Rebooting automatically.\n"); } } else if (!just_exit) {              //当command命令中有just_exit字段 status = INSTALL_NONE; // No command specified ui->SetBackground(RecoveryUI::NONE); if (is_ro_debuggable()) { ui->ShowText(true); } } if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) {   //安装失败,复制log信息到/cache/recovery/。如果进行了wipe_data/wipe_cache/apply_from_sdcard(也就是修改了flash),
//直接return结束recovery,否则现实error背景图片 copy_logs(); ui
->SetBackground(RecoveryUI::ERROR); } Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;  if ((status != INSTALL_SUCCESS && !sideload_auto_reboot) || ui->IsTextVisible()) {       //status在just_exit中已经变为none,会执行此if语句 #ifdef SUPPORT_UTF8_MULTILINGUAL ml_select(device); #endif Device::BuiltinAction temp = prompt_and_wait(device, status);       //prompt_and_wait()函数是个死循环 开始显示recovery选项 并处理用户通过按键或者触摸屏的选项,如Reboot system等 if (temp != Device::NO_ACTION) { after = temp; } } finish_recovery(send_intent); switch (after) { case Device::SHUTDOWN: ui->Print("Shutting down...\n"); property_set(ANDROID_RB_PROPERTY, "shutdown,"); break; case Device::REBOOT_BOOTLOADER: ui->Print("Rebooting to bootloader...\n"); property_set(ANDROID_RB_PROPERTY, "reboot,bootloader"); break; default: char reason[PROPERTY_VALUE_MAX]; snprintf(reason, PROPERTY_VALUE_MAX, "reboot,%s", device->GetRebootReason()); ui->Print("Rebooting...\n"); property_set(ANDROID_RB_PROPERTY, reason); break; } sleep(5); return EXIT_SUCCESS; }
复制代码

 

上面的代码中已经把recovery启动后的流程描述的差不多了,下面是一点细节性的描述

 

1.获取command命令

get_args(&argc, &argv);

此函数没有什么可说的,先判断事都有参数传进来,如果有解析传入的命令,否走从/cache/recovery/command文件中解析命令

注意,此函数会先把struct bootloader_message boot写入到misc分区,目的是防止断电等原因导致关机,开机后lk会从misc分区中读取相关信息,如果发现是"boot-recovery"会再次进入recovery模式,misc分区会在退出recovery时被清除,以至于可以正常开机,如果手机每次都是进入recovery而不能正常开机,可以分析是否没有清楚misc分区。

复制代码
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 line args come from, in decreasing precedence:
//   - the actual command line
//   - the bootloader control block (one per line, after "recovery")
//   - the contents of COMMAND_FILE (one per line)
static void
get_args(int *argc, char ***argv) {
    struct bootloader_message boot;
    memset(&boot, 0, sizeof(boot));
    get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure
    stage = strndup(boot.stage, sizeof(boot.stage));

    if (boot.command[0] != 0 && boot.command[0] != 255) {
        LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
    }

    if (boot.status[0] != 0 && boot.status[0] != 255) {
        LOGI("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
    }

    // --- if arguments weren't supplied, look in the bootloader control block
    if (*argc <= 1) {
        boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
        const char *arg = strtok(boot.recovery, "\n");
        if (arg != NULL && !strcmp(arg, "recovery")) {
            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
            (*argv)[0] = strdup(arg);
            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
                if ((arg = strtok(NULL, "\n")) == NULL) break;
                (*argv)[*argc] = strdup(arg);
            }
            LOGI("Got arguments from boot message\n");
        } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
            LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
        }
    }

    // --- if that doesn't work, try the command file
    if (*argc <= 1) {
        FILE *fp = fopen_path(COMMAND_FILE, "r");
        if (fp != NULL) {
            char *token;
            char *argv0 = (*argv)[0];
            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
            (*argv)[0] = argv0;  // use the same program name

            char buf[MAX_ARG_LENGTH];
            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
                if (!fgets(buf, sizeof(buf), fp)) break;
                token = strtok(buf, "\r\n");
                if (token != NULL) {
                    (*argv)[*argc] = strdup(token);  // Strip newline.
                } else {
                    --*argc;
                }
            }

            check_and_fclose(fp, COMMAND_FILE);
            LOGI("Got arguments from %s\n", COMMAND_FILE);
        }
    }

    // --> write the arguments we have back into the bootloader control block
    // always boot into recovery after this (until finish_recovery() is called)
    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));  //***************************************************
    strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
    int i;
    for (i = 1; i < *argc; ++i) {
        strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
        strlcat(boot.recovery, "\n", sizeof(boot.recovery));
    }
    set_bootloader_message(&boot);
}
复制代码

 

 

2.解析command命令

while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {...}

可知从/cache/recovery/command文件中获取并与OPTIONS列表参数进行比较,把相应的字符串赋值或者修改相应的变量

复制代码
 //while循环解析command或者传入的参数,并把对应的功能设置为true或给相应的变量赋值,下面是command中可能的命令及其value /*
static const struct option OPTIONS[] = {
  { "send_intent", required_argument, NULL, 'i' },
  { "update_package", required_argument, NULL, 'u' },
  { "wipe_data", no_argument, NULL, 'w' },
  { "wipe_cache", no_argument, NULL, 'c' },
  { "show_text", no_argument, NULL, 't' },
  { "sideload", no_argument, NULL, 's' },
  { "sideload_auto_reboot", no_argument, NULL, 'a' },
  { "just_exit", no_argument, NULL, 'x' },
  { "locale", required_argument, NULL, 'l' },
  { "stages", required_argument, NULL, 'g' },
  { "shutdown_after", no_argument, NULL, 'p' },
  { "reason", required_argument, NULL, 'r' },
  { NULL, 0, NULL, 0 },
};
*/
复制代码

 

 

3.安装升级包

status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, true);

此函数安装升级包,update_package是路径,从/cache/recovery/command文件中解析

static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";  TEMPORARY_INSTALL_FILE存放升级时的log信息,后面会把此文件复制到/cache/recovery/文件中

复制代码
bootable/recovery/install.cpp

int
install_package(const char* path, bool* wipe_cache, const char* install_file, bool needs_mount) { modified_flash = true;  FILE* install_log = fopen_path(install_file, "w");        //打开log文件 if (install_log) { fputs(path, install_log);                             //向log文件中写入安装包路径 fputc('\n', install_log); } else { LOGE("failed to open last_install: %s\n", strerror(errno)); } int result; if (setup_install_mounts() != 0) {                       //mount /tmp和/cache ,成功返回0 LOGE("failed to set up expected mounts for install; aborting\n"); result = INSTALL_ERROR; } else { result = really_install_package(path, wipe_cache, needs_mount);       //执行安装 } if (install_log) {             //向log文件写入安装结果,成功写入1,失败写入0 fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log); fputc('\n', install_log); fclose(install_log); } return result; }
复制代码
复制代码
int setup_install_mounts() {                         //挂在/cache   /tmp分区
    if (fstab == NULL) {
        LOGE("can't set up install mounts: no fstab loaded\n");
        return -1;
    }
    for (int i = 0; i < fstab->num_entries; ++i) {
        Volume* v = fstab->recs + i;

        if (strcmp(v->mount_point, "/tmp") == 0 ||
            strcmp(v->mount_point, "/cache") == 0) {
            if (ensure_path_mounted(v->mount_point) != 0) {
                LOGE("failed to mount %s\n", v->mount_point);
                return -1;
            }

        } else {
            if (ensure_path_unmounted(v->mount_point) != 0) {
                LOGE("failed to unmount %s\n", v->mount_point);
                return -1;
            }
        }
    }
    return 0;
}
复制代码
复制代码
static int
really_install_package(const char *path, bool* wipe_cache, bool needs_mount)
{
    ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);                   //设置背景为安装背景,就是小机器人
    ui->Print("Finding update package...\n");             
    // Give verification half the progress bar...
    ui->SetProgressType(RecoveryUI::DETERMINATE);                            //初始化升级时进度条       
    ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);        //设置进度条时间
    LOGI("Update location: %s\n", path);

    // Map the update package into memory.
    ui->Print("Opening update package...\n");

    if (path && needs_mount) {                            //判断升级包所在路径是否被挂在
        ensure_path_mounted((path[0] == '@') ? path + 1 : path);
    }

    MemMapping map;                                 //把升级包路径映射到内存中
    if (sysMapFile(path, &map) != 0) {
        LOGE("failed to map file\n");
        return INSTALL_CORRUPT;
    }

    int numKeys;                                   //加载密钥
    Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
    if (loadedKeys == NULL) {
        LOGE("Failed to load keys\n");
        return INSTALL_CORRUPT;
    }
    LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);

    ui->Print("Verifying update package...\n");

    int err;                                  //校验升级包是否被修改,一般在调试ota升级时会把这段代码进行屏蔽,使本地编译的升级包可以正常升级
    err = verify_file(map.addr, map.length, loadedKeys, numKeys);
    free(loadedKeys);
    LOGI("verify_file returned %d\n", err);
    if (err != VERIFY_SUCCESS) {
        LOGE("signature verification failed\n");
        sysReleaseMap(&map);
        return INSTALL_CORRUPT;
    }

    /* Try to open the package.
     */
    ZipArchive zip;                 //打开升级包
    err = mzOpenZipArchive(map.addr, map.length, &zip);
    if (err != 0) {
        LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
        sysReleaseMap(&map);          //这行代码很重要,只有失败时才释放map内存,结束安装。提前释放map内存会导致下面代码无法正常进行,界面上会显示失败。
        return INSTALL_CORRUPT;
    }

    /* Verify and install the contents of the package.
     */
    ui->Print("Installing update...\n");
    ui->SetEnableReboot(false);
    int result = try_update_binary(path, &zip, wipe_cache);        //执行安装包内的执行脚本
    ui->SetEnableReboot(true);
    ui->Print("\n");

    sysReleaseMap(&map);

#ifdef USE_MDTP
    /* If MDTP update failed, return an error such that recovery will not finish. */
    if (result == INSTALL_SUCCESS) {
        if (!mdtp_update()) {
            ui->Print("Unable to verify integrity of /system for MDTP, update aborted.\n");
            return INSTALL_ERROR;
        }
        ui->Print("Successfully verified integrity of /system for MDTP.\n");
    }
#endif /* USE_MDTP */

    return result;
}
复制代码

 install_package流程:

1).设置ui界面,包括背景和进度条

2).检查是否挂在tmp和cache,tmp存放升级log,cache存放升级包

3).加载密钥并校验升级包,防止升级包被用户自己修改

4).打开升级包,并执行升级包内的安装程序

 

 

4.执行升级包中的升级文件

try_update_binary()

复制代码
try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) {
    const ZipEntry* binary_entry =                                     //在升级包中查找是否存在META-INF/com/google/android/update-binary文件
            mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
    if (binary_entry == NULL) {
        mzCloseZipArchive(zip);
        return INSTALL_CORRUPT;
    }

    const char* binary = "/tmp/update_binary";      //在tmp中创建临时文件夹,权限755
    unlink(binary);
    int fd = creat(binary, 0755);
    if (fd < 0) {
        mzCloseZipArchive(zip);
        LOGE("Can't make %s\n", binary);
        return INSTALL_ERROR;
    }
    bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);     //把update.zip升级包解压到/tmp/update_binary文件夹中
    sync();
    close(fd);
    mzCloseZipArchive(zip);

    if (!ok) {
        LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
        return INSTALL_ERROR;
    }

    int pipefd[2];
    pipe(pipefd);

    // When executing the update binary contained in the package, the
    // arguments passed are:
    //
    //   - the version number for this interface
    //
    //   - an fd to which the program can write in order to update the
    //     progress bar.  The program can write single-line commands:
    //
    //        progress <frac> <secs>
    //            fill up the next <frac> part of of the progress bar
    //            over <secs> seconds.  If <secs> is zero, use
    //            set_progress commands to manually control the
    //            progress of this segment of the bar.
    //
    //        set_progress <frac>
    //            <frac> should be between 0.0 and 1.0; sets the
    //            progress bar within the segment defined by the most
    //            recent progress command.
    //
    //        firmware <"hboot"|"radio"> <filename>
    //            arrange to install the contents of <filename> in the
    //            given partition on reboot.
    //
    //            (API v2: <filename> may start with "PACKAGE:" to
    //            indicate taking a file from the OTA package.)
    //
    //            (API v3: this command no longer exists.)
    //
    //        ui_print <string>
    //            display <string> on the screen.
    //
    //        wipe_cache
    //            a wipe of cache will be performed following a successful
    //            installation.
    //
    //        clear_display
    //            turn off the text display.
    //
    //        enable_reboot
    //            packages can explicitly request that they want the user
    //            to be able to reboot during installation (useful for
    //            debugging packages that don't exit).
    //
    //   - the name of the package zip file.
    //

    const char** args = (const char**)malloc(sizeof(char*) * 5);          //创建指针数组,并分配内存
    args[0] = binary;                                                     //[0]存放字符串 "/tmp/update_binary" ,也就是升级包解压的目的地址
    args[1] = EXPAND(RECOVERY_API_VERSION);   // defined in Android.mk    //[1]存放RECOVERY_API_VERSION,在Android.mk中定义,我的值为3  RECOVERY_API_VERSION := 3
    char* temp = (char*)malloc(10);
    sprintf(temp, "%d", pipefd[1]);
    args[2] = temp;
    args[3] = (char*)path;                                                //[3]存放update.zip路径
    args[4] = NULL;

    pid_t pid = fork();                                                   //创建一个新进程,为子进程
    if (pid == 0) {       //进程创建成功,执行META-INF/com/google/android/update-binary脚本,给脚本传入参数args
        umask(022);
        close(pipefd[0]);
        execv(binary, (char* const*)args);
        fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
        _exit(-1);
    }
    close(pipefd[1]);

    *wipe_cache = false;

    char buffer[1024];
    FILE* from_child = fdopen(pipefd[0], "r");
    while (fgets(buffer, sizeof(buffer), from_child) != NULL) {                    //父进程通过管道pipe读取子进程的值,使用strtok分割函数把子进程传过来的参数进行解析,执行相应的ui修改
        char* command = strtok(buffer, " \n"); 
        if (command == NULL) {
            continue;
        } else if (strcmp(command, "progress") == 0) {
            char* fraction_s = strtok(NULL, " \n");
            char* seconds_s = strtok(NULL, " \n");

            float fraction = strtof(fraction_s, NULL);
            int seconds = strtol(seconds_s, NULL, 10);

            ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds);
        } else if (strcmp(command, "set_progress") == 0) {
            char* fraction_s = strtok(NULL, " \n");
            float fraction = strtof(fraction_s, NULL);
            ui->SetProgress(fraction);
        } else if (strcmp(command, "ui_print") == 0) {
            char* str = strtok(NULL, "\n");
            if (str) {
                ui->Print("%s", str);
            } else {
                ui->Print("\n");
            }
            fflush(stdout);
        } else if (strcmp(command, "wipe_cache") == 0) {
            *wipe_cache = true;
        } else if (strcmp(command, "clear_display") == 0) {
            ui->SetBackground(RecoveryUI::NONE);
        } else if (strcmp(command, "enable_reboot") == 0) {
            // packages can explicitly request that they want the user
            // to be able to reboot during installation (useful for
            // debugging packages that don't exit).
            ui->SetEnableReboot(true);
        } else {
            LOGE("unknown command [%s]\n", command);
        }
    }
    fclose(from_child);

    int status;
    waitpid(pid, &status, 0);
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
        return INSTALL_ERROR;
    }

    return INSTALL_SUCCESS;
}
复制代码

try_update_binary流程:

1.查找META-INF/com/google/android/update-binary二进制脚本

2.解压update.zip包到/tmp/update_binary

3.创建子进程,执行update-binary二进制安装脚本,并通过管道与父进程通信,父进程更新ui界面。

 

到此,android 的 Recovery的流程已经分析完了,知道流程后再去分析Recovery的相关问题就比较容易了。



 

Android------设置里面的恢复出厂设置:recovery 模式启动进入流程



1.  上层应用的设置->隐私权->恢复出厂设置对应的java代码在如下路径文件:
 packages/apps/Settings/src/com/android/settings/MasterClear.java
 MasterClear:mFinalClickListener()函数会发送一个广播出去:
 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));

2.  这个广播的接收者在收到广播之后会开启一个java服务线程:MasterClearReceiver:RebootThread
 frameworks/base/services/java/com/Android/server/MasterClearReceiver.java  -- TAG = "MasterClear"
 public void onReceive(Context context, Intent intent) {

[cpp]  view plain copy
  1.     RebootThread mThread = new RebootThread(context, intent);  
  2.     mThread.start();  
  3. }  


    在线程的run函数中会调用函数:RecoverySystem.rebootWipeUserData(mContext);这个方法是RecoverySystem类的静态方法。

3.  RecoverySystem类定义于文件:frameworks/base/core/java/android/os/RecoverySystem.java   --  TAG = "RecoverySystem"
 

[cpp]  view plain copy
  1. public class RecoverySystem {  
  2.   /** Used to communicate with recovery.  See bootable/recovery/recovery.c. */  
  3.       private static File RECOVERY_DIR = new File("/cache/recovery");  
  4.      private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");  
  5.      private static File LOG_FILE = new File(RECOVERY_DIR, "log");  
  6.     
  7.   public static void rebootWipeUserData(Context context)  
  8.          throws IOException {  
  9.          bootCommand(context, "--wipe_data");  
  10.      }  
  11.        
  12.      private static void bootCommand(Context context, String arg) throws IOException {  
  13.          RECOVERY_DIR.mkdirs();  // In case we need it  
  14.          COMMAND_FILE.delete();  // In case it's not writable  
  15.          LOG_FILE.delete();  
  16.    
  17.          FileWriter command = new FileWriter(COMMAND_FILE);  
  18.          try {  
  19.              command.write(arg);  // 往文件/cache/recovery/command中写入recovery ELF的执行参数。  
  20.              command.write("\n");  
  21.          } finally {  
  22.              command.close();  
  23.          }  
  24.    
  25.          // Having written the command file, go ahead and reboot  
  26.          PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);  
  27.          pm.reboot("recovery");  // 调用PowerManager类中的reboot方法  
  28.    
  29.          throw new IOException("Reboot failed (no permissions?)");  
  30.      }  
  31.  }  


 
4.  PowerManager类定义于文件:frameworks/base/core/java/android/os/PowerManager.java  --  TAG = "PowerManager"
[cpp]  view plain copy
  1. public class PowerManager  
  2. {  
  3.  ...  
  4.  public void reboot(String reason)  
  5.     {  
  6.         try {  
  7.             mService.reboot(reason);  
  8.         } catch (RemoteException e) {  
  9.         }  
  10.     }  
  11.    
  12.   public PowerManager(IPowerManager service, Handler handler)  
  13.     {  
  14.         mService = service;  
  15.         mHandler = handler;  
  16.     }  
  17.    
  18.  IPowerManager mService;  
  19.     Handler mHandler;  
  20. }  


5.  mService指向的是PowerManagerService类,这个类定义于文件:

[cpp]  view plain copy
  1. frameworks/base/services/java/com/android/server/PowerManagerService.java  --  TAG = "PowerManagerService"  
  2. /** 
  3.     * Reboot the device immediately, passing 'reason' (may be null) 
  4.     * to the underlying __reboot system call.  Should not return. 
  5.     */  
  6.    public void reboot(String reason)  
  7.    {  
  8.        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);  
  9.   
  10.        if (mHandler == null || !ActivityManagerNative.isSystemReady()) {  
  11.            throw new IllegalStateException("Too early to call reboot()");  
  12.        }  
  13.   
  14.        final String finalReason = reason;  
  15.        Runnable runnable = new Runnable() {  
  16.            public void run() {  
  17.                synchronized (this) {  
  18.                    ShutdownThread.reboot(mContext, finalReason, false);  
  19.                } // 调用ShutdownThread服务中的reboot方法  
  20.                 
  21.            }  
  22.        };  
  23.        // ShutdownThread must run on a looper capable of displaying the UI.  
  24.        mHandler.post(runnable);  
  25.   
  26.        // PowerManager.reboot() is documented not to return so just wait for the inevitable.  
  27.        synchronized (runnable) {  
  28.            while (true) {  
  29.                try {  
  30.                    runnable.wait();  
  31.                } catch (InterruptedException e) {  
  32.                }  
  33.            }  
  34.        }  
  35.    }  

[cpp]  view plain copy
  1. frameworks/base/services/java/com/android/server/PowerManagerService.java  --  TAG = "PowerManagerService"  
  2. /** 
  3.     * Reboot the device immediately, passing 'reason' (may be null) 
  4.     * to the underlying __reboot system call.  Should not return. 
  5.     */  
  6.    public void reboot(String reason)  
  7.    {  
  8.        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);  
  9.   
  10.        if (mHandler == null || !ActivityManagerNative.isSystemReady()) {  
  11.            throw new IllegalStateException("Too early to call reboot()");  
  12.        }  
  13.   
  14.        final String finalReason = reason;  
  15.        Runnable runnable = new Runnable() {  
  16.            public void run() {  
  17.                synchronized (this) {  
  18.                    ShutdownThread.reboot(mContext, finalReason, false);  
  19.                } // 调用ShutdownThread服务中的reboot方法  
  20.                 
  21.            }  
  22.        };  
  23.        // ShutdownThread must run on a looper capable of displaying the UI.  
  24.        mHandler.post(runnable);  
  25.   
  26.        // PowerManager.reboot() is documented not to return so just wait for the inevitable.  
  27.        synchronized (runnable) {  
  28.            while (true) {  
  29.                try {  
  30.                    runnable.wait();  
  31.                } catch (InterruptedException e) {  
  32.                }  
  33.            }  
  34.        }  
  35.    }  


6.  ShutdownThread类在下列文件中实现:
[cpp]  view plain copy
  1. frameworks/base/core/java/com/android/internal/app/ShutdownThread.java   -- TAG = "ShutdownThread"  
  2. public final class ShutdownThread extends Thread {  
  3.  ...  
  4.  public static void reboot(final Context context, String reason, boolean confirm) {  
  5.         mReboot = true;  
  6.         mRebootReason = reason;  
  7.         shutdown(context, confirm);  
  8.     }  
  9.      
  10.     ...  
  11.     public void run() {  
  12.      ...  
  13.      if (mReboot) {  
  14.             Log.i(TAG, "Rebooting, reason: " + mRebootReason);  
  15.             try {  
  16.                 Power.reboot(mRebootReason);  
  17.             } catch (Exception e) {  
  18.                 Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);  
  19.             }  
  20.         } else if (SHUTDOWN_VIBRATE_MS > 0) {  
  21.             ...  
  22.         }  
  23.         ...  
  24.     }  
  25. }  


 流程:reboot() --> shutdown() --> beginShutdownSequence() --> sInstance.start() --> run() --> Power.reboot(mRebootReason).
 最后调用Power类的reboot方法。

7.  Power类定义于文件: frameworks/base/core/java/android/os/Power.java    --- 

[cpp]  view plain copy
  1. public class Power  
  2. {  
  3.  ...  
  4.  public static void reboot(String reason) throws IOException  
  5.     {  
  6.         rebootNative(reason);  
  7.     }  
  8.   
  9.     private static native void rebootNative(String reason) throws IOException ;  
  10. }  


[cpp]  view plain copy
  1. frameworks/base/core/java/android/os/Power.java    ---  
  2. public class Power  
  3. {  
  4.  ...  
  5.  public static void reboot(String reason) throws IOException  
  6.     {  
  7.         rebootNative(reason);  
  8.     }  
  9.   
  10.     private static native void rebootNative(String reason) throws IOException ;  
  11. }  

调用本地JNI接口rebootNative().

8. Power类对应的JNI接口函数定义于文件:

[cpp]  view plain copy
  1. frameworks/base/core/jni/android_os_Power.cpp  
  2. static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason)  
  3. {  
  4.     sync();  
  5. #ifdef HAVE_ANDROID_OS  
  6.     if (reason == NULL) {  
  7.         reboot(RB_AUTOBOOT);  
  8.     } else {  
  9.         const char *chars = env->GetStringUTFChars(reason, NULL);  
  10.         __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,  
  11.                  LINUX_REBOOT_CMD_RESTART2, (char*) chars);  
  12.         env->ReleaseStringUTFChars(reason, chars);  // In case it fails.  
  13.     }  
  14.     jniThrowIOException(env, errno);  
  15. #endif  
  16. }  


 上面的各种宏定义于文件:bionic/libc/kernel/common/linux/reboot.h
[cpp]  view plain copy
  1. #define LINUX_REBOOT_MAGIC1 0xfee1dead  
  2. #define LINUX_REBOOT_MAGIC2 672274793  
  3. #define LINUX_REBOOT_MAGIC2A 85072278  
  4. #define LINUX_REBOOT_MAGIC2B 369367448  
  5. #define LINUX_REBOOT_MAGIC2C 537993216  
  6.   
  7. /* 
  8.  * Commands accepted by the _reboot() system call. 
  9.  * 
  10.  * RESTART     Restart system using default command and mode. 
  11.  * HALT        Stop OS and give system control to ROM monitor, if any. 
  12.  * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command. 
  13.  * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task. 
  14.  * POWER_OFF   Stop OS and remove all power from system, if possible. 
  15.  * RESTART2    Restart system using given command string. 
  16.  * SW_SUSPEND  Suspend system using software suspend if compiled in. 
  17.  * KEXEC       Restart system using a previously loaded Linux kernel 
  18.  */  
  19. #define LINUX_REBOOT_CMD_RESTART 0x01234567  
  20. #define LINUX_REBOOT_CMD_HALT 0xCDEF0123  
  21. #define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF  
  22. #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000  
  23. #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC  
  24. #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4  
  25. #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2  
  26. #define LINUX_REBOOT_CMD_KEXEC 0x45584543  
  27.   
  28. bionic/libc/include/sys/reboot.h  
  29. #define RB_AUTOBOOT     LINUX_REBOOT_CMD_RESTART  
  30. #define RB_HALT_SYSTEM  LINUX_REBOOT_CMD_HALT  
  31. #define RB_ENABLE_CAD   LINUX_REBOOT_CMD_CAD_ON  
  32. #define RB_DISABLE_CAD  LINUX_REBOOT_CMD_CAD_OFF  
  33. #define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF  


9.  libc中__reboot的实现
 bionic/libc/arch-arm/syscalls/__reboot.S

[cpp]  view plain copy
  1.  #include <sys/linux-syscalls.h>  
  2.   
  3.     .text  
  4.     .type __reboot, #function  
  5.     .globl __reboot  
  6.     .align 4  
  7.     .fnstart  
  8.   
  9. __reboot:  
  10.     .save   {r4, r7}  
  11.     stmfd   sp!, {r4, r7}  
  12.     ldr     r7, =__NR_reboot // 系统调用号 88, binoic/libc/include/sys/linux-syscalls.h  
  13.     swi     #0       
  14.     ldmfd   sp!, {r4, r7}  
  15.     movs    r0, r0  
  16.     bxpl    lr  
  17.     b       __set_syscall_errno  
  18.     .fnend  

10. reboot系统调用实现
 kernel/kernel/sys.c
 

[cpp]  view plain copy
  1. SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)  
  2.  {  
  3.   char buffer[256];  
  4.   int ret = 0;  
  5.    
  6.   /* We only trust the superuser with rebooting the system. */  
  7.   if (!capable(CAP_SYS_BOOT))  
  8.    return -EPERM;  
  9.    
  10.   /* For safety, we require "magic" arguments. */  
  11.   if (magic1 != LINUX_REBOOT_MAGIC1 ||  
  12.       (magic2 != LINUX_REBOOT_MAGIC2 &&  
  13.                   magic2 != LINUX_REBOOT_MAGIC2A &&  
  14.     magic2 != LINUX_REBOOT_MAGIC2B &&  
  15.                   magic2 != LINUX_REBOOT_MAGIC2C))  
  16.    return -EINVAL;  
  17.    
  18.   if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)  
  19.    cmd = LINUX_REBOOT_CMD_HALT;  
  20.    
  21.   lock_kernel();  
  22.   switch (cmd) {  
  23.    ...  
  24.    case LINUX_REBOOT_CMD_POWER_OFF:  
  25.     kernel_power_off();  
  26.     unlock_kernel();  
  27.     do_exit(0);  
  28.     break;  
  29.     
  30.    case LINUX_REBOOT_CMD_RESTART2:  
  31.     if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {  
  32.      unlock_kernel();  
  33.      return -EFAULT;  
  34.     }  
  35.     buffer[sizeof(buffer) - 1] = '\0';  
  36.     
  37.     kernel_restart(buffer);  
  38.     break;  
  39.     
  40.     ...  
  41.     
  42.    default:  
  43.     ret = -EINVAL;  
  44.     break;  
  45.   }  
  46.   unlock_kernel();  
  47.   return ret;  
  48.  }  
  49.   
  50.  void kernel_restart(char *cmd)  
  51.  {  
  52.   kernel_restart_prepare(cmd);  
  53.   if (!cmd)  
  54.    printk(KERN_EMERG "Restarting system.\n");  
  55.   else  
  56.    printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);  
  57.   machine_restart(cmd);  
  58.  }  
  59.  void kernel_restart_prepare(char *cmd)  
  60.  {  
  61.   blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); // 调用通知链reboot_notifier_list上的函数  
  62.   system_state = SYSTEM_RESTART;  
  63.   device_shutdown(); // shutdown设备  
  64.   sysdev_shutdown(); // 系统设备shutdoen  
  65.  }  
  66.   
  67.  @kernel/arch/arm/kernel/process.c  
  68.  void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart;  
  69.  void machine_restart(char *cmd)  
  70.  {  
  71.   arm_pm_restart(reboot_mode, cmd);  
  72.  }  
  73.  void arm_machine_restart(char mode, const char *cmd)  
  74.  {  
  75.   /* 
  76.    * Clean and disable cache, and turn off interrupts 
  77.    */  
  78.   cpu_proc_fin();  
  79.    
  80.   /* 
  81.    * Tell the mm system that we are going to reboot - 
  82.    * we may need it to insert some 1:1 mappings so that 
  83.    * soft boot works. 
  84.    */  
  85.   setup_mm_for_reboot(mode);  
  86.    
  87.   /* 
  88.    * Now call the architecture specific reboot code. 
  89.    */  
  90.   arch_reset(mode, cmd);   // reset硬件系统,写reboot标记,供bootloader中判断  
  91.    
  92.   /* 
  93.    * Whoops - the architecture was unable to reboot. 
  94.    * Tell the user! 
  95.    */  
  96.   mdelay(1000);  
  97.   printk("Reboot failed -- System halted\n");  
  98.   while (1);  
  99.  }  
  100.   
  101. 11. arch_reset()  
  102.  文件:kernel/arch/arm/mach-mt6516/system.c  
  103.  void arch_reset(char mode, const char *cmd)  
  104.  {  
  105.   printk("arch_reset: cmd = %s\n", cmd ? : "NULL");  
  106.    
  107.   if (cmd && !strcmp(cmd, "charger")) {  
  108.    /* do nothing */  
  109.   } else if (cmd && !strcmp(cmd, "recovery")) {  
  110.    rtc_mark_recovery();  // 写recovery的标记到寄存器中去。  
  111.   } else {  
  112.    rtc_mark_swreset();  
  113.   }  
  114.    
  115.   DRV_WriteReg32(RGU_USRST1,0xbb1f);   
  116.     
  117.   printk("MT6516 SW Reset\n");  
  118.   DRV_WriteReg32(WDT_MODE, 0x2221);  
  119.   DRV_WriteReg32(WDT_RESTART, 0x1971);  
  120.   DRV_WriteReg32(WDT_SWRST, 0x1209);  
  121.    
  122.   /* enter loop waiting for restart */  
  123.   while (1);  
  124.  }  
  125.  @ kernel/driver/ret/ret-mt6516.c  
  126.  /* used in arch_reset() */  
  127.  void rtc_mark_recovery(void)  
  128.  {  
  129.   u16 pdn1;  
  130.    
  131.   spin_lock_irq(&rtc_lock);  
  132.   pdn1 = rtc_read(RTC_PDN1) & ~0x0030;  
  133.   pdn1 |= 0x0010;  
  134.   rtc_writeif_unlock();  
  135.   rtc_write(RTC_PDN1, pdn1);  
  136.   rtc_writeif_lock();  
  137.   spin_unlock_irq(&rtc_lock);  
  138.  }  
  139.  /* used in arch_reset() */  
  140.  void rtc_mark_swreset(void)  
  141.  {  
  142.   u16 pdn1;  
  143.    
  144.   spin_lock_irq(&rtc_lock);  
  145.   pdn1 = rtc_read(RTC_PDN1) & ~0x0030;  
  146.   pdn1 |= 0x0020;  
  147.   rtc_writeif_unlock();  
  148.   rtc_write(RTC_PDN1, pdn1);  
  149.   rtc_writeif_lock();  
  150.   spin_unlock_irq(&rtc_lock);  
  151.  }  


 可以看出,recovery和reset都是往RTC_PDN1的bit5:bit4上分别写01和10来标识。
 
 
12. 正常的log如下:
[html]  view plain copy
  1. #logcat ShutdownThread:D *:S &  
  2. # --------- beginning of /dev/log/system  
  3. --------- beginning of /dev/log/main  
  4. D/ShutdownThread(  127): !!! Request to shutdown !!!  
  5. D/ShutdownThread(  127): Notifying thread to start radio shutdown  
  6. D/ShutdownThread(  127): shutdown acquire partial WakeLock 2  
  7. I/ShutdownThread(  127): Sending shutdown broadcast...  
  8. I/ShutdownThread(  127): Shutting down activity manager...  
  9. W/ShutdownThread(  127): Turning off radio...  
  10. I/ShutdownThread(  127): Waiting for Bluetooth and Radio...  
  11. I/ShutdownThread(  127): Radio and Bluetooth shutdown complete.  
  12. I/ShutdownThread(  127): Shutting down MountService  
  13. W/ShutdownThread(  127): Result code 0 from MountService.shutdown  
  14. [  127.981918] save exit: isCheckpointed 1  
  15. [  127.985002] save exit: isCheckpointed 1  
  16. I/ShutdownThread(  127): Rebooting, reason: recovery  
  17. [  128.081532] [lizhiguo reboot1] LINUX_REBOOT_CMD_RESTART2.  
  18. [  128.082357] GPS: mt3326_gps_shutdown: Shutting down  
  19. [  128.083011] GPS: mt3326_gps_power: Switching GPS device off  
  20. [  128.083741] GPS: mt3326_gps_power: null pointer!!  
  21. [  128.084376] GPIO Shut down  
  22. [  128.089814] [MATV] shutdown  
  23. [  128.090193] [H264_DEC] h264_dec_shutdown  
  24. [  128.090710] JPEG Codec shutdown  
  25. [  128.091248] ----MT6516 M3D shutdown----  
  26. [  128.091839] m2d_shutdown() is called  
  27. [  128.092320] ******** MT6516 WDT driver shutdown!! ********  
  28. [  128.093040] [MM_QUEUE] mm_queue_shutdown  
  29. [  128.094333] [lizhiguo reboot2] kernel_restart.  
  30. [  128.094955] Restarting system with command 'recovery'.  
  31. [  128.097483] [lizhiguo reboot3] arm_machine_restart.  
  32. [  128.099275] arch_reset: cmd = recovery  
  33. [  128.100917] MT6516 SW Reset  
  34. u516 EVBgetflashID ADBC successful!!!  
  35. [MEM] complex R/W mem test pass  



13. uboot中会先后检查三种方式进入recovery是否成立:第一种是kernel直接写一个寄存器来标记下次启动将进入recovery模式;第二种是快捷键:powerkey+downVOL;第三中就是上层应用发送下来的回复出厂设置的命令,这个命令在restart之前kernel会往MISC分区中写command(boot-recovery)。这项工作在文件:bootable/bootloader/uboot/board/mt6516/mt6516_recovery.c完成。
recovery_check_key_trigger()
recovery_check_command_trigger()

[cpp]  view plain copy
  1. BOOL recovery_check_command_trigger(void)  
  2. {  
  3.  struct misc_message misc_msg;  
  4.  struct misc_message *pmisc_msg = &misc_msg;  
  5.  const unsigned int size = NAND_WRITE_SIZE * MISC_PAGES;  
  6.  unsigned char *pdata;  
  7.      int ret;  
  8.   
  9.  pdata = (uchar*)malloc(sizeof(uchar)*size);  
  10.   
  11.  ret = mboot_recovery_load_misc(pdata, size);  
  12.    
  13.     if (ret < 0)  
  14.     {  
  15.      return FALSE;   
  16.     }   
  17.    
  18. #ifdef LOG_VERBOSE  
  19.     MSG("\n--- get_bootloader_message ---\n");  
  20.     dump_data(pdata, size);  
  21.     MSG("\n");  
  22. #endif  
  23.   
  24.  memcpy(pmisc_msg, &pdata[NAND_WRITE_SIZE * MISC_COMMAND_PAGE], sizeof(misc_msg));   
  25.  MSG("Boot command: %.*s\n"sizeof(misc_msg.command), misc_msg.command);  
  26.  MSG("Boot status: %.*s\n"sizeof(misc_msg.status), misc_msg.status);  
  27.  MSG("Boot message\n\"%.20s\"\n", misc_msg.recovery);  
  28.   
  29.  if(strcmp(misc_msg.command, "boot-recovery")==0)  
  30.  { g_boot_mode = RECOVERY_BOOT;  
  31.  }  
  32.   
  33.  return TRUE;  
  34. }  
  35. // recovery模式检测  
  36. BOOL recovery_detection(void)  
  37. {  
  38.  if ((DRV_Reg16(RTC_PDN1) & 0x0030) == 0x0010) { /* factory data reset */  
  39.   g_boot_mode = RECOVERY_BOOT;  
  40.   return TRUE;  
  41.  } // 读取寄存器的值  
  42.   
  43.     if(recovery_check_key_trigger())  
  44.     {  
  45.      return TRUE;  
  46.     }  
  47.     // 检测是否有快捷键按下  
  48.      
  49.     #ifdef CFG_NAND_BOOT  
  50.  recovery_check_command_trigger();  
  51.  #endif  
  52.  // 检测是否通过将忘MISC分区写命令的方式  
  53.  // 以上如果都不是,那么最后一次检查模式全局量是够是RECOVERY_BOOT  
  54.  if (g_boot_mode == RECOVERY_BOOT)  
  55.  { return TRUE;  
  56.  }  
  57.  else  
  58.  { return FALSE;  
  59.  }  
  60. }  


14. g_boot_mode = RECOVERY_BOOT这个成立之后,uboot将会从RECOVERY分区加载recovery.img进SDRAM来运行。
 其实这个recovery.img和boot.img结构类似,zImage一样,所不同的是ramdisk.img不同而已。
 在运行recovery这个elf的时候会从/cache/recovery/comamnd中读取参数,这个参数是android的上层应用写进入的,--wipe-data,
 之后会清除USERDATA和CACHE分区,在将recovery的log文件放在/cache/recovery/下,将原来的command文件删除,最后
 调用函数reboot(RB_AUTOBOOT)来重新启动系统。
 bootable/recovery/recovery.c
 
 最后需要注意的一个问题是,recovery这个elf在编译user-release版本软件的时候没有copy到/system/bin下面去,需要修改
 bootable/recovery/Android.mk文件中的如下地方:
 /* BENGIN: lizhiguo 2011-07-27, copy recovery to /system/bin for user builds.*/
 #LOCAL_MODULE_TAGS := eng
 /* END: lizhiguo 2011-07-27 */
 如果放开这行,将只会在eng版本软件中有copy到/system/bin的动作。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值