Android系统关机接口总结如下,看下实现流程
一.framework接口关机
二.adb reboot -p
三.echo "o" > /proc/sysrq-trigger
如低电量会自动关机,走如下流程
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函数进行关机。
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"
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函数进行关机的。当然,小编最喜欢的是拔电池进行关机,简单快捷高效。