亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

Android 關機、重啟、recovery流程分析

標簽:
Android

以Android5.1的代码来分析。

应用层和框架层实现

上层应用可以通过PowerManager来实现关机、重启、进recovery等功能。比如RecoverySystem 中就是使用PM使系统进入recovery模式:

源码路径:frameworks/base/core/java/android/os/RecoverySystem.java

    private static void bootCommand(Context context, String... args) throws IOException {        ......
        // Having written the command file, go ahead and reboot
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        pm.reboot(PowerManager.REBOOT_RECOVERY);       ......
    }

PowerManager会通过aidl与PowerManagerService交互,reboot带reason参数,像重启、关机、恢复出厂设置等等;

源码路径:frameworks/base/core/java/android/os/IPowerManager.aidl

reboot在aidl中的定义
interface IPowerManager{    // WARNING: The first five methods must remain the first five methods because their
    // transaction numbers must not change unless IPowerManager.cpp is also updated.
    void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws,
            String historyTag);    void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName,            int uidtoblame);    void releaseWakeLock(IBinder lock, int flags);    void updateWakeLockUids(IBinder lock, in int[] uids);
    ......    void reboot(boolean confirm, String reason, boolean wait);    void shutdown(boolean confirm, boolean wait);    void crash(String message);    void setStayOnSetting(int val);    void boostScreenBrightness(long time);    // temporarily overrides the screen brightness settings to allow the user to
    // see the effect of a settings change without applying it immediately
    void setTemporaryScreenBrightnessSettingOverride(int brightness);    void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj);    // sets the attention light (used by phone app only)
    void setAttentionLight(boolean on, int color);
}
PMS中reboot调用shutdownOrRebootInternal

源码路径:frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

        @Override // Binder call
        public void reboot(boolean confirm, String reason, boolean wait) {
            ......            final long ident = Binder.clearCallingIdentity();            try {
                shutdownOrRebootInternal(false, confirm, reason, wait);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

    public static void lowLevelShutdown() {
        SystemProperties.set("sys.powerctl", "shutdown");
    }
    public static void lowLevelReboot(String reason) {        if (reason == null) {
            reason = "";
        }
        long duration;        if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
            SystemProperties.set("ctl.start", "pre-recovery");            duration = 300 * 1000L;
        } else {
            SystemProperties.set("sys.powerctl", "reboot," + reason);            duration = 20 * 1000L;
        }
        try {            Thread.sleep(duration);
        } catch (InterruptedException e) {            Thread.currentThread().interrupt();
        }
    }

shutdownOrRebootInternal根据reason来决定是调用ShutdownThread的reboot或者shutdown:

  private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,            final String reason, boolean wait) {
        ......
        Runnable runnable = new Runnable() {            @Override
            public void run() {                synchronized (this) {                    if (shutdown) {
                        ShutdownThread.shutdown(mContext, confirm);
                    } else {
                        ShutdownThread.reboot(mContext, reason, confirm);
                    }
                ......

不管是关机还是重启都是走shutdownInner 源码路径: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;
        mRebootReason = reason;
        shutdownInner(context, confirm);
    }
    ......    public static void shutdown(final Context context, boolean confirm) {
        mReboot = false;
        mRebootSafeMode = false;
        shutdownInner(context, confirm);
    }
    ......    static void shutdownInner(final Context context, boolean confirm) {
    ......
            beginShutdownSequence(context);
        }
    }

看一下beginShutdownSequence

    private static void beginShutdownSequence(Context context) {
       ......        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        sInstance.start();
    }

已经start了,我们就去run里面瞧一瞧干了什么事,就对“REBOOT_SAFEMODE_PROPERTY”设置了1或者0,然后调用rebootOrShutdown。

   /**
     * Makes sure we handle the shutdown gracefully.
     * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
     */
    public void run() {        ....//关机或者重启前的一些处理
            String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
            SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);......
        if (mRebootSafeMode) {
            SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
        }        ......
        rebootOrShutdown(mReboot, mRebootReason);
    }

rebootOrShutdown中会调用PMS的lowLevelReboot或者lowLevelShutdown,这两个函数在上面有展示

    public static void rebootOrShutdown(boolean reboot, String reason) {        if (reboot) {            Log.i(TAG, "Rebooting, reason: " + reason);
            PowerManagerService.lowLevelReboot(reason);//重启
            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
        } else if (SHUTDOWN_VIBRATE_MS > 0) {      ....//关机震动相关处理
        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown();//关机
    }

再回过头去看PowerManagerService里面的lowLevelShutdown,发现关机、重启、recovery等是通过设置Properties来实现控制的:SystemProperties.set("sys.powerctl"......


native实现

Android系统开始会执行init,而init会去解析执行init.rc中的内容。其中就有对于Properties “sys.powerctl ”的处理(当“sys.powerctl 发生改变时触发进行powerctl 操作)。 位于源码:system/core/rootdir/init.rc

on property:sys.powerctl=*
    powerctl ${sys.powerctl}

而powerctl 是init的内嵌命令,在init源码中: 源码路径:system/core/init/builtins.c

int do_powerctl(int nargs, char **args)
{
......    if (strncmp(command, "shutdown", 8) == 0) {
        cmd = ANDROID_RB_POWEROFF;        len = 8;
    } else if (strncmp(command, "reboot", 6) == 0) {
        cmd = ANDROID_RB_RESTART2;        len = 6;
    } else {
        ERROR("powerctl: unrecognized command '%s'\n", command);        return -EINVAL;
    }    if (command[len] == ',') {
        reboot_target = &command[len + 1];
    } 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系统调用reboot,如果是recovery、wipe data等操作还需要额外写参数到misc分区,以便开机时区分正常进Android系统还是recovery、efex。

源码路径:system/core/libcutils/android_reboot.c

int android_reboot(int cmd, int flags UNUSED, 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:
            //ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
            //               LINUX_REBOOT_CMD_RESTART2, arg);
            write_misc(arg);
            ret = reboot(RB_AUTOBOOT);            break;
        default:
            ret = -1;
    }    return ret;
}

write_misc源码路径:system/core/libcutils/misc_rw.c有兴趣的可以研究一下。

static const char *MISC_DEVICE = "/dev/block/by-name/misc";/* Bootloader Message
 */struct bootloader_message {
    char command[32];    char status[32];    char recovery[1024];
};
....../* force the next boot to recovery/efex */int write_misc(char *reason) {    struct bootloader_message boot, temp;

    memset(&boot, 0, sizeof(boot));    if (!strcmp("recovery", reason)) {
        reason = "boot-recovery";
    }    strcpy(boot.command, reason);    if (set_bootloader_message_block(&boot, MISC_DEVICE) )        return -1;    //read for compare
    memset(&temp, 0, sizeof(temp));    if (get_bootloader_message_block(&temp, MISC_DEVICE))        return -1;    if( memcmp(&boot, &temp, sizeof(boot)) )        return -1;    return 0;
}static int set_bootloader_message_block(const struct bootloader_message *in,                                        const char* device) {
    FILE* f = fopen(device, "wb");    if (f == NULL) {
        LOGE("Can't open %s\n(%s)\n", device, strerror(errno));        return -1;
    }    int count = fwrite(in, sizeof(*in), 1, f);    if (count != 1) {
        LOGE("Failed writing %s\n(%s)\n", device, strerror(errno));        return -1;
    }
    sync();    if (fclose(f) != 0) {
        LOGE("Failed closing %s\n(%s)\n", device, strerror(errno));        return -1;
    }    return 0;
}

那么问题来了,开机时怎么判断是该正常开机、还是进入recovery呢? 一般是在u-boot中读取misc分区的数据,然后进行解析后对boot env "bootcmd"进行参数配置,这样就可以引导进入不同的系统了。 例如全志平台u-boot: board/sunxi/common/board_common.c

int update_bootcmd(void)
{
......
	misc_message = (struct bootloader_message *)misc_args;
	memset(misc_args, 0x0, 2048);
	memset(misc_fill, 0xff, 2048);
	
	mode = detect_other_boot_mode();
	switch(mode)
	{
......		case ANDROID_NULL_MODE:
			{
......
				else
				{
					misc_offset = sunxi_partition_get_offset_byname("misc");
......
						printf("misc partition found\n");						//read misc partition data
						sunxi_flash_read(misc_offset, 2048/512, misc_args);
......
	else if(!strcmp(misc_message->command, "boot-recovery"))
......
			puts("Recovery detected, will boot recovery\n");
			sunxi_str_replace(boot_commond, "boot_normal", "boot_recovery");
		}		/* android recovery will clean the misc */
	}
	else if(!strcmp(misc_message->command, "bootloader"))
	{
		puts("Fastboot detected, will boot fastboot\n");
		sunxi_str_replace(boot_commond, "boot_normal", "boot_fastboot");		if(misc_offset)
			sunxi_flash_write(misc_offset, 2048/512, misc_fill);
	}
	else if(!strcmp(misc_message->command, "usb-recovery"))
	{
		puts("Recovery detected, will usb recovery\n");
		sunxi_str_replace(boot_commond, "boot_normal", "boot_recovery");
	}
	else if(!strcmp(misc_message->command ,"debug_mode"))
	{
		puts("debug_mode detected ,will enter debug_mode");		if(0 == debug_mode_set())
		{
			debug_mode_update_info();
		}
		sunxi_flash_write(misc_offset,2048/512,misc_fill);
......
	setenv("bootcmd", boot_commond);
	printf("to be run cmd=%s\n", boot_commond);

	return 0;
}

boot_recovery的evn:

"boot_recovery=sunxi_flash read 4007f800 recovery;boota 4007f800\0"

boot_recovery将recovery加载到内存中并引导,然后就这样就进入recovery中了。

原文出处

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消