Android系统关机的全流程解析
在powermanager的api文档中,给出了一个关机/重启接口:
public void reboot (string reason)
对于这个接口的描述很简单,就是几句话。
接口的作用就是重启设备,而且,就算重启成功了也没有返回值。
需要包含reboot权限,也就是android.permission.reboot
唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null。
一、上层空间
1.frameworks/base/core/java/android/os/powermanager.java
/** * reboot the device. will not return if the reboot is * successful. requires the {@link android.manifest.permission#reboot} * permission. * * @param reason code to pass to the kernel (e.g., "recovery") to * request special boot modes, or null. */ public void reboot(string reason) { try { mservice.reboot(reason); } catch (remoteexception e) { } }
mservice为ipowermanager binder接口服务。
/** * {@hide} */ public powermanager(ipowermanager service, handler handler) { mservice = service; mhandler = handler; }
2.frameworks/base/core/java/android/os/ipowermanager.aidl
interface ipowermanager { ... void reboot(string reason); ... }
3.frameworks/base/services/java/com/android/server/powermanagerservice.java
/** * reboot the device immediately, passing 'reason' (may be null) * to the underlying __reboot system call. should not return. */ public void reboot(string reason) { mcontext.enforcecallingorselfpermission(android.manifest.permission.reboot, null); if (mhandler == null || !activitymanagernative.issystemready()) { throw new illegalstateexception("too early to call reboot()"); } final string finalreason = reason; runnable runnable = new runnable() { public void run() { synchronized (this) { shutdownthread.reboot(getuicontext(), finalreason, false); } } }; // shutdownthread must run on a looper capable of displaying the ui. mhandler.post(runnable); // powermanager.reboot() is documented not to return so just wait for the inevitable. synchronized (runnable) { while (true) { try { runnable.wait(); } catch (interruptedexception e) { } } } }
4.frameworks/base/services/java/com/android/server/pm/shutdownthread.java
/** * request a clean shutdown, waiting for subsystems to clean up their * state etc. must be called from a looper thread in which its ui * is shown. * * @param context context used to display the shutdown progress dialog. * @param reason code to pass to the kernel (e.g. "recovery"), or null. * @param confirm true if user confirmation is needed before shutting down. */ public static void reboot(final context context, string reason, boolean confirm) { mreboot = true; mrebootsafemode = false; mrebootreason = reason; shutdowninner(context, confirm); }
这里说明是需要重启,且不是安全模式,重启参数为传递下来的reason,shutdowninner的confirm参数是用来设置是否有确认提示框的,通过reboot接口调用重启是没有的,为false。
重启的实现在run()中,因为shutdownthread是thread的扩展,所以run会自动运行。
/** * 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() { 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 : ""); systemproperties.set(shutdown_action_property, reason); } /* * if we are rebooting into safe mode, write a system property * indicating so. */ if (mrebootsafemode) { systemproperties.set(reboot_safemode_property, "1"); } ... rebootorshutdown(mreboot, mrebootreason); }
在重启前会将重启原因写入sys.shutdown.requested,如果没有则为空,如果是安全模式还会将persist.sys.safemode置1,之后会进行一些关机前的预处理,关闭activitymanager以及mountservice,最终调用rebootorshutdown进行关机操作。
/** * do not call this directly. use {@link #reboot(context, string, boolean)} * or {@link #shutdown(context, boolean)} instead. * * @param reboot true to reboot or false to shutdown * @param reason reason for reboot */ public static void rebootorshutdown(boolean reboot, string reason) { if (reboot) { log.i(tag, "rebooting, reason: " + reason); try { powermanagerservice.lowlevelreboot(reason); } catch (exception e) { log.e(tag, "reboot failed, will attempt shutdown instead", e); } } else if (shutdown_vibrate_ms > 0) { // vibrate before shutting down vibrator vibrator = new systemvibrator(); try { vibrator.vibrate(shutdown_vibrate_ms); } 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,稍后分析。如果不是重启,即mreboot=false,那就是需要关机了,在shutdown函数中就能够知道。
/** * request a clean shutdown, waiting for subsystems to clean up their * state etc. must be called from a looper thread in which its ui * is shown. * * @param context context used to display the shutdown progress dialog. * @param confirm true if user confirmation is needed before shutting down. */ public static void shutdown(final context context, boolean confirm) { mreboot = false; mrebootsafemode = false; shutdowninner(context, confirm); }
关机的时候需要震动,就是这里了shutdown_vibrate_ms,默认的定义是500ms。但是在代码上看,无论如何,最后都会调用一下lowlevelshutdown函数,也就是关机。逻辑上,这里可能是个问题,但是实际中,如果重启操作能够调用成功的话,整个系统都重启了,后边的代码当然不可能执行到了。
目光转回powermanagerservice
4.frameworks/base/services/java/com/android/server/powermanagerservice.java
/** * low-level function to reboot the device. * * @param reason code to pass to the kernel (e.g. "recovery"), or null. * @throws ioexception if reboot fails for some reason (eg, lack of * permission) */ public static void lowlevelreboot(string reason) throws ioexception { nativereboot(reason); } /** * low-level function turn the device off immediately, without trying * to be clean. most people should use * {@link com.android.server.pm.internal.app.shutdownthread} for a clean shutdown. */ public static void lowlevelshutdown() { nativeshutdown(); }
很熟悉的字样native,是jni调用了:
private static native void nativeshutdown(); private static native void nativereboot(string reason) throws ioexception;
5.frameworks/base/services/jni/com_android_server_powermanagerservice.cpp
static jninativemethod gpowermanagerservicemethods[] = { /* name, signature, funcptr */ ... { "nativeshutdown", "()v", (void*) nativeshutdown }, { "nativereboot", "(ljava/lang/string;)v", (void*) nativereboot }, ... };
这两个好哥俩的实现也是在一起的:
static void nativeshutdown(jnienv *env, jobject clazz) { android_reboot(android_rb_poweroff, 0, 0); } static void nativereboot(jnienv *env, jobject clazz, jstring reason) { if (reason == null) { android_reboot(android_rb_restart, 0, 0); } else { const char *chars = env->getstringutfchars(reason, null); android_reboot(android_rb_restart2, 0, (char *) chars); env->releasestringutfchars(reason, chars); // in case it fails. } jnithrowioexception(env, errno); }
可以看到无论是关机还是重启,都是调用android_reboot来实现的,只是参数不一样而已。
6.system/core/libcutils/android_reboot.c
int android_reboot(int cmd, int flags, char *arg) { int ret = 0; int reason = -1; #ifdef recovery_pre_command if (cmd == (int) android_rb_restart2) { if (arg && strlen(arg) > 0) { char cmd[path_max]; sprintf(cmd, recovery_pre_command " %s", arg); system(cmd); } } #endif if (!(flags & android_rb_flag_no_sync)) sync(); if (!(flags & android_rb_flag_no_remount_ro)) remount_ro(); switch (cmd) { case android_rb_restart: reason = rb_autoboot; break; case android_rb_poweroff: ret = reboot(rb_power_off); return ret; case android_rb_restart2: // reboot_magic break; default: return -1; } #ifdef recovery_pre_command_clear_reason reason = rb_autoboot; #endif if (reason != -1) ret = reboot(reason); else ret = __reboot(linux_reboot_magic1, linux_reboot_magic2, linux_reboot_cmd_restart2, arg); return ret; }
以reboot recovery为例,arg即为recovery,所在在第五步的时候会传入android_rb_restart2。到了android_reboot函数中,会看到这样的定义#ifdef recovery_pre_command,即属于重启前会执行的命令,如果定义了就会执行。
下面也是做了一些关机重启前的预处理工作,sync()作用是将缓存中的信息写入磁盘,以免程序异常结束导致文件被损坏,linux系统关机前会做几次这样的动作;而remount_ro()作用是通过调用emergency_remount()强制将文件系统挂载为只读,不再允许任何写入操作,同时会通过检查/proc/mounts的设备状态来确认是否当前的所有写入工作已经完成,这个检查过程是阻塞操作。
接下来才是对参数的解析处理:
1)普通重启 android_rb_restart, reason = rb_autoboot;
2)关机 android_rb_poweroff, 无需reason,直接调用reboot进行关机;
3)带参数的特殊重启 android_rb_restart2, reason 将为默认值 -1
这里又出现一个#ifdef recovery_pre_command_clear_reason,如果定义了它,则无论上层传下来的参数是什么样的,最终都只是普通重启而已。定义它的方式是在boardconfig.mk中加入target_recovery_pre_command_clear_reason := true,应该有厂商会喜欢这么做的,毕竟除了普通重启,都可能带给用户一定的风险。
最后会对reason进行一个检测,那么通过上边的分析,其实只有带参数的特殊重启才会为-1,而不等于-1的情况中有普通重启和关机,而关机已经自行解决了……所以,不等于-1的情况到了这里也只有普通重启了。最终这里就是区分普通重启与特殊重启的地方了。这里再插入一个问题,其他的几个cmd都是什么值呢?答案在bionic/libc/include/sys/reboot.h中:
#define rb_autoboot linux_reboot_cmd_restart #define rb_halt_system linux_reboot_cmd_halt #define rb_enable_cad linux_reboot_cmd_cad_on #define rb_disable_cad linux_reboot_cmd_cad_off #define rb_power_off linux_reboot_cmd_power_off
而,linux_reboot_xxxx之类的在bionic/libc/kernel/common/linux/reboot.h中:
#define linux_reboot_magic1 0xfee1dead #define linux_reboot_magic2 672274793 /* warning: do not edit, auto-generated code - see top for instructions */ #define linux_reboot_magic2a 85072278 #define linux_reboot_magic2b 369367448 #define linux_reboot_magic2c 537993216 #define linux_reboot_cmd_restart 0x01234567 /* warning: do not edit, auto-generated code - see top for instructions */ #define linux_reboot_cmd_halt 0xcdef0123 #define linux_reboot_cmd_cad_on 0x89abcdef #define linux_reboot_cmd_cad_off 0x00000000 #define linux_reboot_cmd_power_off 0x4321fedc /* warning: do not edit, auto-generated code - see top for instructions */ #define linux_reboot_cmd_restart2 0xa1b2c3d4 #define linux_reboot_cmd_sw_suspend 0xd000fce2 #define linux_reboot_cmd_kexec 0x45584543
至于为什么他们是这样奇怪的值这个问题,我只能说他们是magic number,魔法嘛,本来就是正常人不能够理解的,所以~~~放过他们吧,只要知道他们没有是-1的就ok啦。
先来看reboot函数,按照往常的经验,reboot最终一定会调用到__reboot的。
7.bionic/libc/unistd/reboot.c
int reboot (int mode) { return __reboot( linux_reboot_magic1, linux_reboot_magic2, mode, null ); }
bingo!果然是这样,如此说来reboot(reason) -> reboot(rb_autoboot) -> __reboot( linux_reboot_magic1, linux_reboot_magic2, linux_reboot_cmd_restart, null ),要是直接这样写多好~~~免得绕这一层了。
二、kernel域
8.__reboot通过syscall来到内核
这里用一些篇幅简要介绍syscall,以后遇到类似的东西更好追踪一些。
第七步中的__reboot在arm架构的实现是这样的(bionic/libc/arch-arm/syscalls/__reboot.s)
entry(__reboot) .save {r4, r7} stmfd sp!, {r4, r7} ldr r7, =__nr_reboot swi #0 ldmfd sp!, {r4, r7} movs r0, r0 bxpl lr b __set_syscall_errno end(__reboot)
可以看出来,这里将__reboot的实现映射到了__nr_reboot, 而在bionic/libc/sys/linux-syscalls.h能够找到:
#define __nr_reboot (__nr_syscall_base + 88)
其被指定了一个固定的偏移量,在被调用的时候就是通过这个偏移量去内核中寻找对应的入口的,由此可见,内核中一定有着相同的定义,否则将不能成功调用。内核中对syscall偏移量的定义在内核源码中的arch/arm/include/asm/unistd.h,相关信息完全一致。
已经找到了内核中的对应映射,那么下一步就要去找寻真正的实现函数了,在include/asm-generic/unistd.h中可以找到内核对__nr_reboot的syscall函数映射,即
/* kernel/sys.c */ #define __nr_setpriority 140 __syscall(__nr_setpriority, sys_setpriority) #define __nr_getpriority 141 __syscall(__nr_getpriority, sys_getpriority) #define __nr_reboot 142 __syscall(__nr_reboot, sys_reboot)
同时,能够发现如此温馨的一幕,内核已经指引我们下一步该去哪里寻找sys_reboot,即kernel/sys.c。
9.kernel/sys.c
在进入这个文件前,我们先去include/linux/syscalls.h中查看一下sys_reboot的定义:
asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user *arg);
与__reboot的调用参数一致。
进入sys.c文件后,并没有找到名为sys_reboot的函数,而通过仔细查找,发现一个很有趣的函数,其定义为syscall_define4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),对比__reboot的参数,能够符合。究竟是不是这个函数?
同样在include/linux/syscalls.h文件中,能够找到这样几个定义:
#define syscall_define1(name, ...) syscall_definex(1, _##name, __va_args__) #define syscall_define2(name, ...) syscall_definex(2, _##name, __va_args__) #define syscall_define3(name, ...) syscall_definex(3, _##name, __va_args__) #define syscall_define4(name, ...) syscall_definex(4, _##name, __va_args__) #define syscall_define5(name, ...) syscall_definex(5, _##name, __va_args__) #define syscall_define6(name, ...) syscall_definex(6, _##name, __va_args__) ... #define syscall_definex(x, sname, ...) \ __syscall_definex(x, sname, __va_args__) ... #define __syscall_definex(x, name, ...) \ asmlinkage long sys##name(__sc_decl##x(__va_args__))
整合后等价于:
#define syscall_define4(name, ...) \ asmlinkage long sys##_name(__sc_decl##4(__va_args__))
这样就不难看出,syscall_define4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)就是sys_reboot,也就是上层调用的__reboot的最终实现。函数实现如下:
/* * 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) { char buffer[256]; int ret = 0; /* we only trust the superuser with rebooting the system. */ if (!capable(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; /* 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; }
在此函数中,首先会检测权限问题,只有超级用户才可以执行重启系统的操作:
/* we only trust the superuser with rebooting the system. */ if (!capable(cap_sys_boot)) return -eperm;
否则将返回权限错误。对应的权限列表在include/linux/capability.h中,重启操作为22.
随后对magic number进行了校验:
/* 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;
如果数据传输过程中没有发生错误的话,这里也当然不会有问题,所以只是一个安全性校验,基本不会发生错误。
之后有一个很有趣的检查,如果用户要求关机,而pm_power_off为空的话,就把用户的关机命令转换为挂起:
/* 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;
在arch/arm/kernel/process.c中可以找到它的定义:
/* * function pointers to optional machine specific functions */ void (*pm_power_off)(void); export_symbol(pm_power_off);
好的,只是一个函数指针,而且做了全局操作,整个kernel都可以调用它。以高通msm7x30为例,在arch/arm/mach-msm/pm2.c中对这个函数指针进行了赋值:
pm_power_off = msm_pm_power_off;
msm_pm_power_off的具体实现就不再跟踪了,各家的都不一样,跟下去没有太大意义。现在只要知道,我分析的这个kernel是给了这个函数指针赋值的,所以不为空,关机命令将正常执行。
接下来就是这个函数的正题了,对用户命令进行解析操作,同时这个过程是用reboot_mutex互斥锁来进行保护的,以保证同一时间只可能有一个解析过程,避免冲突。
下边贴出所有关机重启相关的命令定义:
/* * commands accepted by the _reboot() system call. * * restart restart system using default command and mode. * halt stop os and give system control to rom monitor, if any. * cad_on ctrl-alt-del sequence causes restart command. * cad_off ctrl-alt-del sequence sends sigint to init task. * power_off stop os and remove all power from system, if possible. * restart2 restart system using given command string. * sw_suspend suspend system using software suspend if compiled in. * kexec restart system using a previously loaded linux kernel */ #define linux_reboot_cmd_restart 0x01234567 #define linux_reboot_cmd_halt 0xcdef0123 #define linux_reboot_cmd_cad_on 0x89abcdef #define linux_reboot_cmd_cad_off 0x00000000 #define linux_reboot_cmd_power_off 0x4321fedc #define linux_reboot_cmd_restart2 0xa1b2c3d4 #define linux_reboot_cmd_sw_suspend 0xd000fce2 #define linux_reboot_cmd_kexec 0x45584543
注释中的说明很详细了,比较陌生的就是关于cad,其实就是用来想用ctrl+alt+del操作的;然后sw_syspend是软件休眠;kexec就太高端了,属于内核的一个补丁,用来利用老内核重启,详细资料:http://www.ibm.com/developerworks/cn/linux/l-kexec/?ca=dwcn-newsletter-linux
以上这些只有前六个命令被android系统所使用,为什么这么说,可以去看bionic/libc/include/sys/reboot.h,上边已经贴出了。linux_reboot_cmd_halt虽有定义,但是也没有发现android系统中哪里有调用,有高手找到的话,希望能够告知一下。最终的最终,能够用到的就只有三个:
- restart
- power_off
- restart2
10.最终实现
重启调用的是kernel_restart,区别是参数是不是空,关机则调用kernel_power_off(),先看关机:
/** * kernel_power_off - power_off the system * * shutdown everything and perform a clean system power_off. */ void kernel_power_off(void) { kernel_shutdown_prepare(system_power_off); if (pm_power_off_prepare) pm_power_off_prepare(); disable_nonboot_cpus(); syscore_shutdown(); printk(kern_emerg "power down.\n"); kmsg_dump(kmsg_dump_poweroff); machine_power_off(); } export_symbol_gpl(kernel_power_off);
最了一系列准备工作,最终调用machine_power_off():
void machine_power_off(void) { machine_shutdown(); if (pm_power_off) pm_power_off(); }
之前找寻的pm_power_off在这里就有用处了,是关机的最后一步操作。关机完成,之后看下重启操作:
/** * kernel_restart - reboot the system * @cmd: pointer to buffer containing command to execute for restart * or %null * * shutdown everything and perform a clean reboot. * this is not safe to call in interrupt context. */ void kernel_restart(char *cmd) { kernel_restart_prepare(cmd); 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); } export_symbol_gpl(kernel_restart);
同样的套路,也是会进行一些准备工作,之后调用machine_restart(cmd), 如果是普通重启,那么中个cmd就为null,如果是特殊重启,那么这个cmd就是一层一层传递下来得那个arg了。
void machine_restart(char *cmd) { machine_shutdown(); arm_pm_restart(reboot_mode, cmd); } ... void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart; export_symbol_gpl(arm_pm_restart);
而还记得刚才的pm2.c吗?在那里同样对arm_pm_restart进行了指针赋值:
arm_pm_restart = msm_pm_restart;
赋值的函数为msm_pm_init, 其调用为
late_initcall_sync(msm_pm_init);
late_initcall_sync的启动优先级是最低的,为7。module_init其实是6的优先级,数字越大优先级越低。所以,这样推断的话,最终arm_pm_restart这个函数指针会指向msm_pm_restart。关于msm_pm_restart的具体实现也不细看了,跟前边说的一样,都是各家不一样,就几行代码:
static void msm_pm_restart(char str, const char *cmd) { msm_rpcrouter_close(); msm_proc_comm(pcom_reset_chip, &restart_reason, 0); for (;;) ; }
但是细心的朋友可能会发现这里有一个restart_reason,这个并不是传递下来的参数。事实上,这个值已经在之前kernel_restart_prepare(cmd)的时候就已经设置好了。
void kernel_restart_prepare(char *cmd) { blocking_notifier_call_chain(&reboot_notifier_list, sys_restart, cmd); system_state = system_restart; usermodehelper_disable(); device_shutdown(); syscore_shutdown(); }
就是blocking_notifier机制,这个操作在之前的shutdown关机操作中也有,且是同一个list,都是reboot_notifier_list。也很容易理解,就是将注册在reboot_notifier_list上的函数传入相关参数后执行,作为了解,看一下具体是怎么使用的:(arch/arm/mach-msm/pm2.c)
static int msm_reboot_call (struct notifier_block *this, unsigned long code, void *_cmd) { if ((code == sys_restart) && _cmd) { char *cmd = _cmd; if (!strcmp(cmd, "bootloader")) { restart_reason = 0x77665500; } else if (!strcmp(cmd, "recovery")) { restart_reason = 0x77665502; } else if (!strcmp(cmd, "eraseflash")) { restart_reason = 0x776655ef; } else if (!strncmp(cmd, "oem-", 4)) { unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff; restart_reason = 0x6f656d00 | code; } else { restart_reason = 0x77665501; } } return notify_done; } static struct notifier_block msm_reboot_notifier = { .notifier_call = msm_reboot_call, }; ... static int __init msm_pm_init(void) { ... register_reboot_notifier(&msm_reboot_notifier); ... }
ok,万事大吉,在kernel_restart_prepare的时候msm_reboot_call会被首先调用,这个函数的作用就是根据用户命令给restart_reason赋值,从而在之后调用msm_pm_restart的时候使用。这里我们发现在reboot的时候可以带的参数不仅有recovery,bootloader,还有eraseflash和oem-???,字面上看应该是用来擦除rom和解锁之类的操作了。
三、关机怎么用?
本文的分析是由android给出的reboot接口开始的,但是分析来分析去,回头想一想会发现,android给出的接口reboot就真的只能重启而已,不能进行关机操作,可以在跟踪这个流程的过程中会发现,确实是有存在关机的相关接口的。那么关机该怎么用呢?
frameworks/base/services/java/com/android/serverbatteryservice.java
private final void shutdownifnopower() { // shut down gracefully if our battery is critically low and we are not powered. // wait until the system has booted before attempting to display the shutdown dialog. if (mbatterylevel == 0 && !ispowered() && activitymanagernative.issystemready()) { intent intent = new intent(intent.action_request_shutdown); intent.putextra(intent.extra_key_confirm, false); intent.setflags(intent.flag_activity_new_task); mcontext.startactivity(intent); }
上一篇: 关于javaWeb中405错误的解决方法
下一篇: 字符串desede 3des加密示例分享
推荐阅读
-
Android系统关机的全流程解析
-
在CentOS系统上从零开始搭建WordPress博客的全流程记录
-
在CentOS系统上从零开始搭建WordPress博客的全流程记录,centoswordpress
-
解析Android获取系统cpu信息,内存,版本,电量等信息的方法详解
-
Android 关机弹出选择菜单的深入解析
-
解析android中系统日期时间的获取
-
解析Android获取系统cpu信息,内存,版本,电量等信息的方法详解
-
JSP+Servlet制作Java Web登录功能的全流程解析
-
Android9.0 SystemUI 网络信号栏定制修改的流程解析
-
解析android中系统日期时间的获取