Android关机流程

转自:http://www.2cto.com/kf/201501/367859.html


ShutdownThread.java文件

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

代码如下:

 

 

?
1
2
3
4
5
6
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
2
3
4
5
6
7
8
9
10
11
12
if (!checkAnimationFileExist()) {
     // throw up an indeterminate system dialog to indicate radio is
     // shutting down.
     ProgressDialog pd = new ProgressDialog(context);
     pd.setTitle(context.getText(com.android.internal.R.string.power_off));
     pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
     pd.setIndeterminate( true );
     pd.setCancelable( false );
     pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
 
     pd.show();
}

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

 

代码如下:

 

 

?
1
2
3
4
sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
         PowerManager.PARTIAL_WAKE_LOCK, TAG + -cpu); //这个只是锁住cpu不进入休眠,但screen是off的,需full锁来保证screen常亮
sInstance.mCpuWakeLock.setReferenceCounted( false );
sInstance.mCpuWakeLock.acquire();


 

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

代码如下:

 

?
1
2
3
4
sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
         PowerManager.FULL_WAKE_LOCK, TAG + -screen); //保持srceen常亮
sInstance.mScreenWakeLock.setReferenceCounted( false );
sInstance.mScreenWakeLock.acquire();

起一下新进程

 

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

代码如下:

 

?
1
2
3
4
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent, //发广播
         UserHandle.ALL, null , br, mHandler, 0 , null , null );

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

 

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

代码如下:

[ActivityManagerService.java]

 

?
1
2
3
4
5
6
7
8
final IActivityManager am =
             ActivityManagerNative.asInterface(ServiceManager.checkService(activity));
         if (am != null ) {
             try {
                 am.shutdown(MAX_BROADCAST_TIME);
             } catch (RemoteException e) {
             }
         }

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

 

代码如下:

[PackageManagerService.java]

 

?
1
2
3
4
5
final PackageManagerService pm = (PackageManagerService)
     ServiceManager.getService( package );
if (pm != null ) {
     pm.shutdown();
}

show shutdown animation,播放关机动画了

 

代码如下:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
private static void showShutdownAnimation() {
     /*
      * When boot completed, service.bootanim.exit property is set to 1.
      * Bootanimation checks this property to stop showing the boot animation.
      * Since we use the same code for shutdown animation, we
      * need to reset this property to 0. If this is not set to 0 then shutdown
      * will stop and exit after displaying the first frame of the animation
      */
     SystemProperties.set(service.bootanim.exit, 0 );
 
     SystemProperties.set(ctl.start, bootanim); //也是用bootanim进程,跟开关动画一样的方式。
}

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

 

代码如下:

shutdownRadios(MAX_RADIO_WAIT_TIME);


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

代码如下:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Set initial variables and time out time.
mActionDone = false ;
final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
synchronized (mActionDoneSync) {
     try {
         final IMountService mount = IMountService.Stub.asInterface(
                 ServiceManager.checkService(mount));
         if (mount != null ) {
             mount.shutdown(observer);
         } else {
             Log.w(TAG, MountService unavailable for shutdown);
         }
     } catch (Exception e) {
         Log.e(TAG, Exception during MountService shutdown, e);
     }
     while (!mActionDone) {
         long delay = endShutTime - SystemClock.elapsedRealtime();
         if (delay <= 0 ) {
             Log.w(TAG, Shutdown wait timed out);
             break ;
         }
         try {
             mActionDoneSync.wait(delay);
         } catch (InterruptedException e) {
         }
     }
}

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

 

代码如下:

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void rebootOrShutdown( boolean reboot, String reason) {
     deviceRebootOrShutdown(reboot, 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 ) {
         // vibrate before shutting down
         Vibrator vibrator = new SystemVibrator();
         try {
             vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
         } 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(); //关机
}

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

 

 

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

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

 

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

 

?
1
2
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]

 

?
1
KEYWORD(powerctl,    COMMAND, 1 , do_powerctl)

do_powerctl的实现

 

代码如下:

[system/core/init/builtins.c]

 

?
1
2
3
4
5
6
int do_powerctl( int nargs, char **args)
{
....
 
     return android_reboot(cmd, 0 , reboot_target);
}

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

 

 

[system/core/libcutils/android_reboot.c]

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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);
             break ;
 
         default :
             ret = - 1 ;
     }
 
     return ret;
}

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

 

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
2
3
int reboot( int mode) {
   return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);
}

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

 

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

 

?
1
2
3
4
5
6
7
8
9
10
ENTRY(__reboot)
     mov     ip, r7
     ldr     r7, =__NR_reboot //也跑到__NR_reboot系统调用
     swi     # 0
     mov     r7, ip
     cmn     r0, #(MAX_ERRNO + 1 )
     bxls    lr
     neg     r0, r0
     b       __set_errno_internal
END(__reboot)

 

 

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

如下:

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

 

?
1
#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
2
#define __NR_reboot 142
__SYSCALL(__NR_reboot, sys_reboot)

 

 

__NR_reboot 映射到 sys_reboot

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

用SYSCALL_DEFINE定义

[kernel/kernel/sys.c]

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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(&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 ] = '' ;
 
         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;
}

 

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

如下:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
void kernel_power_off( void )
{
     kernel_shutdown_prepare(SYSTEM_POWER_OFF); //关闭外设
     if (pm_power_off_prepare)
         pm_power_off_prepare();
     migrate_to_reboot_cpu();
     syscore_shutdown(); //关闭syscore
     printk(KERN_EMERG Power down.
); //关键打印
     kmsg_dump(KMSG_DUMP_POWEROFF);
     machine_power_off();
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void kernel_restart( char *cmd)
{
     kernel_restart_prepare(cmd); //关闭外设
     migrate_to_reboot_cpu();
     syscore_shutdown(); //关闭syscore
     if (!cmd)
         printk(KERN_EMERG Restarting system.
); //关键打印
     else
         printk(KERN_EMERG Restarting system with command '%s' .
, cmd);
     kmsg_dump(KMSG_DUMP_RESTART);
     machine_restart(cmd);
}

都执行XX_prepare()函数

 

 

?
1
2
3
4
5
6
7
8
static void kernel_shutdown_prepare( enum system_states state)
{
     blocking_notifier_call_chain(&reboot_notifier_list,
         (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL);
     system_state = state;
     usermodehelper_disable();
     device_shutdown();
}

?
1
2
3
4
5
6
7
void kernel_restart_prepare( char *cmd)
{
     blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
     system_state = SYSTEM_RESTART;
     usermodehelper_disable();
     device_shutdown();
}

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

 

machine_power_off() machine_resestart()函数实现

 

?
1
2
3
4
5
6
7
8
void machine_power_off( void )
{
     preempt_disable();
     smp_send_stop();
 
     if (pm_power_off)
         pm_power_off(); //关机
}

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void machine_restart( char *cmd)
{
     preempt_disable();
     smp_send_stop();
 
     /* Flush the console to make sure all the relevant messages make it
      * out to the console drivers */
     arm_machine_flush_console();
 
     arm_pm_restart(reboot_mode, cmd); //重启
 
     /* Give a grace period for failure to restart of 1s */
     mdelay( 1000 );
 
     /* Whoops - the platform was unable to reboot. Tell the user! */
     printk(Reboot failed -- System halted
);
     local_irq_disable();
     while ( 1 );
}

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

 

赋值如下:

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

 

?
1
2
pm_power_off = do_msm_poweroff;
arm_pm_restart = do_msm_restart;

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

 

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

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void do_msm_poweroff( void )
{
....
     pr_notice(Powering off the SoC
); //关键打印
#ifdef CONFIG_MSM_DLOAD_MODE
     set_dload_mode( 0 ); //关机,所以dloadmode是0
#endif
     qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN); //配置PMIC,是关机
.....
     /* MSM initiated power off, lower ps_hold */
     __raw_writel( 0 , msm_ps_hold); //拉 PS_HOLD,执行关机动作。
 
     mdelay( 10000 );
     pr_err(Powering off has failed
);
     return ;
}

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void do_msm_restart( enum reboot_mode reboot_mode, const char *cmd)
...
 
     pr_notice(Going down for restart now
); //关键打印
 
     msm_restart_prepare(cmd); //重启准备前动作
 
#ifdef CONFIG_MSM_DLOAD_MODE
     /*
      * Trigger a watchdog bite here and if this fails,
      * device will take the usual restart path.
      */
 
     if (WDOG_BITE_ON_PANIC && in_panic)
         msm_trigger_wdog_bite();
#endif
 
....
     halt_spmi_pmic_arbiter();
     __raw_writel( 0 , msm_ps_hold); //拉PS_HOLD重启
 
     mdelay( 10000 );
}

msm_restart_prepare()实现

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
static void msm_restart_prepare( const char *cmd)
{
#ifdef CONFIG_MSM_DLOAD_MODE
 
     /* Write download mode flags if we're panic'ing
      * Write download mode flags if restart_mode says so
      * Kill download mode if master-kill switch is set
      */
 
     set_dload_mode(download_mode &&
             (in_panic || restart_mode == RESTART_DLOAD)); //设置dload
#endif
 
     /* Hard reset the PMIC unless memory contents must be maintained. */
     if (get_dload_mode() || (cmd != NULL && cmd[ 0 ] != '' ))
         qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET); //设置PIMC为热重启
     else
         qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); //设置PIMC为硬重启
 
     if (cmd != NULL) {
         if (!strncmp(cmd, bootloader, 10 )) {
             __raw_writel( 0x77665500 , restart_reason); //写一些东东到IMEM,用于bootloader,recovery等
         } else if (!strncmp(cmd, recovery, 8 )) {
             __raw_writel( 0x77665502 , restart_reason);
         } else if (!strcmp(cmd, rtc)) {
             __raw_writel( 0x77665503 , restart_reason);
         } else if (!strncmp(cmd, oem-, 4 )) {
             unsigned long code;
             int ret;
             ret = kstrtoul(cmd + 4 , 16 , &code);
             if (!ret)
                 __raw_writel( 0x6f656d00 | (code & 0xff ),
                          restart_reason);
         } else if (!strncmp(cmd, edl, 3 )) {
             enable_emergency_dload_mode();
         } else {
             __raw_writel( 0x77665501 , restart_reason);
         }
     }
 
.....
 
}

 

 

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


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值