分类:Android Source 1443人阅读 评论(2) 收藏 举报

目录(?)[+]

资料:

https://developer.android.com/intl/zh-CN/reference/android/os/PowerManager.html


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

public voidreboot(Stringreason)

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

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

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

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

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

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

  1. /**
  2. *Rebootthedevice.Willnotreturniftherebootis
  3. *successful.Requiresthe{@linkandroid.Manifest.permission#REBOOT}
  4. *permission.
  5. *
  6. *@paramreasoncodetopasstothekernel(e.g.,"recovery")to
  7. *requestspecialbootmodes,ornull.
  8. */
  9. publicvoidreboot(Stringreason)
  10. {
  11. try{
  12. mService.reboot(reason);
  13. }catch(RemoteExceptione){
  14. }
  15. }

mService为IPowerManager Binder接口服务。

  1. /**
  2. *{@hide}
  3. */
  4. publicPowerManager(IPowerManagerservice,Handlerhandler)
  5. {
  6. mService=service;
  7. mHandler=handler;
  8. }


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

  1. interfaceIPowerManager
  2. {
  3. ...
  4. voidreboot(Stringreason);
  5. ...
  6. }

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

  1. /**
  2. *Rebootthedeviceimmediately,passing'reason'(maybenull)
  3. *totheunderlying__rebootsystemcall.Shouldnotreturn.
  4. */
  5. publicvoidreboot(Stringreason)
  6. {
  7. mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT,null);
  8. if(mHandler==null||!ActivityManagerNative.isSystemReady()){
  9. thrownewIllegalStateException("Tooearlytocallreboot()");
  10. }
  11. finalStringfinalReason=reason;
  12. Runnablerunnable=newRunnable(){
  13. publicvoidrun(){
  14. synchronized(this){
  15. ShutdownThread.reboot(getUiContext(),finalReason,false);
  16. }
  17. }
  18. };
  19. //ShutdownThreadmustrunonaloopercapableofdisplayingtheUI.
  20. mHandler.post(runnable);
  21. //PowerManager.reboot()isdocumentednottoreturnsojustwaitfortheinevitable.
  22. synchronized(runnable){
  23. while(true){
  24. try{
  25. runnable.wait();
  26. }catch(InterruptedExceptione){
  27. }
  28. }
  29. }
  30. }

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

  1. /**
  2. *Requestacleanshutdown,waitingforsubsystemstocleanuptheir
  3. *stateetc.MustbecalledfromaLooperthreadinwhichitsUI
  4. *isshown.
  5. *
  6. *@paramcontextContextusedtodisplaytheshutdownprogressdialog.
  7. *@paramreasoncodetopasstothekernel(e.g."recovery"),ornull.
  8. *@paramconfirmtrueifuserconfirmationisneededbeforeshuttingdown.
  9. */
  10. publicstaticvoidreboot(finalContextcontext,Stringreason,booleanconfirm){
  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. *Makessurewehandletheshutdowngracefully.
  3. *Shutsoffpowerregardlessofradioandbluetoothstateiftheallotedtimehaspassed.
  4. */
  5. publicvoidrun(){
  6. BroadcastReceiverbr=newBroadcastReceiver(){
  7. @OverridepublicvoidonReceive(Contextcontext,Intentintent){
  8. //Wedon'tallowappstocancelthis,soignoretheresult.
  9. actionDone();
  10. }
  11. };
  12. /*
  13. *Writeasystempropertyincasethesystem_serverrebootsbeforewe
  14. *gettotheactualhardwarerestart.Ifthathappens,we'llretryat
  15. *thebeginningoftheSystemServerstartup.
  16. */
  17. {
  18. Stringreason=(mReboot?"1":"0")+(mRebootReason!=null?mRebootReason:"");
  19. SystemProperties.set(SHUTDOWN_ACTION_PROPERTY,reason);
  20. }
  21. /*
  22. *Ifwearerebootingintosafemode,writeasystemproperty
  23. *indicatingso.
  24. */
  25. if(mRebootSafeMode){
  26. SystemProperties.set(REBOOT_SAFEMODE_PROPERTY,"1");
  27. }
  28. ...
  29. rebootOrShutdown(mReboot,mRebootReason);
  30. }

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

  1. /**
  2. *Donotcallthisdirectly.Use{@link#reboot(Context,String,boolean)}
  3. *or{@link#shutdown(Context,boolean)}instead.
  4. *
  5. *@paramreboottruetorebootorfalsetoshutdown
  6. *@paramreasonreasonforreboot
  7. */
  8. publicstaticvoidrebootOrShutdown(booleanreboot,Stringreason){
  9. if(reboot){
  10. Log.i(TAG,"Rebooting,reason:"+reason);
  11. try{
  12. PowerManagerService.lowLevelReboot(reason);
  13. }catch(Exceptione){
  14. Log.e(TAG,"Rebootfailed,willattemptshutdowninstead",e);
  15. }
  16. }elseif(SHUTDOWN_VIBRATE_MS>0){
  17. //vibratebeforeshuttingdown
  18. Vibratorvibrator=newSystemVibrator();
  19. try{
  20. vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
  21. }catch(Exceptione){
  22. //Failuretovibrateshouldn'tinterruptshutdown.Justlogit.
  23. Log.w(TAG,"Failedtovibrateduringshutdown.",e);
  24. }
  25. //vibratorisasynchronoussoweneedtowaittoavoidshuttingdowntoosoon.
  26. try{
  27. Thread.sleep(SHUTDOWN_VIBRATE_MS);
  28. }catch(InterruptedExceptionunused){
  29. }
  30. }
  31. //Shutdownpower
  32. Log.i(TAG,"Performinglow-levelshutdown...");
  33. PowerManagerService.lowLevelShutdown();
  34. }
  35. }

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

  1. /**
  2. *Requestacleanshutdown,waitingforsubsystemstocleanuptheir
  3. *stateetc.MustbecalledfromaLooperthreadinwhichitsUI
  4. *isshown.
  5. *
  6. *@paramcontextContextusedtodisplaytheshutdownprogressdialog.
  7. *@paramconfirmtrueifuserconfirmationisneededbeforeshuttingdown.
  8. */
  9. publicstaticvoidshutdown(finalContextcontext,booleanconfirm){
  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-levelfunctiontorebootthedevice.
  3. *
  4. *@paramreasoncodetopasstothekernel(e.g."recovery"),ornull.
  5. *@throwsIOExceptionifrebootfailsforsomereason(eg,lackof
  6. *permission)
  7. */
  8. publicstaticvoidlowLevelReboot(Stringreason)throwsIOException{
  9. nativeReboot(reason);
  10. }
  11. /**
  12. *Low-levelfunctionturnthedeviceoffimmediately,withouttrying
  13. *tobeclean.Mostpeopleshoulduse
  14. *{@linkcom.android.server.pm.internal.app.ShutdownThread}foracleanshutdown.
  15. */
  16. publicstaticvoidlowLevelShutdown(){
  17. nativeShutdown();
  18. }

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

  1. privatestaticnativevoidnativeShutdown();
  2. privatestaticnativevoidnativeReboot(Stringreason)throwsIOException;


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

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

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

  1. staticvoidnativeShutdown(JNIEnv*env,jobjectclazz){
  2. android_reboot(ANDROID_RB_POWEROFF,0,0);
  3. }
  4. staticvoidnativeReboot(JNIEnv*env,jobjectclazz,jstringreason){
  5. if(reason==NULL){
  6. android_reboot(ANDROID_RB_RESTART,0,0);
  7. }else{
  8. constchar*chars=env->GetStringUTFChars(reason,NULL);
  9. android_reboot(ANDROID_RB_RESTART2,0,(char*)chars);
  10. env->ReleaseStringUTFChars(reason,chars);//Incaseitfails.
  11. }
  12. jniThrowIOException(env,errno);
  13. }

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


6.system/core/libcutils/android_reboot.c

  1. intandroid_reboot(intcmd,intflags,char*arg)
  2. {
  3. intret=0;
  4. intreason=-1;
  5. #ifdefRECOVERY_PRE_COMMAND
  6. if(cmd==(int)ANDROID_RB_RESTART2){
  7. if(arg&&strlen(arg)>0){
  8. charcmd[PATH_MAX];
  9. sprintf(cmd,RECOVERY_PRE_COMMAND"%s",arg);
  10. system(cmd);
  11. }
  12. }
  13. #endif
  14. if(!(flags&ANDROID_RB_FLAG_NO_SYNC))
  15. sync();
  16. if(!(flags&ANDROID_RB_FLAG_NO_REMOUNT_RO))
  17. remount_ro();
  18. switch(cmd){
  19. caseANDROID_RB_RESTART:
  20. reason=RB_AUTOBOOT;
  21. break;
  22. caseANDROID_RB_POWEROFF:
  23. ret=reboot(RB_POWER_OFF);
  24. returnret;
  25. caseANDROID_RB_RESTART2:
  26. //REBOOT_MAGIC
  27. break;
  28. default:
  29. return-1;
  30. }
  31. #ifdefRECOVERY_PRE_COMMAND_CLEAR_REASON
  32. reason=RB_AUTOBOOT;
  33. #endif
  34. if(reason!=-1)
  35. ret=reboot(reason);
  36. else
  37. ret=__reboot(LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,
  38. LINUX_REBOOT_CMD_RESTART2,arg);
  39. returnret;
  40. }

以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. #defineRB_AUTOBOOTLINUX_REBOOT_CMD_RESTART
  2. #defineRB_HALT_SYSTEMLINUX_REBOOT_CMD_HALT
  3. #defineRB_ENABLE_CADLINUX_REBOOT_CMD_CAD_ON
  4. #defineRB_DISABLE_CADLINUX_REBOOT_CMD_CAD_OFF
  5. #defineRB_POWER_OFFLINUX_REBOOT_CMD_POWER_OFF

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

  1. #defineLINUX_REBOOT_MAGIC10xfee1dead
  2. #defineLINUX_REBOOT_MAGIC2672274793
  3. /*WARNING:DONOTEDIT,AUTO-GENERATEDCODE-SEETOPFORINSTRUCTIONS*/
  4. #defineLINUX_REBOOT_MAGIC2A85072278
  5. #defineLINUX_REBOOT_MAGIC2B369367448
  6. #defineLINUX_REBOOT_MAGIC2C537993216
  7. #defineLINUX_REBOOT_CMD_RESTART0x01234567
  8. /*WARNING:DONOTEDIT,AUTO-GENERATEDCODE-SEETOPFORINSTRUCTIONS*/
  9. #defineLINUX_REBOOT_CMD_HALT0xCDEF0123
  10. #defineLINUX_REBOOT_CMD_CAD_ON0x89ABCDEF
  11. #defineLINUX_REBOOT_CMD_CAD_OFF0x00000000
  12. #defineLINUX_REBOOT_CMD_POWER_OFF0x4321FEDC
  13. /*WARNING:DONOTEDIT,AUTO-GENERATEDCODE-SEETOPFORINSTRUCTIONS*/
  14. #defineLINUX_REBOOT_CMD_RESTART20xA1B2C3D4
  15. #defineLINUX_REBOOT_CMD_SW_SUSPEND0xD000FCE2
  16. #defineLINUX_REBOOT_CMD_KEXEC0x45584543
至于为什么他们是这样奇怪的值这个问题,我只能说他们是magic number,魔法嘛,本来就是正常人不能够理解的,所以~~~放过他们吧,只要知道他们没有是-1的就OK啦。

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


7.bionic/libc/unistd/reboot.c

  1. intreboot(intmode)
  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. stmfdsp!,{r4,r7}
  4. ldrr7,=__NR_reboot
  5. swi#0
  6. ldmfdsp!,{r4,r7}
  7. movsr0,r0
  8. bxpllr
  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_setpriority140
  3. __SYSCALL(__NR_setpriority,sys_setpriority)
  4. #define__NR_getpriority141
  5. __SYSCALL(__NR_getpriority,sys_getpriority)
  6. #define__NR_reboot142
  7. __SYSCALL(__NR_reboot,sys_reboot)

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


9.kernel/sys.c

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

  1. asmlinkagelongsys_reboot(intmagic1,intmagic2,unsignedintcmd,
  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. #defineSYSCALL_DEFINE1(name,...)SYSCALL_DEFINEx(1,_##name,__VA_ARGS__)
  2. #defineSYSCALL_DEFINE2(name,...)SYSCALL_DEFINEx(2,_##name,__VA_ARGS__)
  3. #defineSYSCALL_DEFINE3(name,...)SYSCALL_DEFINEx(3,_##name,__VA_ARGS__)
  4. #defineSYSCALL_DEFINE4(name,...)SYSCALL_DEFINEx(4,_##name,__VA_ARGS__)
  5. #defineSYSCALL_DEFINE5(name,...)SYSCALL_DEFINEx(5,_##name,__VA_ARGS__)
  6. #defineSYSCALL_DEFINE6(name,...)SYSCALL_DEFINEx(6,_##name,__VA_ARGS__)
  7. ...
  8. #defineSYSCALL_DEFINEx(x,sname,...)\
  9. __SYSCALL_DEFINEx(x,sname,__VA_ARGS__)
  10. ...
  11. #define__SYSCALL_DEFINEx(x,name,...)\
  12. asmlinkagelongsys##name(__SC_DECL##x(__VA_ARGS__))

整合后等价于:
  1. #defineSYSCALL_DEFINE4(name,...)\
  2. asmlinkagelongsys##_name(__SC_DECL##4(__VA_ARGS__))

这样就不难看出,SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,void __user *, arg)就是sys_reboot,也就是上层调用的__reboot的最终实现。函数实现如下:
  1. /*
  2. *Rebootsystemcall:forobviousreasonsonlyrootmaycallit,
  3. *andevenrootneedstosetupsomemagicnumbersintheregisters
  4. *sothatsomemistakewon'tmakethisrebootthewholemachine.
  5. *Youcanalsosetthemeaningofthectrl-alt-del-keyhere.
  6. *
  7. *rebootdoesn'tsync:dothatyourselfbeforecallingthis.
  8. */
  9. SYSCALL_DEFINE4(reboot,int,magic1,int,magic2,unsignedint,cmd,
  10. void__user*,arg)
  11. {
  12. charbuffer[256];
  13. intret=0;
  14. /*Weonlytrustthesuperuserwithrebootingthesystem.*/
  15. if(!capable(CAP_SYS_BOOT))
  16. return-EPERM;
  17. /*Forsafety,werequire"magic"arguments.*/
  18. if(magic1!=LINUX_REBOOT_MAGIC1||
  19. (magic2!=LINUX_REBOOT_MAGIC2&&
  20. magic2!=LINUX_REBOOT_MAGIC2A&&
  21. magic2!=LINUX_REBOOT_MAGIC2B&&
  22. magic2!=LINUX_REBOOT_MAGIC2C))
  23. return-EINVAL;
  24. /*Insteadoftryingtomakethepower_offcodelooklike
  25. *haltwhenpm_power_offisnotsetdoittheeasyway.
  26. */
  27. if((cmd==LINUX_REBOOT_CMD_POWER_OFF)&&!pm_power_off)
  28. cmd=LINUX_REBOOT_CMD_HALT;
  29. mutex_lock(&reboot_mutex);
  30. switch(cmd){
  31. caseLINUX_REBOOT_CMD_RESTART:
  32. kernel_restart(NULL);
  33. break;
  34. caseLINUX_REBOOT_CMD_CAD_ON:
  35. C_A_D=1;
  36. break;
  37. caseLINUX_REBOOT_CMD_CAD_OFF:
  38. C_A_D=0;
  39. break;
  40. caseLINUX_REBOOT_CMD_HALT:
  41. kernel_halt();
  42. do_exit(0);
  43. panic("cannothalt");
  44. caseLINUX_REBOOT_CMD_POWER_OFF:
  45. kernel_power_off();
  46. do_exit(0);
  47. break;
  48. caseLINUX_REBOOT_CMD_RESTART2:
  49. if(strncpy_from_user(&buffer[0],arg,sizeof(buffer)-1)<0){
  50. ret=-EFAULT;
  51. break;
  52. }
  53. buffer[sizeof(buffer)-1]='\0';
  54. kernel_restart(buffer);
  55. break;
  56. #ifdefCONFIG_KEXEC
  57. caseLINUX_REBOOT_CMD_KEXEC:
  58. ret=kernel_kexec();
  59. break;
  60. #endif
  61. #ifdefCONFIG_HIBERNATION
  62. caseLINUX_REBOOT_CMD_SW_SUSPEND:
  63. ret=hibernate();
  64. break;
  65. #endif
  66. default:
  67. ret=-EINVAL;
  68. break;
  69. }
  70. mutex_unlock(&reboot_mutex);
  71. returnret;
  72. }

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

  1. /*Weonlytrustthesuperuserwithrebootingthesystem.*/
  2. if(!capable(CAP_SYS_BOOT))
  3. return-EPERM;

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

随后对magic number进行了校验:

  1. /*Forsafety,werequire"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. /*Insteadoftryingtomakethepower_offcodelooklike
  2. *haltwhenpm_power_offisnotsetdoittheeasyway.
  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. *Functionpointerstooptionalmachinespecificfunctions
  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. *Commandsacceptedbythe_reboot()systemcall.
  3. *
  4. *RESTARTRestartsystemusingdefaultcommandandmode.
  5. *HALTStopOSandgivesystemcontroltoROMmonitor,ifany.
  6. *CAD_ONCtrl-Alt-DelsequencecausesRESTARTcommand.
  7. *CAD_OFFCtrl-Alt-DelsequencesendsSIGINTtoinittask.
  8. *POWER_OFFStopOSandremoveallpowerfromsystem,ifpossible.
  9. *RESTART2Restartsystemusinggivencommandstring.
  10. *SW_SUSPENDSuspendsystemusingsoftwaresuspendifcompiledin.
  11. *KEXECRestartsystemusingapreviouslyloadedLinuxkernel
  12. */
  13. #defineLINUX_REBOOT_CMD_RESTART0x01234567
  14. #defineLINUX_REBOOT_CMD_HALT0xCDEF0123
  15. #defineLINUX_REBOOT_CMD_CAD_ON0x89ABCDEF
  16. #defineLINUX_REBOOT_CMD_CAD_OFF0x00000000
  17. #defineLINUX_REBOOT_CMD_POWER_OFF0x4321FEDC
  18. #defineLINUX_REBOOT_CMD_RESTART20xA1B2C3D4
  19. #defineLINUX_REBOOT_CMD_SW_SUSPEND0xD000FCE2
  20. #defineLINUX_REBOOT_CMD_KEXEC0x45584543

注释中的说明很详细了,比较陌生的就是关于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_offthesystem
  3. *
  4. *Shutdowneverythingandperformacleansystempower_off.
  5. */
  6. voidkernel_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"Powerdown.\n");
  14. kmsg_dump(KMSG_DUMP_POWEROFF);
  15. machine_power_off();
  16. }
  17. EXPORT_SYMBOL_GPL(kernel_power_off);

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

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

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

  1. /**
  2. *kernel_restart-rebootthesystem
  3. *@cmd:pointertobuffercontainingcommandtoexecuteforrestart
  4. *or%NULL
  5. *
  6. *Shutdowneverythingandperformacleanreboot.
  7. *Thisisnotsafetocallininterruptcontext.
  8. */
  9. voidkernel_restart(char*cmd)
  10. {
  11. kernel_restart_prepare(cmd);
  12. if(!cmd)
  13. printk(KERN_EMERG"Restartingsystem.\n");
  14. else
  15. printk(KERN_EMERG"Restartingsystemwithcommand'%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. voidmachine_restart(char*cmd)
  2. {
  3. machine_shutdown();
  4. arm_pm_restart(reboot_mode,cmd);
  5. }
  6. ...
  7. void(*arm_pm_restart)(charstr,constchar*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. staticvoidmsm_pm_restart(charstr,constchar*cmd)
  2. {
  3. msm_rpcrouter_close();
  4. msm_proc_comm(PCOM_RESET_CHIP,&restart_reason,0);
  5. for(;;)
  6. ;
  7. }

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

  1. voidkernel_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. staticintmsm_reboot_call
  2. (structnotifier_block*this,unsignedlongcode,void*_cmd)
  3. {
  4. if((code==SYS_RESTART)&&_cmd){
  5. char*cmd=_cmd;
  6. if(!strcmp(cmd,"bootloader")){
  7. restart_reason=0x77665500;
  8. }elseif(!strcmp(cmd,"recovery")){
  9. restart_reason=0x77665502;
  10. }elseif(!strcmp(cmd,"eraseflash")){
  11. restart_reason=0x776655EF;
  12. }elseif(!strncmp(cmd,"oem-",4)){
  13. unsignedcode=simple_strtoul(cmd+4,0,16)&0xff;
  14. restart_reason=0x6f656d00|code;
  15. }else{
  16. restart_reason=0x77665501;
  17. }
  18. }
  19. returnNOTIFY_DONE;
  20. }
  21. staticstructnotifier_blockmsm_reboot_notifier={
  22. .notifier_call=msm_reboot_call,
  23. };
  24. ...
  25. staticint__initmsm_pm_init(void)
  26. {
  27. ...
  28. register_reboot_notifier(&msm_reboot_notifier);
  29. ...
  30. }

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. privatefinalvoidshutdownIfNoPower(){
  2. //shutdowngracefullyifourbatteryiscriticallylowandwearenotpowered.
  3. //waituntilthesystemhasbootedbeforeattemptingtodisplaytheshutdowndialog.
  4. if(mBatteryLevel==0&&!isPowered()&&ActivityManagerNative.isSystemReady()){
  5. Intentintent=newIntent(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. }

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

更多相关文章

  1. Android(安卓)  Intent 的几种启动活动的方式
  2. Lua学习 2) —— Android与Lua互调
  3. android api 中文 (75)—— AdapterView.OnItemClickListener
  4. Android四大基本组件介绍与生命周期
  5. Android消息机制不完全解析(上)
  6. Android灯光系统(硬件访问服务框架)
  7. android 横纵屏切换
  8. Android基础 --- Widget
  9. 箭头函数的基础使用

随机推荐

  1. 【android studio】安卓中修改每个每个活
  2. android中单位的简单介绍
  3. 在android中,编译的项目使用到第三方jar的
  4. 【Android】_干货_制作透明背景icon
  5. android 模拟器调用系统照相机
  6. 三小时完成玩Android/看妹子客户端(Retrof
  7. Android(安卓)源码通过makefile配置文件
  8. 指尖上的Android之实战篇--说明篇(二)
  9. Android变身iOS?联想5S可别吹了
  10. android TextView 显示不全的问题解决,此