设备驱动-----Android关机流程总结2

在PowerManager的API文档中,给出了一个关机/重启接口:

public void reboot (String reason)

对于这个接口的描述很简单,就是几句话。

接口的作用就是重启设备,而且,就算重启成功了也没有返回值。

需要包含REBOOT权限,也就是android.permission.REBOOT

唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null。

--------------------------------上层空间--------------------------------

1.frameworks/base/core/java/android/os/PowerManager.java

  1. /**  
  2.  * Reboot the device.  Will not return if the reboot is 
  3.  * successful.  Requires the {@link android.Manifest.permission#REBOOT} 
  4.  * permission. 
  5.  * 
  6.  * @param reason code to pass to the kernel (e.g., "recovery") to 
  7.  *               request special boot modes, or null. 
  8.  */  
  9. public void reboot(String reason)  
  10. {     
  11.     try {  
  12.         mService.reboot(reason);  
  13.     } catch (RemoteException e) {  
  14.     }     
  15. }   

mService为IPowerManager Binder接口服务。

  1. /** 
  2.  * {@hide} 
  3.  */  
  4. public PowerManager(IPowerManager service, Handler handler)  
  5. {  
  6.     mService = service;  
  7.     mHandler = handler;  
  8. }  


2.frameworks/base/core/java/android/os/IPowerManager.aidl

  1. interface IPowerManager  
  2. {  
  3. ...  
  4. void reboot(String reason);  
  5. ...  
  6. }  

3.frameworks/base/services/java/com/android/server/PowerManagerService.java

  1. /**   
  2.  * Reboot the device immediately, passing 'reason' (may be null) 
  3.  * to the underlying __reboot system call.  Should not return. 
  4.  */  
  5. public void reboot(String reason)  
  6. {      
  7.     mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);  
  8.   
  9.     if (mHandler == null || !ActivityManagerNative.isSystemReady()) {  
  10.         throw new IllegalStateException("Too early to call reboot()");  
  11.     }      
  12.   
  13.     final String finalReason = reason;  
  14.     Runnable runnable = new Runnable() {  
  15.         public void run() {  
  16.             synchronized (this) {  
  17.                 ShutdownThread.reboot(getUiContext(), finalReason, false);  
  18.             }      
  19.   
  20.         }      
  21.     };     
  22.     // ShutdownThread must run on a looper capable of displaying the UI.  
  23.     mHandler.post(runnable);  
  24.   
  25.     // PowerManager.reboot() is documented not to return so just wait for the inevitable.  
  26.     synchronized (runnable) {  
  27.         while (true) {  
  28.             try {  
  29.                 runnable.wait();  
  30.             } catch (InterruptedException e) {   
  31.             }      
  32.         }      
  33.     }      
  34. }  

4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java

  1. /** 
  2.  * Request a clean shutdown, waiting for subsystems to clean up their 
  3.  * state etc.  Must be called from a Looper thread in which its UI 
  4.  * is shown. 
  5.  * 
  6.  * @param context Context used to display the shutdown progress dialog. 
  7.  * @param reason code to pass to the kernel (e.g. "recovery"), or null. 
  8.  * @param confirm true if user confirmation is needed before shutting down. 
  9.  */  
  10. public static void reboot(final Context context, String reason, boolean confirm) {  
  11.     mReboot = true;  
  12.     mRebootSafeMode = false;  
  13.     mRebootReason = reason;  
  14.     shutdownInner(context, confirm);  
  15. }  

这里说明是需要重启,且不是安全模式,重启参数为传递下来的reason,shutdownInner的confirm参数是用来设置是否有确认提示框的,通过reboot接口调用重启是没有的,为false。

重启的实现在run()中,因为ShutdownThread是Thread的扩展,所以run会自动运行。

  1. /** 
  2.  * Makes sure we handle the shutdown gracefully. 
  3.  * Shuts off power regardless of radio and bluetooth state if the alloted time has passed. 
  4.  */   
  5. public void run() {  
  6.     BroadcastReceiver br = new BroadcastReceiver() {  
  7.         @Override public void onReceive(Context context, Intent intent) {  
  8.             // We don't allow apps to cancel this, so ignore the result.  
  9.             actionDone();  
  10.         }  
  11.     };  
  12.   
  13.     /* 
  14.      * Write a system property in case the system_server reboots before we 
  15.      * get to the actual hardware restart. If that happens, we'll retry at 
  16.      * the beginning of the SystemServer startup. 
  17.      */   
  18.     {     
  19.         String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");  
  20.         SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);  
  21.     }  
  22.   
  23.     /* 
  24.      * If we are rebooting into safe mode, write a system property 
  25.      * indicating so. 
  26.      */   
  27.     if (mRebootSafeMode) {  
  28.         SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");  
  29.     }  
  30.     ...  
  31.     rebootOrShutdown(mReboot, mRebootReason);  
  32. }   

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

  1.     /** 
  2.      * Do not call this directly. Use {@link #reboot(Context, String, boolean)} 
  3.      * or {@link #shutdown(Context, boolean)} instead. 
  4.      * 
  5.      * @param reboot true to reboot or false to shutdown 
  6.      * @param reason reason for reboot 
  7.      */  
  8.     public static void rebootOrShutdown(boolean reboot, String reason) {  
  9.         if (reboot) {  
  10.             Log.i(TAG, "Rebooting, reason: " + reason);   
  11.             try {  
  12.                 PowerManagerService.lowLevelReboot(reason);  
  13.             } catch (Exception e) {  
  14.                 Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);  
  15.             }   
  16.         } else if (SHUTDOWN_VIBRATE_MS > 0) {  
  17.             // vibrate before shutting down  
  18.             Vibrator vibrator = new SystemVibrator();  
  19.             try {  
  20.                 vibrator.vibrate(SHUTDOWN_VIBRATE_MS);  
  21.             } catch (Exception e) {  
  22.                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.  
  23.                 Log.w(TAG, "Failed to vibrate during shutdown.", e);  
  24.             }     
  25.                   
  26.             // vibrator is asynchronous so we need to wait to avoid shutting down too soon.  
  27.             try {  
  28.                 Thread.sleep(SHUTDOWN_VIBRATE_MS);  
  29.             } catch (InterruptedException unused) {  
  30.             }     
  31.         }     
  32.               
  33.         // Shutdown power  
  34.         Log.i(TAG, "Performing low-level shutdown...");  
  35.         PowerManagerService.lowLevelShutdown();  
  36.     }  
  37. }  

如果确认重启,则调用PowerManagerService的lowLevelReboot函数,参数就是传递下来的reason,稍后分析。如果不是重启,即mReboot=false,那就是需要关机了,在shutdown函数中就能够知道。

  1. /** 
  2.  * Request a clean shutdown, waiting for subsystems to clean up their 
  3.  * state etc.  Must be called from a Looper thread in which its UI 
  4.  * is shown. 
  5.  * 
  6.  * @param context Context used to display the shutdown progress dialog. 
  7.  * @param confirm true if user confirmation is needed before shutting down. 
  8.  */  
  9. public static void shutdown(final Context context, boolean confirm) {  
  10.     mReboot = false;  
  11.     mRebootSafeMode = false;  
  12.     shutdownInner(context, confirm);  
  13. }  

关机的时候需要震动,就是这里了SHUTDOWN_VIBRATE_MS,默认的定义是500ms。但是在代码上看,无论如何,最后都会调用一下lowLevelShutdown函数,也就是关机。逻辑上,这里可能是个问题,但是实际中,如果重启操作能够调用成功的话,整个系统都重启了,后边的代码当然不可能执行到了。

目光转回PowerManagerService

4.frameworks/base/services/java/com/android/server/PowerManagerService.java

  1. /**   
  2.  * Low-level function to reboot the device. 
  3.  * 
  4.  * @param reason code to pass to the kernel (e.g. "recovery"), or null. 
  5.  * @throws IOException if reboot fails for some reason (eg, lack of 
  6.  *         permission) 
  7.  */  
  8. public static void lowLevelReboot(String reason) throws IOException {  
  9.     nativeReboot(reason);  
  10. }    
  11.   
  12. /**   
  13.  * Low-level function turn the device off immediately, without trying 
  14.  * to be clean.  Most people should use 
  15.  * {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown. 
  16.  */  
  17. public static void lowLevelShutdown() {  
  18.     nativeShutdown();  
  19. }   

很熟悉的字样native,是JNI调用了:

  1. private static native void nativeShutdown();  
  2. private static native void nativeReboot(String reason) throws IOException;  


5.frameworks/base/services/jni/com_android_server_PowerManagerService.cpp

  1. static JNINativeMethod gPowerManagerServiceMethods[] = {   
  2.     /* name, signature, funcPtr */  
  3.     ...  
  4.     { "nativeShutdown""()V",  
  5.             (void*) nativeShutdown },  
  6.     { "nativeReboot""(Ljava/lang/String;)V",  
  7.             (void*) nativeReboot },  
  8.     ...  
  9. };  

这两个好哥俩的实现也是在一起的:

  1. static void nativeShutdown(JNIEnv *env, jobject clazz) {  
  2.     android_reboot(ANDROID_RB_POWEROFF, 0, 0);  
  3. }  
  4.   
  5. static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {  
  6.     if (reason == NULL) {  
  7.         android_reboot(ANDROID_RB_RESTART, 0, 0);  
  8.     } else {  
  9.         const char *chars = env->GetStringUTFChars(reason, NULL);  
  10.         android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);  
  11.         env->ReleaseStringUTFChars(reason, chars);  // In case it fails.  
  12.     }  
  13.     jniThrowIOException(env, errno);  
  14. }  

可以看到无论是关机还是重启,都是调用android_reboot来实现的,只是参数不一样而已。


6.system/core/libcutils/android_reboot.c

  1. int android_reboot(int cmd, int flags, char *arg)  
  2. {  
  3.     int ret = 0;  
  4.     int reason = -1;  
  5.   
  6. #ifdef RECOVERY_PRE_COMMAND  
  7.     if (cmd == (int) ANDROID_RB_RESTART2) {  
  8.         if (arg && strlen(arg) > 0) {  
  9.             char cmd[PATH_MAX];  
  10.             sprintf(cmd, RECOVERY_PRE_COMMAND " %s", arg);  
  11.             system(cmd);  
  12.         }  
  13.     }  
  14. #endif  
  15.   
  16.     if (!(flags & ANDROID_RB_FLAG_NO_SYNC))  
  17.         sync();  
  18.   
  19.     if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))  
  20.         remount_ro();  
  21.   
  22.     switch (cmd) {  
  23.         case ANDROID_RB_RESTART:  
  24.             reason = RB_AUTOBOOT;  
  25.             break;  
  26.   
  27.         case ANDROID_RB_POWEROFF:  
  28.             ret = reboot(RB_POWER_OFF);  
  29.             return ret;  
  30.   
  31.         case ANDROID_RB_RESTART2:  
  32.             // REBOOT_MAGIC  
  33.             break;  
  34.   
  35.         default:  
  36.             return -1;  
  37.     }  
  38.   
  39. #ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON  
  40.     reason = RB_AUTOBOOT;  
  41. #endif  
  42.   
  43.     if (reason != -1)  
  44.         ret = reboot(reason);  
  45.     else  
  46.         ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,  
  47.                            LINUX_REBOOT_CMD_RESTART2, arg);  
  48.   
  49.     return ret;  
  50. }  

以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中:

  1. #define RB_AUTOBOOT     LINUX_REBOOT_CMD_RESTART  
  2. #define RB_HALT_SYSTEM  LINUX_REBOOT_CMD_HALT  
  3. #define RB_ENABLE_CAD   LINUX_REBOOT_CMD_CAD_ON  
  4. #define RB_DISABLE_CAD  LINUX_REBOOT_CMD_CAD_OFF  
  5. #define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF  

而,LINUX_REBOOT_XXXX之类的在bionic/libc/kernel/common/linux/reboot.h中:

  1. #define LINUX_REBOOT_MAGIC1 0xfee1dead  
  2. #define LINUX_REBOOT_MAGIC2 672274793  
  3. /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */  
  4. #define LINUX_REBOOT_MAGIC2A 85072278  
  5. #define LINUX_REBOOT_MAGIC2B 369367448  
  6. #define LINUX_REBOOT_MAGIC2C 537993216  
  7. #define LINUX_REBOOT_CMD_RESTART 0x01234567  
  8. /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */  
  9. #define LINUX_REBOOT_CMD_HALT 0xCDEF0123  
  10. #define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF  
  11. #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000  
  12. #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC  
  13. /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */  
  14. #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4  
  15. #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2  
  16. #define LINUX_REBOOT_CMD_KEXEC 0x45584543  
至于为什么他们是这样奇怪的值这个问题,我只能说他们是magic number,魔法嘛,本来就是正常人不能够理解的,所以~~~放过他们吧,只要知道他们没有是-1的就OK啦。

先来看reboot函数,按照往常的经验,reboot最终一定会调用到__reboot的。


7.bionic/libc/unistd/reboot.c

  1. int reboot (int  mode)   
  2. {  
  3.     return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );  
  4. }  

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)

  1. ENTRY(__reboot)  
  2.     .save   {r4, r7}   
  3.     stmfd   sp!, {r4, r7}  
  4.     ldr     r7, =__NR_reboot  
  5.     swi     #0    
  6.     ldmfd   sp!, {r4, r7}  
  7.     movs    r0, r0  
  8.     bxpl    lr    
  9.     b       __set_syscall_errno  
  10. END(__reboot)  

可以看出来,这里将__reboot的实现映射到了__NR_reboot, 而在bionic/libc/sys/linux-syscalls.h能够找到:

  1. #define __NR_reboot                       (__NR_SYSCALL_BASE + 88)  

其被指定了一个固定的偏移量,在被调用的时候就是通过这个偏移量去内核中寻找对应的入口的,由此可见,内核中一定有着相同的定义,否则将不能成功调用。内核中对syscall偏移量的定义在内核源码中的arch/arm/include/asm/unistd.h,相关信息完全一致。

已经找到了内核中的对应映射,那么下一步就要去找寻真正的实现函数了,在include/asm-generic/unistd.h中可以找到内核对__NR_reboot的syscall函数映射,即

  1. /* kernel/sys.c */  
  2. #define __NR_setpriority 140  
  3. __SYSCALL(__NR_setpriority, sys_setpriority)  
  4. #define __NR_getpriority 141  
  5. __SYSCALL(__NR_getpriority, sys_getpriority)  
  6. #define __NR_reboot 142  
  7. __SYSCALL(__NR_reboot, sys_reboot)  

同时,能够发现如此温馨的一幕,内核已经指引我们下一步该去哪里寻找sys_reboot,即kernel/sys.c。


9.kernel/sys.c

在进入这个文件前,我们先去include/linux/syscalls.h中查看一下sys_reboot的定义:

  1. asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd,  
  2.                 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文件中,能够找到这样几个定义:

  1. #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)  
  2. #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)  
  3. #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)  
  4. #define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)  
  5. #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)  
  6. #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)  
  7. ...  
  8.   
  9. #define SYSCALL_DEFINEx(x, sname, ...)              \  
  10.     __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)  
  11. ...  
  12.   
  13. #define __SYSCALL_DEFINEx(x, name, ...)                 \  
  14.     asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))  

整合后等价于:
  1. #define SYSCALL_DEFINE4(name, ...) \  
  2.     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的最终实现。函数实现如下:
  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.     char buffer[256];  
  13.     int ret = 0;  
  14.   
  15.     /* We only trust the superuser with rebooting the system. */  
  16.     if (!capable(CAP_SYS_BOOT))  
  17.         return -EPERM;  
  18.   
  19.     /* For safety, we require "magic" arguments. */  
  20.     if (magic1 != LINUX_REBOOT_MAGIC1 ||  
  21.         (magic2 != LINUX_REBOOT_MAGIC2 &&  
  22.                     magic2 != LINUX_REBOOT_MAGIC2A &&  
  23.             magic2 != LINUX_REBOOT_MAGIC2B &&  
  24.                     magic2 != LINUX_REBOOT_MAGIC2C))  
  25.         return -EINVAL;  
  26.   
  27.     /* Instead of trying to make the power_off code look like 
  28.      * halt when pm_power_off is not set do it the easy way. 
  29.      */  
  30.     if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)  
  31.         cmd = LINUX_REBOOT_CMD_HALT;  
  32.   
  33.     mutex_lock(&reboot_mutex);  
  34.     switch (cmd) {  
  35.     case LINUX_REBOOT_CMD_RESTART:  
  36.         kernel_restart(NULL);  
  37.         break;  
  38.   
  39.     case LINUX_REBOOT_CMD_CAD_ON:  
  40.         C_A_D = 1;  
  41.         break;  
  42.   
  43.     case LINUX_REBOOT_CMD_CAD_OFF:  
  44.         C_A_D = 0;  
  45.         break;  
  46.   
  47.     case LINUX_REBOOT_CMD_HALT:  
  48.         kernel_halt();  
  49.         do_exit(0);  
  50.         panic("cannot halt");  
  51.   
  52.     case LINUX_REBOOT_CMD_POWER_OFF:  
  53.         kernel_power_off();  
  54.         do_exit(0);  
  55.         break;  
  56.   
  57.     case LINUX_REBOOT_CMD_RESTART2:  
  58.         if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {  
  59.             ret = -EFAULT;  
  60.             break;  
  61.         }  
  62.         buffer[sizeof(buffer) - 1] = '\0';  
  63.   
  64.         kernel_restart(buffer);  
  65.         break;  
  66.   
  67. #ifdef CONFIG_KEXEC  
  68.     case LINUX_REBOOT_CMD_KEXEC:  
  69.         ret = kernel_kexec();  
  70.         break;  
  71. #endif  
  72.   
  73. #ifdef CONFIG_HIBERNATION  
  74.     case LINUX_REBOOT_CMD_SW_SUSPEND:  
  75.         ret = hibernate();  
  76.         break;  
  77. #endif  
  78.   
  79.     default:  
  80.         ret = -EINVAL;  
  81.         break;  
  82.     }  
  83.     mutex_unlock(&reboot_mutex);  
  84.     return ret;  
  85. }  

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

  1. /* We only trust the superuser with rebooting the system. */  
  2. if (!capable(CAP_SYS_BOOT))  
  3.     return -EPERM;  

否则将返回权限错误。对应的权限列表在include/linux/capability.h中,重启操作为22.

随后对magic number进行了校验:

  1. /* For safety, we require "magic" arguments. */  
  2. if (magic1 != LINUX_REBOOT_MAGIC1 ||  
  3.     (magic2 != LINUX_REBOOT_MAGIC2 &&  
  4.                 magic2 != LINUX_REBOOT_MAGIC2A &&  
  5.         magic2 != LINUX_REBOOT_MAGIC2B &&  
  6.                 magic2 != LINUX_REBOOT_MAGIC2C))  
  7.     return -EINVAL;  

如果数据传输过程中没有发生错误的话,这里也当然不会有问题,所以只是一个安全性校验,基本不会发生错误。

之后有一个很有趣的检查,如果用户要求关机,而pm_power_off为空的话,就把用户的关机命令转换为挂起:

  1. /* Instead of trying to make the power_off code look like 
  2.  * halt when pm_power_off is not set do it the easy way. 
  3.  */  
  4. if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)  
  5.     cmd = LINUX_REBOOT_CMD_HALT;  

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

  1. /* 
  2.  * Function pointers to optional machine specific functions 
  3.  */  
  4. void (*pm_power_off)(void);  
  5. EXPORT_SYMBOL(pm_power_off);  

好的,只是一个函数指针,而且做了全局操作,整个kernel都可以调用它。以高通msm7x30为例,在arch/arm/mach-msm/pm2.c中对这个函数指针进行了赋值:

  1. pm_power_off = msm_pm_power_off;  

msm_pm_power_off的具体实现就不再跟踪了,各家的都不一样,跟下去没有太大意义。现在只要知道,我分析的这个kernel是给了这个函数指针赋值的,所以不为空,关机命令将正常执行。

接下来就是这个函数的正题了,对用户命令进行解析操作,同时这个过程是用reboot_mutex互斥锁来进行保护的,以保证同一时间只可能有一个解析过程,避免冲突。

下边贴出所有关机重启相关的命令定义:

  1. /* 
  2.  * Commands accepted by the _reboot() system call. 
  3.  *       
  4.  * RESTART     Restart system using default command and mode. 
  5.  * HALT        Stop OS and give system control to ROM monitor, if any. 
  6.  * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command. 
  7.  * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task. 
  8.  * POWER_OFF   Stop OS and remove all power from system, if possible. 
  9.  * RESTART2    Restart system using given command string. 
  10.  * SW_SUSPEND  Suspend system using software suspend if compiled in. 
  11.  * KEXEC       Restart system using a previously loaded Linux kernel 
  12.  */       
  13.           
  14. #define LINUX_REBOOT_CMD_RESTART    0x01234567  
  15. #define LINUX_REBOOT_CMD_HALT       0xCDEF0123  
  16. #define LINUX_REBOOT_CMD_CAD_ON     0x89ABCDEF  
  17. #define LINUX_REBOOT_CMD_CAD_OFF    0x00000000   
  18. #define LINUX_REBOOT_CMD_POWER_OFF  0x4321FEDC  
  19. #define LINUX_REBOOT_CMD_RESTART2   0xA1B2C3D4  
  20. #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2  
  21. #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(),先看关机:

  1. /** 
  2.  *  kernel_power_off - power_off the system 
  3.  * 
  4.  *  Shutdown everything and perform a clean system power_off. 
  5.  */  
  6. void kernel_power_off(void)  
  7. {  
  8.     kernel_shutdown_prepare(SYSTEM_POWER_OFF);  
  9.     if (pm_power_off_prepare)  
  10.         pm_power_off_prepare();  
  11.     disable_nonboot_cpus();  
  12.     syscore_shutdown();  
  13.     printk(KERN_EMERG "Power down.\n");  
  14.     kmsg_dump(KMSG_DUMP_POWEROFF);  
  15.     machine_power_off();  
  16. }  
  17. EXPORT_SYMBOL_GPL(kernel_power_off);  

最了一系列准备工作,最终调用machine_power_off():

  1. void machine_power_off(void)  
  2. {     
  3.     machine_shutdown();  
  4.     if (pm_power_off)  
  5.         pm_power_off();  
  6. }  

之前找寻的pm_power_off在这里就有用处了,是关机的最后一步操作。关机完成,之后看下重启操作:

  1. /** 
  2.  *  kernel_restart - reboot the system 
  3.  *  @cmd: pointer to buffer containing command to execute for restart 
  4.  *      or %NULL 
  5.  * 
  6.  *  Shutdown everything and perform a clean reboot. 
  7.  *  This is not safe to call in interrupt context. 
  8.  */  
  9. void kernel_restart(char *cmd)  
  10. {  
  11.     kernel_restart_prepare(cmd);  
  12.     if (!cmd)  
  13.         printk(KERN_EMERG "Restarting system.\n");  
  14.     else  
  15.         printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);  
  16.     kmsg_dump(KMSG_DUMP_RESTART);  
  17.     machine_restart(cmd);  
  18. }  
  19. EXPORT_SYMBOL_GPL(kernel_restart);  

同样的套路,也是会进行一些准备工作,之后调用machine_restart(cmd), 如果是普通重启,那么中个cmd就为NULL,如果是特殊重启,那么这个cmd就是一层一层传递下来得那个arg了。

  1. void machine_restart(char *cmd)  
  2. {  
  3.     machine_shutdown();  
  4.     arm_pm_restart(reboot_mode, cmd);  
  5. }  
  6. ...  
  7. void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart;  
  8. EXPORT_SYMBOL_GPL(arm_pm_restart);  

而还记得刚才的pm2.c吗?在那里同样对arm_pm_restart进行了指针赋值:

  1. arm_pm_restart = msm_pm_restart;  

赋值的函数为msm_pm_init, 其调用为

  1. late_initcall_sync(msm_pm_init);  

late_initcall_sync的启动优先级是最低的,为7。module_init其实是6的优先级,数字越大优先级越低。所以,这样推断的话,最终arm_pm_restart这个函数指针会指向msm_pm_restart。关于msm_pm_restart的具体实现也不细看了,跟前边说的一样,都是各家不一样,就几行代码:
  1. static void msm_pm_restart(char str, const char *cmd)  
  2. {         
  3.     msm_rpcrouter_close();  
  4.     msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);  
  5.   
  6.     for (;;)  
  7.         ;  
  8. }   

但是细心的朋友可能会发现这里有一个restart_reason,这个并不是传递下来的参数。事实上,这个值已经在之前kernel_restart_prepare(cmd)的时候就已经设置好了。

  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.     syscore_shutdown();  
  8. }  

就是blocking_notifier机制,这个操作在之前的shutdown关机操作中也有,且是同一个list,都是reboot_notifier_list。也很容易理解,就是将注册在reboot_notifier_list上的函数传入相关参数后执行,作为了解,看一下具体是怎么使用的:(arch/arm/mach-msm/pm2.c)


  1. static int msm_reboot_call  
  2.     (struct notifier_block *this, unsigned long code, void *_cmd)  
  3. {     
  4.     if ((code == SYS_RESTART) && _cmd) {  
  5.         char *cmd = _cmd;  
  6.         if (!strcmp(cmd, "bootloader")) {  
  7.             restart_reason = 0x77665500;  
  8.         } else if (!strcmp(cmd, "recovery")) {  
  9.             restart_reason = 0x77665502;  
  10.         } else if (!strcmp(cmd, "eraseflash")) {  
  11.             restart_reason = 0x776655EF;  
  12.         } else if (!strncmp(cmd, "oem-", 4)) {  
  13.             unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;  
  14.             restart_reason = 0x6f656d00 | code;  
  15.         } else {  
  16.             restart_reason = 0x77665501;   
  17.         }     
  18.     }         
  19.     return NOTIFY_DONE;  
  20. }             
  21.           
  22. static struct notifier_block msm_reboot_notifier = {  
  23.     .notifier_call = msm_reboot_call,  
  24. };  
  25.   
  26. ...  
  27.   
  28. static int __init msm_pm_init(void)  
  29. {  
  30. ...  
  31.     register_reboot_notifier(&msm_reboot_notifier);  
  32. ...  
  33. }  

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
  1. private final void shutdownIfNoPower() {  
  2. // shut down gracefully if our battery is critically low and we are not powered.  
  3. // wait until the system has booted before attempting to display the shutdown dialog.  
  4. if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {  
  5.     Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);  
  6.     intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
  7.     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  8.     mContext.startActivity(intent);  
  9. }  

这样就可以了,不用多说了吧。

你可能感兴趣的:(设备驱动-----Android关机流程总结2)