android reboot流程视图

下面是android简单的reboot流程视图

技术分享


下面是源码分析

从Java层的ShutdownThread.java的shutdown()开始:


frameworks/base/services/java/com/android/server/power/ShutdownThread.java

发出shutdown的广播,这个很重要,要通知所有应用我要关机了。

[java]
 private static void beginShutdownSequence(Context context) {
.......
        Log.i(TAG, "Sending shutdown broadcast...");
         
         // First send the high-level shut down broadcast.
         mActionDone = false;
         Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         mContext.sendOrderedBroadcastAsUser(intent,
                 UserHandle.ALL, null, br, mHandler, 0, null, null);
         
         final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
         synchronized (mActionDoneSync) {
             while (!mActionDone) {
                 long delay = endTime - SystemClock.elapsedRealtime();
                 if (delay <= 0) {
                     Log.w(TAG, "Shutdown broadcast timed out");
                     break;
                 } 
                 try {
                     mActionDoneSync.wait(delay);
                 } catch (InterruptedException e) {
                 }   
             }   
         }       

关闭activity管理器,因为不需要了。

         Log.i(TAG, "Shutting down activity manager...");
                 
         final IActivityManager am =
             ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
         if (am != null) {
             try {
                 am.shutdown(MAX_BROADCAST_TIME);
             } catch (RemoteException e) {
             }
         }

关闭radio设备,包括NFC,modem等。

        shutdownRadios(MAX_RADIO_WAIT_TIME);

关闭mountservice,这个是阻塞操作,失败会导致关机失败,把电耗光。成功则打印:Result code 0 from mount....

         Log.i(TAG, "Shutting down MountService");
                 
         // 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) {
                 }   
             }   
         }  


然后调rebootOrShutdown()函数
     public static void rebootOrShutdown(boolean reboot, String 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);
             } 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) {
             }   
         }   
             
         // Oem specific shutdown
        deviceRebootOrShutdown(reboot, reason);
             
         // Shutdown power
         Log.i(TAG, "Performing low-level shutdown...");
         PowerManagerService.lowLevelShutdown();
     }   

仔细看这函数,会走两个不同流程PowerManagerService.lowLevelReboot(reason);
或者PowerManagerService.lowLevelShutdown();
注意reason是重启用的,比如recovery,rtc,等。


下面来到PowerManagerSercice.java文件
frameworks/base/services/java/com/android/server/power/PowerManagerService.java

[java]
     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();                                                                                                   
         }
     }   

不管是lowLevelReboot还是lowLevelShutdown,都会执行SystemProperties.set("sys.powerctl",XXX)的操作。设置一下系统属性,有些同学可能会问,设下属性就可以了吗?当然可以。
因为这个属性会触发init.rc的某些操作,建议不懂的同学好好恶补一下init进程的作用吧。

来到init进程
system/core/init/

[init.rc]
 
 on property:sys.powerctl=*
     powerctl ${sys.powerctl}                                                                                                                                                
 
看到吧,往sys.powerctl属性写东西时,就会触发这个开关。就是调用powerctl指令,并把${sys.powerctl}参数传递给它。
这个指令init进程会解释。

在这里关联,powerctl指令对应的操作是do_powerctl
system/core/init/keywords.h 
[h]
      KEYWORD(powerctl,    COMMAND, 1, do_powerctl)
      KEYWORD(restart,     COMMAND, 1, do_restart)                                                                                                   
      KEYWORD(restorecon,  COMMAND, 1, do_restorecon)


system/core/init/builtins.c

[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);                                                                                                  
 }                             

do_powerctl函数接受${sys.powerctl}参数,然后调用android_reboot()函数
这个函数在android_reboot.c定义

system/core/libcutils/android_reboot.c 

android_reboot()函数做了如下几个工作:
第一:sync()回写block设备的内容,这个是阻塞操作
第二:remount_ro()把所有block设备remount成只读,不允许写操作,所以你会发现logkit抓的demsg最后一句话是SysRq : Emergency Remount R/O 因为后面的存储成只读模式了,无法写入,这时的LOG只存在内存中,要用last kmsg技术导出来。这个也是阻塞操作。
第三:调用reboot或__reboot函数

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
 int reboot (int  mode)
  {
      return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );
 }
从这里可以看出reboot还是调到__reboot函数,这个函数是汇编指令

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)

好吧,__reboot被扯到__NR_reboot里来了。那__NR_reboot是那里定义呢。

好吧,在这!!
bionic/libc/kernel/arch-arm/asm/unistd.h
 #define __NR_reboot (__NR_SYSCALL_BASE+ 88)
__NR_reboot指向内核系统调用的88入口处。

好吧,下面要进内核找找了
-------------------------------------------
注意:android的系统调用都在bionic里。
bionic/libc/kernel/arch-arm/unistd.h与kernel/include/asm-generic/uinistd.h系统调用是相对应的。
这点很重要,方便以后代码跟踪
---------------------------------------------

看kernel里的unistd.h文件

kernel/include/asm-generic/unistd.h

 #define __NR_reboot 142       
 __SYSCALL(__NR_reboot, sys_reboot)

__NR_reboot被映射到sys_reboot

搜索一下sys_reboot那里定义,好像找不到,别急

在这里,这里涉及到SYSCALL_DEFINE的宏,不懂的同事可以去补一下。
kernel/kernel/sys.c

 SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
          void __user *, arg)
 {

     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;
 
}

sys_reboot干了这些事
根据传入的reason,选择是kernel_restart()还是kernel_power_off()
如果是restart2还会把reason拷到内核空间,传给kernel_restart()函数

下面就转到平台相关代码,这里以高通为例

kernel/arch/arm/mach-msm/restart.c

如果是关机就调用
static void __msm_power_off(int lower_pshold)
{
printk(KERN_CRIT "Powering off the SoC\n");
}
打印关键LOG:Powering off the Soc

如果是重启会调用
 void msm_restart(char mode, const char *cmd)
msm_restart_prepare(cmd);

打印关键LOG:Going down for restart now

注意到msm_restart_prepare()没?

 static void msm_restart_prepare(const char *cmd)
 {   
 #ifdef CONFIG_MSM_DLOAD_MODE
     
     /* This looks like a normal reboot at this point. */
     set_dload_mode(0);
     
     /* Write download mode flags if we‘re panic‘ing */
     set_dload_mode(in_panic);
     
     /* Write download mode flags if restart_mode says so */
     if (restart_mode == RESTART_DLOAD)
         set_dload_mode(1);
 
     /* Kill download mode if master-kill switch is set */
     if (!download_mode)
         set_dload_mode(0);

 #endif 
     
     pm8xxx_reset_pwr_off(1);
     /* Hard reset the PMIC unless memory contents must be maintained. */
     if (get_dload_mode() || (cmd != NULL && cmd[0] != ‘\0‘))

         qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);
  
     else    
         qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET);

     if (cmd != NULL) {
         if (!strncmp(cmd, "bootloader", 10)) {
             __raw_writel(0x77665500, restart_reason);
         } 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;
             code = simple_strtoul(cmd + 4, NULL, 16) & 0xff;
             __raw_writel(0x6f656d00 | code, restart_reason);
         } else if (!strncmp(cmd, "edl", 3)) {
             enable_emergency_dload_mode();
         } else {
             __raw_writel(0x77665501, restart_reason);
         }   
     }   

msm_restart_prepare()函数是处理重启前的一些工作。
是否重启进入dload模式
根据reason判断是warm or hard reset
根据reason往imem里写内容
重启时sbl1读取,以便判断进入那种模式。

好吧。完了

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。