Android關機方法

Android系統關機接口總結如下,看下實現流程

一.framework接口關機
二.adb reboot -p
三.echo "o" > /proc/sysrq-trigger

一.framework接口關機

如低電量會自動關機,走如下流程

frameworks/base/services/java/com/android/server/power/PowerManagerService.java(http://aospxref.com/android-4.4.4_r2.0.1/xref/frameworks/base/services/java/com/android/server/power/PowerManagerService.java)

/**
     * Low-level function turn the device off immediately, without trying
     * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
     */
    public static void lowLevelShutdown() {
        SystemProperties.set("sys.powerctl", "shutdown");
    }

    /**
     * Low-level function to reboot the device. On success, this function
     * doesn't return. If more than 5 seconds passes from the time,
     * a reboot is requested, this method returns.
     *
     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
     */
    public static void lowLevelReboot(String reason) {
        if (reason == null) {
            reason = "";
        }
        SystemProperties.set("sys.powerctl", "reboot," + reason);
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

system/core/rootdir/init.rc(http://aospxref.com/android-4.4.4_r2.0.1/xref/system/core/rootdir/init.rc#404

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

system/core/init/keywords.h(http://aospxref.com/android-4.4.4_r2.0.1/xref/system/core/init/keywords.h)

KEYWORD(powerctl,    COMMAND, 1, do_powerctl)

system/core/init/builtins.c(http://aospxref.com/android-4.4.4_r2.0.1/xref/system/core/init/builtins.c

int do_powerctl(int nargs, char **args)
{
    char command[PROP_VALUE_MAX];
    int res;
    int len = 0;
    int cmd = 0;
    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;
        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);
}

system/core/libcutils/android_reboot.c(http://aospxref.com/android-4.4.4_r2.0.1/xref/system/core/libcutils/android_reboot.c#104)

int android_reboot(int cmd, int flags, 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 = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;

        default:
            ret = -1;
    }

    return ret;
}

bionic/libc/bionic/reboot.c(http://aospxref.com/android-4.4.4_r2.0.1/xref/bionic/libc/bionic/reboot.c)

int reboot (int  mode) 
{
    return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );
}

bionic/libc/arch-arm/syscalls/__reboot.S(http://aospxref.com/android-4.4.4_r2.0.1/xref/bionic/libc/arch-arm/syscalls/__reboot.S)

ENTRY(__reboot)
    mov     ip, r7
    ldr     r7, =__NR_reboot
    swi     #0
    mov     r7, ip
    cmn     r0, #(MAX_ERRNO + 1)
    bxls    lr
    neg     r0, r0
    b       __set_errno
END(__reboot)

bionic/libc/kernel/arch-arm/asm/unistd.h(http://aospxref.com/android-4.4.4_r2.0.1/xref/bionic/libc/kernel/arch-arm/asm/unistd.h

#define __NR_reboot (__NR_SYSCALL_BASE+ 88)

接下看下內核的系統調用

arch/arm/kernel/calls.S(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/kernel/calls.S?h=v4.4-rc1)

/* 85 */	CALL(sys_readlink)
		CALL(sys_uselib)
		CALL(sys_swapon)
		CALL(sys_reboot)
		CALL(OBSOLETE(sys_old_readdir))	/* used by libc4 */
/* 90 */	CALL(OBSOLETE(sys_old_mmap))	/* used by libc4 */

kernel/reboot.c(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/reboot.c?h=v4.4-rc1

/*
 * Reboot system call: for obvious reasons only root may call it,
 * and even root needs to set up some magic numbers in the registers
 * so that some mistake won't make this reboot the whole machine.
 * You can also set the meaning of the ctrl-alt-del-key here.
 *
 * reboot doesn't sync: do that yourself before calling this.
 */
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
		void __user *, arg)
{
	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(&system_transition_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:
		ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
		if (ret < 0) {
			ret = -EFAULT;
			break;
		}
		buffer[sizeof(buffer) - 1] = '\0';

		kernel_restart(buffer);
		break;

#ifdef CONFIG_KEXEC_CORE
	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(&system_transition_mutex);
	return ret;
}

也就是調用內核的kernel_power_off函數進行關機。

二.adb reboot -p

system/core/adb/commandline.c(http://aospxref.com/android-4.4.4_r2.0.1/xref/system/core/adb/commandline.c#924)

這裏對命令進行了封裝(加了分號)

int adb_commandline(int argc, char **argv)
{
    ...
    if(!strcmp(argv[0], "remount") || !strcmp(argv[0], "reboot")
            || !strcmp(argv[0], "reboot-bootloader")
            || !strcmp(argv[0], "tcpip") || !strcmp(argv[0], "usb")
            || !strcmp(argv[0], "root")) {
        char command[100];
        if (!strcmp(argv[0], "reboot-bootloader"))
            snprintf(command, sizeof(command), "reboot:bootloader");
        else if (argc > 1)
            snprintf(command, sizeof(command), "%s:%s", argv[0], argv[1]);
        else
            snprintf(command, sizeof(command), "%s:", argv[0]);
        int fd = adb_connect(command);
        if(fd >= 0) {
            read_and_dump(fd);
            adb_close(fd);
            return 0;
        }
        fprintf(stderr,"error: %s\n", adb_error());
        return 1;
    }
    ...
}

system/core/adb/services.c(http://aospxref.com/android-4.4.4_r2.0.1/xref/system/core/adb/services.c)

int service_to_fd(const char *name)
{
    int ret = -1;

    if(!strncmp(name, "tcp:", 4)) {
    } else if(!strncmp(name, "remount:", 8)) {
        ret = create_service_thread(remount_service, NULL);
    } else if(!strncmp(name, "reboot:", 7)) {
        void* arg = strdup(name + 7);
        if(arg == 0) return -1;
        ret = create_service_thread(reboot_service, arg);
    } 
}

void reboot_service(int fd, void *arg)
{
    char buf[100];
    char property_val[PROPERTY_VALUE_MAX];
    int ret;

    sync();

    ret = snprintf(property_val, sizeof(property_val), "reboot,%s", (char *) arg);
    if (ret >= (int) sizeof(property_val)) {
        snprintf(buf, sizeof(buf), "reboot string too long. length=%d\n", ret);
        writex(fd, buf, strlen(buf));
        goto cleanup;
    }

    ret = property_set(ANDROID_RB_PROPERTY, property_val);
    if (ret < 0) {
        snprintf(buf, sizeof(buf), "reboot failed: %d\n", ret);
        writex(fd, buf, strlen(buf));
        goto cleanup;
    }
    // Don't return early. Give the reboot command time to take effect
    // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
    while(1) { pause(); }
cleanup:
    free(arg);
    adb_close(fd);
}

而ANDROID_RB_PROPERTY定義爲sys.powerctl,後面的流程跟方法一一致

system/core/include/cutils/android_reboot.h(http://aospxref.com/android-4.4.4_r2.0.1/xref/system/core/include/cutils/android_reboot.h)

#define ANDROID_RB_PROPERTY "sys.powerctl"

三.echo "o" > /proc/sysrq-trigger

drivers/tty/sysrq.c(https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/tty/sysrq.c?h=v4.4.219)

static struct sysrq_key_op *sysrq_key_table[36] = {
	&sysrq_loglevel_op,		/* 0 */
	&sysrq_loglevel_op,		/* 1 */
	&sysrq_loglevel_op,		/* 2 */
	&sysrq_loglevel_op,		/* 3 */
	&sysrq_loglevel_op,		/* 4 */
	&sysrq_loglevel_op,		/* 5 */
	&sysrq_loglevel_op,		/* 6 */
	&sysrq_loglevel_op,		/* 7 */
	&sysrq_loglevel_op,		/* 8 */
	&sysrq_loglevel_op,		/* 9 */

	/*
	 * a: Don't use for system provided sysrqs, it is handled specially on
	 * sparc and will never arrive.
	 */
	NULL,				/* a */
	&sysrq_reboot_op,		/* b */
	&sysrq_crash_op,		/* c & ibm_emac driver debug */
	&sysrq_showlocks_op,		/* d */
	&sysrq_term_op,			/* e */
	&sysrq_moom_op,			/* f */
	/* g: May be registered for the kernel debugger */
	NULL,				/* g */
	NULL,				/* h - reserved for help */
	&sysrq_kill_op,			/* i */
#ifdef CONFIG_BLOCK
	&sysrq_thaw_op,			/* j */
#else
	NULL,				/* j */
#endif
	&sysrq_SAK_op,			/* k */
#ifdef CONFIG_SMP
	&sysrq_showallcpus_op,		/* l */
#else
	NULL,				/* l */
#endif
	&sysrq_showmem_op,		/* m */
	&sysrq_unrt_op,			/* n */
	/* o: This will often be registered as 'Off' at init time */
	NULL,				/* o */
	&sysrq_showregs_op,		/* p */
	&sysrq_show_timers_op,		/* q */
	&sysrq_unraw_op,		/* r */
	&sysrq_sync_op,			/* s */
	&sysrq_showstate_op,		/* t */
	&sysrq_mountro_op,		/* u */
	/* v: May be registered for frame buffer console restore */
	NULL,				/* v */
	&sysrq_showstate_blocked_op,	/* w */
	/* x: May be registered on mips for TLB dump */
	/* x: May be registered on ppc/powerpc for xmon */
	/* x: May be registered on sparc64 for global PMU dump */
	NULL,				/* x */
	/* y: May be registered on sparc64 for global register dump */
	NULL,				/* y */
	&sysrq_ftrace_dump_op,		/* z */
};

void __handle_sysrq(int key, bool check_mask){
        op_p = __sysrq_get_key_op(key);
        if (op_p) {
			op_p->handler(key);
	} 
}

static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
				   size_t count, loff_t *ppos)
{
	if (count) {
		char c;
		if (get_user(c, buf))
			return -EFAULT;
		__handle_sysrq(c, false);
	}
	return count;
}

static const struct file_operations proc_sysrq_trigger_operations = {
	.write		= write_sysrq_trigger,
	.llseek		= noop_llseek,
};

static void sysrq_init_procfs(void)
{
	if (!proc_create("sysrq-trigger", S_IWUSR, NULL,
			 &proc_sysrq_trigger_operations))
		pr_err("Failed to register proc interface\n");
}

"o"參數目前爲NULL,那在哪裏賦值呢

kernel/power/poweroff.c(https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/kernel/power/poweroff.c)

static void do_poweroff(struct work_struct *dummy)
{
	kernel_power_off();
}

static DECLARE_WORK(poweroff_work, do_poweroff);

static void handle_poweroff(int key)
{
	/* run sysrq poweroff on boot cpu */
	schedule_work_on(cpumask_first(cpu_online_mask), &poweroff_work);
}

static struct sysrq_key_op	sysrq_poweroff_op = {
	.handler        = handle_poweroff,
	.help_msg       = "poweroff(o)",
	.action_msg     = "Power Off",
	.enable_mask	= SYSRQ_ENABLE_BOOT,
};

static int __init pm_sysrq_init(void)
{
	register_sysrq_key('o', &sysrq_poweroff_op);
	return 0;
}

subsys_initcall(pm_sysrq_init);

也就是上面三種方法都是通過調用內核的kernel_power_off函數進行關機的。當然,小編最喜歡的是拔電池進行關機,簡單快捷高效。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章