android 关机 流程_android L 关机流程图

本文详细介绍了Android L系统的关机流程,从Java层的ShutdownThread开始,包括请求音频焦点、显示关机对话框、获取并保持唤醒锁、发送关机广播、关闭Activity Manager、Package Manager、显示关机动画,直到最后的电源管理服务调用,涉及kernel层的电源控制。文章还提到了不同阶段的重要代码片段和关键函数,如device_shutdown、kernel_power_off和machine_restart。
摘要由CSDN通过智能技术生成

下面是简单的流程图,从Java到kernel层。

ShutdownThread.java文件

stop playing music,因为后面可能要playing shutdown music.

代码如下:(我在Android6.0上沒有看到調用requestAudioFocus的代碼)

private static void beginShutdownSequence(Context context) {

....

//acquire audio focus to make the other apps to stop playing muisc

mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

mAudioManager.requestAudioFocus(null,

AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

show system dialog to indicate phone is shutting down,如果没有关机动画的话,要show一个关机提示出来。

代码如下:

1 if (!checkAnimationFileExist()) {

2 // throw up an indeterminate system dialog to indicate radio is

3 // shutting down.

4 ProgressDialog pd = new ProgressDialog(context);

5 pd.setTitle(context.getText(com.android.internal.R.string.power_off));

6 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));

7 pd.setIndeterminate(true);

8 pd.setCancelable(false);

9 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

10

11 pd.show();

12 }

Hold the wakelock,make sure we never fall asleep again,抓锁防止机器关机过程中休眠

代码如下:

1 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(

2 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");//这个只是锁住cpu不进入休眠,但screen是off的,需full锁来保证screen常亮

3 sInstance.mCpuWakeLock.setReferenceCounted(false);

4 sInstance.mCpuWakeLock.acquire();

make sure the screen stays on,再抓一个full锁,防止屏幕半暗

代码如下:

1 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(

2 PowerManager.FULL_WAKE_LOCK, TAG + "-screen");//保持srceen常亮

3 sInstance.mScreenWakeLock.setReferenceCounted(false);

4 sInstance.mScreenWakeLock.acquire();

sending shutdown broadcast,发出广播,通知各app该保存数据赶紧的,我要关机了

代码如下:

1 Intent intent = new Intent(Intent.ACTION_SHUTDOWN);

2 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

3 mContext.sendOrderedBroadcastAsUser(intent,//发广播

4 UserHandle.ALL, null, br, mHandler, 0, null, null);

shutdown activity manager,关闭activity manager,即关闭AppOpsService,UsageStatsService,BatteryStatsService

注意:Android L 与KK在关闭UsageStatsService上有所区别

代码如下:

[ActivityManagerService.java]

1 final IActivityManager am =

2 ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));

3 if (am != null) {

4 try {

5 am.shutdown(MAX_BROADCAST_TIME);

6 } catch (RemoteException e) {

7 }

8 }

shutdown package manager,保存app使用时间到 disk里,这是android L新增的功能。

代码如下:

[PackageManagerService.java]

1 final PackageManagerService pm = (PackageManagerService)

2 ServiceManager.getService("package");

3 if (pm != null) {

4 pm.shutdown();

5 }

show shutdown animation,播放关机动画了

代码如下:

1 private static void showShutdownAnimation() {

2 /*

3 * When boot completed, "service.bootanim.exit" property is set to 1.

4 * Bootanimation checks this property to stop showing the boot animation.

5 * Since we use the same code for shutdown animation, we

6 * need to reset this property to 0. If this is not set to 0 then shutdown

7 * will stop and exit after displaying the first frame of the animation

8 */

9 SystemProperties.set("service.bootanim.exit", "0");

10

11 SystemProperties.set("ctl.start", "bootanim");//也是用bootanim进程,跟开关动画一样的方式。

12 }

shutdown radio[NFC,BT,MODEM],注意这里关闭modem这块与andorid KK的不一样。

代码如下:

shutdownRadios(MAX_RADIO_WAIT_TIME);

shutdown MountService,特别这里会导致关机失败。

代码如下:

1 // Set initial variables and time out time.

2 mActionDone = false;

3 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;

4 synchronized (mActionDoneSync) {

5 try {

6 final IMountService mount = IMountService.Stub.asInterface(

7 ServiceManager.checkService("mount"));

8 if (mount != null) {

9 mount.shutdown(observer);

10 } else {

11 Log.w(TAG, "MountService unavailable for shutdown");

12 }

13 } catch (Exception e) {

14 Log.e(TAG, "Exception during MountService shutdown", e);

15 }

16 while (!mActionDone) {

17 long delay = endShutTime - SystemClock.elapsedRealtime();

18 if (delay <= 0) {

19 Log.w(TAG, "Shutdown wait timed out");

20 break;

21 }

22 try {

23 mActionDoneSync.wait(delay);

24 } catch (InterruptedException e) {

25 }

26 }

27 }

走完上层关机流程,下面就要执行关机动作了。

代码如下:

1 public static void rebootOrShutdown(boolean reboot, String reason) {

2 deviceRebootOrShutdown(reboot, reason);

3 if (reboot) {

4 Log.i(TAG, "Rebooting, reason: " + reason);

5 PowerManagerService.lowLevelReboot(reason);//重启, 其中reason字符串可以爲空、“bootloader”、“recovery”, 在手機下次啓動中LK會根據這些值使手機進入不同的模式

6 Log.e(TAG, "Reboot failed, will attempt shutdown instead");

7 } else if (SHUTDOWN_VIBRATE_MS > 0) {

8 // vibrate before shutting down

9 Vibrator vibrator = new SystemVibrator();

10 try {

11 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);

12 } catch (Exception e) {

13 // Failure to vibrate shouldn't interrupt shutdown. Just log it.

14 Log.w(TAG, "Failed to vibrate during shutdown.", e);

15 }

16

17 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.

18 try {

19 Thread.sleep(SHUTDOWN_VIBRATE_MS);

20 } catch (InterruptedException unused) {

21 }

22 }

23

24 // Shutdown power

25 Log.i(TAG, "Performing low-level shutdown...");

26 PowerManagerService.lowLevelShutdown();//关机

27 }

从代码上看始终会走到lowLevelShutdown(),但如果是重启就不会,lowLevelReboot()就停止了。

lowLevelShutdown()与lowLevelReboot()都在PowerManagerService.java实现,其实都只是设置一个属性:SystemProperties.set("sys.powerctl", "xxx");

正是这个动作触发关机流程往下走,这涉及到init进程的4大功能,请参考我的另一篇文章Android的init进程

sys.powerctl属性触发开关在init.rc定义

on property:sys.powerctl=*

powerctl ${sys.powerctl}

我们来解读这句话,on property:sys.powerctl=*表示当属性sys.powerctl设置为任何值是都会跑到这里,触发动作是powerctl ${sys.powerctl},这个动作的意思是调用powerctl指令,并把sys.powerctl的值传给它。powerctl指令在init 进程会执行。

从下面的表可知,powerctl对应的操作是do_powerctl

[system/core/init/keywords.h]

KEYWORD(powerctl, COMMAND, 1, do_powerctl)

do_powerctl的实现

代码如下:

[system/core/init/builtins.c]

1 int do_powerctl(int nargs, char **args)

2 {

3 char command[PROP_VALUE_MAX];

4 int res;

5 int len = 0;

6 int cmd = 0;

7 const char *reboot_target;

8

9 res = expand_props(command, args[1], sizeof(command)); //args中存放的是: shutdown 或者 reboot 或者 reboot,bootloader 或者 reboot,recovery10 if (res) {

11 ERROR("powerctl: cannot expand '%s'\n", args[1]);

12 return -EINVAL;

13 }

14

15 if (strncmp(command, "shutdown", 8) == 0) {

16 cmd = ANDROID_RB_POWEROFF;

17 len = 8;

18 } else if (strncmp(command, "reboot", 6) == 0) {

19 cmd = ANDROID_RB_RESTART2;

20 len = 6;

21 } else {

22 ERROR("powerctl: unrecognized command '%s'\n", command);

23 return -EINVAL;

24 }

25

26 if (command[len] == ',') {

27 char prop_value[PROP_VALUE_MAX] = {0};

28 reboot_target = &command[len + 1]; // 存放reboot的reason,也就是下次手機重啓將要進入的模式29

30 if ((property_get("init.svc.recovery", prop_value) == 0) &&

31 (strncmp(reboot_target, "keys", 4) == 0)) {

32 ERROR("powerctl: permission denied\n");

33 return -EINVAL;

34 }

35 } else if (command[len] == '\0') { // 如果reason爲空,對於reboot來說,下次手機會正常啓機36 reboot_target = "";

37 } else {

38 ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);

39 return -EINVAL;

40 }

41

42 return android_reboot(cmd, 0, reboot_target);

43 }

它调用android_reboot()函数,实现如下:

[system/core/libcutils/android_reboot.c]

1 int android_reboot(int cmd, int flags UNUSED, const char *arg)

2 {

3 int ret;

4

5 sync();

6 remount_ro();

7

8 switch (cmd) {

9 case ANDROID_RB_RESTART:

10 ret = reboot(RB_AUTOBOOT);

11 break;

12

13 case ANDROID_RB_POWEROFF:

14 ret = reboot(RB_POWER_OFF);

15 break;

16

17 case ANDROID_RB_RESTART2: // arg中存放的是reboot的reason,如bootloader、recovery18 ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,

19 LINUX_REBOOT_CMD_RESTART2, arg);

20 break;

21

22 default:

23 ret = -1;

24 }

25

26 return ret;

27 }

从这里看出它的主要工作:

sync() 回写block设备的内容,这是阻塞型操作。

remount_ro() 把block设备remount成ro,这里有个关键LOG:SysRq : Emergency Remount R/O,这是在logkit所能看到的最后一句LOG,因为remount成ro了,后面的LOG要通过last kmsg技术导出来。

reboot()或者syscall(__NR_reboot....,这点与android KK不同,这边直接用syscall功能,KK则通过汇编。

后面syscall(__NR_reboot...知道,直接调用了linux的__NR_reboot系统调用,这个系统调用会跑哪里?后面会讲。

reboot()这个函数实现如下:

[bionic/libc/bionic/reboot.cpp]

1 int reboot(int mode) {

2 return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);

3 }

调用了__reboot,它在汇编实现 如下:

[bionic/libc/arch-arm/syscalls/__reboot.S]

1 ENTRY(__reboot)

2 mov ip, r7

3 ldr r7, =__NR_reboot //也跑到__NR_reboot系统调用 4 swi #0

5 mov r7, ip

6 cmn r0, #(MAX_ERRNO + 1)

7 bxls lr

8 neg r0, r0

9 b __set_errno_internal

10 END(__reboot)

__NR_reboot对应的内核入口在哪里?

如下:

[bionic/libc/kernel/uapi/asm-generic/unistd.h]

#define __NR_reboot 142

它在内核入口如下:

注:bionic/libc/kernel/uapi/asm-generic/unistd.h与kernel/include/uapi/asm-generic/unistd.h是对应的,方便以后代码追踪

[kernel/include/uapi/asm-generic/unistd.h]

1 #define __NR_reboot 142

2 __SYSCALL(__NR_reboot, sys_reboot)

__NR_reboot 映射到 sys_reboot

grep 下sys_reboot 找不到,其实在这里

用SYSCALL_DEFINE定义

[kernel/kernel/reboot.c]

1 /*

2 * Reboot system call: for obvious reasons only root may call it,

3 * and even root needs to set up some magic numbers in the registers

4 * so that some mistake won't make this reboot the whole machine.

5 * You can also set the meaning of the ctrl-alt-del-key here.

6 *

7 * reboot doesn't sync: do that yourself before calling this.

8 */

9 SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,

10 void __user *, arg)

11 {

12 struct pid_namespace *pid_ns = task_active_pid_ns(current);

13 char buffer[256];

14 int ret = 0;

15

16 /* We only trust the superuser with rebooting the system. */

17 if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))

18 return -EPERM;

19

20 /* For safety, we require "magic" arguments. */

21 if (magic1 != LINUX_REBOOT_MAGIC1 ||

22 (magic2 != LINUX_REBOOT_MAGIC2 &&

23 magic2 != LINUX_REBOOT_MAGIC2A &&

24 magic2 != LINUX_REBOOT_MAGIC2B &&

25 magic2 != LINUX_REBOOT_MAGIC2C))

26 return -EINVAL;

27

28 /*

29 * If pid namespaces are enabled and the current task is in a child

30 * pid_namespace, the command is handled by reboot_pid_ns() which will

31 * call do_exit().

32 */

33 ret = reboot_pid_ns(pid_ns, cmd);

34 if (ret)

35 return ret;

36

37 /* Instead of trying to make the power_off code look like

38 * halt when pm_power_off is not set do it the easy way.

39 */

40 if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)

41 cmd = LINUX_REBOOT_CMD_HALT;

42

43 mutex_lock(&reboot_mutex);

44 switch (cmd) {

45 caseLINUX_REBOOT_CMD_RESTART:

46 kernel_restart(NULL);

47 break;

48

49 case LINUX_REBOOT_CMD_CAD_ON:

50 C_A_D = 1;

51 break;

52

53 case LINUX_REBOOT_CMD_CAD_OFF:

54 C_A_D = 0;

55 break;

56

57 case LINUX_REBOOT_CMD_HALT:

58 kernel_halt();

59 do_exit(0);

60 panic("cannot halt");

61

62 caseLINUX_REBOOT_CMD_POWER_OFF:

63 kernel_power_off();64 do_exit(0);

65 break;

66

67 caseLINUX_REBOOT_CMD_RESTART2:

68 ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);

69 if (ret < 0) {

70 ret = -EFAULT;

71 break;

72 }

73 buffer[sizeof(buffer) - 1] = '\0';

74

75 kernel_restart(buffer);76 break;

77

78 #ifdef CONFIG_KEXEC

79 case LINUX_REBOOT_CMD_KEXEC:

80 ret = kernel_kexec();

81 break;

82 #endif

83

84 #ifdef CONFIG_HIBERNATION

85 case LINUX_REBOOT_CMD_SW_SUSPEND:

86 ret = hibernate();

87 break;

88 #endif

89

90 default:

91 ret = -EINVAL;

92 break;

93 }

94 mutex_unlock(&reboot_mutex);

95 return ret;

96 }

有很多分支,我们只关心kernel_power_off()和kernel_restart()两函数就行

如下:

kernel_power_off:

1 void kernel_power_off(void)

2 {

3 kernel_shutdown_prepare(SYSTEM_POWER_OFF);//关闭外设

4 if (pm_power_off_prepare)

5 pm_power_off_prepare();

6 migrate_to_reboot_cpu(); // 比如執行reboot命令的進程運行在CPU1上,而將來CPU1會先於CPU0被停止,故需要將當前進程轉移至CPU0上

7 syscore_shutdown();//关闭syscore

8 printk(KERN_EMERG "Power down.\n");//关键打印

9 kmsg_dump(KMSG_DUMP_POWEROFF);

10 machine_power_off();

11 }

kernel_restart:

1 void kernel_restart(char *cmd)

2 {

3 kernel_restart_prepare(cmd);//关闭外设

4 migrate_to_reboot_cpu();

5 syscore_shutdown();//关闭syscore

6 if (!cmd)

7 printk(KERN_EMERG "Restarting system.\n");//关键打印

8 else

9 printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);

10 kmsg_dump(KMSG_DUMP_RESTART);

11 machine_restart(cmd);

12 }

都执行XX_prepare()函数

1 static void kernel_shutdown_prepare(enum system_states state)

2 {

3 blocking_notifier_call_chain(&reboot_notifier_list,

4 (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL);

5 system_state = state;

6 usermodehelper_disable();

7 device_shutdown();

8 }

上面的第3行會遍歷reboot_notifier_list鏈表,向其中註冊的每一個notifier_block發送SYS_POWER_OFF事件。

在我們的驅動程序中可以調用如下函數將notifier_block註冊到系統中:

1 /**

2 * register_reboot_notifier - Register function to be called at reboot time

3 * @nb: Info about notifier function to be called

4 *

5 * Registers a function with the list of functions

6 * to be called at reboot time.

7 *

8 * Currently always returns zero, as blocking_notifier_chain_register()

9 * always returns zero.

10 */

11 int register_reboot_notifier(struct notifier_block *nb)

12 {

13 return blocking_notifier_chain_register(&reboot_notifier_list, nb);

14 }

15 EXPORT_SYMBOL(register_reboot_notifier);

在第7行中調用了device_shutdown()函數,如下:

1 /**

2 * device_shutdown - call ->shutdown() on each device to shutdown.

3 */

4 void device_shutdown(void)

5 {

6 struct device *dev, *parent;

7

8 spin_lock(&devices_kset->list_lock);

9 /*

10 * Walk the devices list backward, shutting down each in turn.

11 * Beware that device unplug events may also start pulling

12 * devices offline, even as the system is shutting down.

13 */

14 while (!list_empty(&devices_kset->list)) {

15 dev = list_entry(devices_kset->list.prev, struct device,

16 kobj.entry);

17 ......

18

19 if (dev->bus && dev->bus->shutdown) { // 如 i2c_bus_type20 if (initcall_debug)

21 dev_info(dev, "shutdown\n");

22 dev->bus->shutdown(dev);23 } else if (dev->driver && dev->driver->shutdown) { // 如spi_bus_type、usb_bus_type24 if (initcall_debug)

25 dev_info(dev, "shutdown\n");

26 dev->driver->shutdown(dev);27 }

28 ......

29 }

30 spin_unlock(&devices_kset->list_lock);

31 }

32

kernel_restart_prepare:

1 void kernel_restart_prepare(char *cmd)

2 {

3 blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);

4 system_state = SYSTEM_RESTART;

5 usermodehelper_disable();

6 device_shutdown();

7 }

除了前面不同,都调用了device_shutdown()函数,关闭外设。

machine_power_off() machine_resestart()函数实现

machine_power_off:

1 void machine_power_off(void)

2 {

3 preempt_disable();

4 smp_send_stop();

5

6 if (pm_power_off)

7 pm_power_off();//关机

8 }

其中,第4行的smp_send_stop會將除CPU0之外的CPU全部stop。

machine_restart:

1 void machine_restart(char *cmd)

2 {

3 preempt_disable();

4 smp_send_stop();

5

6 /* Flush the console to make sure all the relevant messages make it

7 * out to the console drivers */

8 arm_machine_flush_console();

9

10 arm_pm_restart(reboot_mode, cmd);//重启

11

12 /* Give a grace period for failure to restart of 1s */

13 mdelay(1000);

14

15 /* Whoops - the platform was unable to reboot. Tell the user! */

16 printk("Reboot failed -- System halted\n");

17 local_irq_disable();

18 while (1);

19 }

pm_power_offf() arm_pm_restart()都是一个函数指针

赋值如下:

[kernel/drivers/power/reset/msm-poweroff.c]

1 pm_power_off = do_msm_poweroff;

2 arm_pm_restart = do_msm_restart;

高通平台的关机代码与之前有所不同,现在文件msm-poweroff.c以前是restart.c。

do_msm_poweroff()与do_msm_restart()实现如下:

do_msm_poweroff:

1 static void do_msm_poweroff(void)

2 {

3 ....

4 pr_notice("Powering off the SoC\n");//关键打印

5 #ifdef CONFIG_MSM_DLOAD_MODE

6 set_dload_mode(0);//关机,所以dloadmode是0

7 #endif

8 qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN);//配置PMIC,是关机

9 .....

10 /* MSM initiated power off, lower ps_hold */

11 __raw_writel(0, msm_ps_hold);//拉 PS_HOLD,执行关机动作。

12

13 mdelay(10000);

14 pr_err("Powering off has failed\n");

15 return;

16 }

do_msm_restart:

1 static void msm_restart_prepare(const char *cmd)

2 {

3 #ifdef CONFIG_MSM_DLOAD_MODE

4

5 /* Write download mode flags if we're panic'ing

6 * Write download mode flags if restart_mode says so

7 * Kill download mode if master-kill switch is set

8 */

9

10 set_dload_mode(download_mode &&

11 (in_panic || restart_mode == RESTART_DLOAD));//设置dload

12 #endif

13

14 /* Hard reset the PMIC unless memory contents must be maintained. */

15 if (get_dload_mode() || (cmd != NULL && cmd[0] != '\0'))

16 qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);//设置PIMC为热重启

17 else

18 qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET);//设置PIMC为硬重启

19

20 if (cmd != NULL) {

21 if (!strncmp(cmd, "bootloader", 10)) {

22 __raw_writel(0x77665500, restart_reason);//写一些东东到IMEM,用于bootloader,recovery等

23 } else if (!strncmp(cmd, "recovery", 8)) {

24 __raw_writel(0x77665502, restart_reason);

25 } else if (!strcmp(cmd, "rtc")) {

26 __raw_writel(0x77665503, restart_reason);

27 } else if (!strncmp(cmd, "oem-", 4)) {

28 unsigned long code;

29 int ret;

30 ret = kstrtoul(cmd + 4, 16, &code);

31 if (!ret)

32 __raw_writel(0x6f656d00 | (code & 0xff),

33 restart_reason);

34 } else if (!strncmp(cmd, "edl", 3)) {

35 enable_emergency_dload_mode();

36 } else {

37 __raw_writel(0x77665501, restart_reason);

38 }

39 }

40

41 .....

42

43 }

do_msm_poweroff()与do_msm_restart()都设置了dload,PMIC,唯一不同的是do_msm_restart()里 多了一个__raw_writel的动作,即reason写入IMEM,目的在于重启进入sbl1时判断应该进入那种模式,如我们开发用的 bootloader模式,恢复出厂设置的recovery模式等。

完了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值