我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android系统关机的全流程解析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android系统关机的全流程解析

在PowerManager的API文档中,给出了一个关机/重启接口:
public void reboot (String reason)

对于这个接口的描述很简单,就是几句话。
接口的作用就是重启设备,而且,就算重启成功了也没有返回值。
需要包含REBOOT权限,也就是android.permission.REBOOT
唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null。

一、上层空间
1.frameworks/base/core/java/android/os/PowerManager.java


 
public void reboot(String reason) 
{   
  try { 
    mService.reboot(reason); 
  } catch (RemoteException e) { 
  }   
}  

mService为IPowerManager Binder接口服务。


 
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


 
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


 
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会自动运行。


  
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(); 
    } 
  }; 
    
  {   
    String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : ""); 
    SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); 
  } 
    
  if (mRebootSafeMode) { 
    SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); 
  } 
  ... 
  rebootOrShutdown(mReboot, mRebootReason); 
}  

在重启前会将重启原因写入sys.shutdown.requested,如果没有则为空,如果是安全模式还会将persist.sys.safemode置1,之后会进行一些关机前的预处理,关闭ActivityManager以及MountService,最终调用rebootOrShutdown进行关机操作。


   
  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函数中就能够知道。


 
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


 
public static void lowLevelReboot(String reason) throws IOException { 
  nativeReboot(reason); 
}  
 
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[] = {  
   
  ... 
  { "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 
 
#define LINUX_REBOOT_MAGIC2A 85072278 
#define LINUX_REBOOT_MAGIC2B 369367448 
#define LINUX_REBOOT_MAGIC2C 537993216 
#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 

至于为什么他们是这样奇怪的值这个问题,我只能说他们是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函数映射,即


 
#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的最终实现。函数实现如下:


 
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, 
    void __user *, arg) 
{ 
  char buffer[256]; 
  int ret = 0; 
   
  if (!capable(CAP_SYS_BOOT)) 
    return -EPERM; 
   
  if (magic1 != LINUX_REBOOT_MAGIC1 || 
    (magic2 != LINUX_REBOOT_MAGIC2 && 
          magic2 != LINUX_REBOOT_MAGIC2A && 
      magic2 != LINUX_REBOOT_MAGIC2B && 
          magic2 != LINUX_REBOOT_MAGIC2C)) 
    return -EINVAL; 
   
  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; 
} 

在此函数中,首先会检测权限问题,只有超级用户才可以执行重启系统的操作:


 
if (!capable(CAP_SYS_BOOT)) 
  return -EPERM; 

否则将返回权限错误。对应的权限列表在include/linux/capability.h中,重启操作为22.
随后对magic number进行了校验:


 
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为空的话,就把用户的关机命令转换为挂起:


 
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off) 
  cmd = LINUX_REBOOT_CMD_HALT; 

在arch/arm/kernel/process.c中可以找到它的定义:


 
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互斥锁来进行保护的,以保证同一时间只可能有一个解析过程,避免冲突。
下边贴出所有关机重启相关的命令定义:


    
#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(),先看关机:


 
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在这里就有用处了,是关机的最后一步操作。关机完成,之后看下重启操作:


 
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); 
} 
您可能感兴趣的文章:Android 关机弹出选择菜单的深入解析Android开发实现长按返回键弹出关机框功能Android实现关机与重启的几种方式(推荐)Android手机获取root权限并实现关机重启功能的方法Android实现关机重启的方法分享Android编程实现系统重启与关机的方法Android6.0动态申请权限所遇到的问题小结Android获取设备隐私 忽略6.0权限管理谈谈Android6.0运行时的权限处理Android6.0 屏幕固定功能详解Android 6.0开发实现关机菜单添加重启按钮的方法


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android系统关机的全流程解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android系统关机的全流程解析

在PowerManager的API文档中,给出了一个关机/重启接口: public void reboot (String reason) 对于这个接口的描述很简单,就是几句话。 接口的作用就是重启设备,而且,就算重启成功了也没有返回值。
2022-06-06

Android教程之开机流程全面解析

本文详细讲述了Android的开机流程。分享给大家供大家参考,具体如下: 开机过程中无线模块的初始化过程;如果sim卡锁开启,或者pin被锁住的时候,会要求输入pin或者puk,但是这个解锁动作必须在系统初始化完成以后才能进行。(图形系统都
2022-06-06

Android View 绘制流程(Draw)全面解析

前言 前几篇文章,笔者分别讲述了DecorView,measure,layout流程等,接下来将详细分析三大工作流程的最后一个流程——绘制流程。测量流程决定了View的大小,布局流程决定了View的位置,那么绘制流程将决定View的样子,一
2022-06-06

Android View 布局流程(Layout)全面解析

前言 上一篇文章,笔者详细讲述了View三大工作流程的第一个,Measure流程,如果对测量流程还不熟悉的读者可以参考一下上一篇文章。测量流程主要是对View树进行测量,获取每一个View的测量宽高,那么有了测量宽高,就是要进行布局流程了,
2022-06-06

Android View 测量流程(Measure)全面解析

前言 上一篇文章,笔者主要讲述了DecorView以及ViewRootImpl相关的作用,这里回顾一下上一章所说的内容:DecorView是视图的顶级View,我们添加的布局文件是它的一个子布局,而ViewRootImpl则负责渲染视图,它
2022-06-06

Android 系统启动流分析 &amp; Zygote启动流程分析

本文是基于Android 7.1进行分析       Zygote在Android系统扮演着不可或缺的角色,Android系统的启动首先需要Zygote参与,比如启动SystemService , 还有一个就是孵化应用的进程,比如我们创建一
2022-06-06

Android系统的开机画面显示过程分析

函数fb_find_logo实现在文件kernel/goldfish/drivers/video/logo/logo.c文件中,如下所示:extern const struct linux_logo logo_linux_mono;  ex
2023-01-31

Android编程实现系统重启与关机的方法

本文实例讲述了Android编程实现系统重启与关机的方法。分享给大家供大家参考,具体如下: 最近在做个东西,巧合碰到了sharedUserId 的问题,所以收集了一些资料,存存档备份。 安装在设备中的每一个apk文件,Android 给每个
2022-06-06

Fedora 8系统安装的流程分析

这篇文章主要介绍Fedora 8系统安装的流程分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!很多用户都很了解Fedora了。这里我们主要介绍Fedora 8系统的安装和剖析使用方法,使用Fedora 8系统也有一
2023-06-17

Android 修改系统关机动画的实现

在Android 系统移植做自己的移动设备,肯定会遇到更改开机或者关机画面,配置自己产品logo 这点是必须的,这些都要在源码中修改,然后编译,下面给大家介绍个关机动画修改,一个简单示例! 文件路径:frameworks\base
2022-06-06

windows7系统关机时间长的原因剖析及解决

等待是一件很烦的事情,相信大家都是这么觉得的吧,尤其是等待电脑关机。很多用户都有疑问,为什么自己的电脑明明没什么问题关机却很慢,今天笔者就解析为何win7关机时间长。 1、开始菜单—>控制面板—>电源选项&mdash
2023-05-30

关于系统特征的详细解析

  下面就是我为大家准备的文章,是关于存储系统特点的解析,下面就开始阅读文章吧!存储系统是指计算机中由存放程序和数据的各种存储设备、控制部件及管理信息调度的设备(硬件)和算法(软件)所组成的系统。计算机的主存储器不能同时满足存取速度快、存储容量大和成本低的要求,在计算机中必须有速度由慢到快、容量由大到小的多级层次存储器
关于系统特征的详细解析
2024-04-17

windows7如何关闭系统保护(全过程图解)

javascript在桌面上的“计算机”图php标http://www.cppcns.com鼠xSQczEwKOJ标右键android“属性”2单击左侧的“系统保护”栏
2023-05-30

系统远程关机权限的设置

在内网环境中,有的人可能同时要管理或操作几台工作站,不用谈其他的管理操作,就连简单的关机操作,如果每次都是一台一台地进行的话,那工作量也是不小的。为了提高远程关机效android率,我编程客栈们可以赋予Guest账号远程关机权限,让网络管理
2023-05-24

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录