Android系统关机的全流程解析
在PowerManager的API文档中,给出了一个关机/重启接口:
publicvoidreboot(Stringreason)
对于这个接口的描述很简单,就是几句话。
接口的作用就是重启设备,而且,就算重启成功了也没有返回值。
需要包含REBOOT权限,也就是android.permission.REBOOT
唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null。
一、上层空间
1.frameworks/base/core/java/android/os/PowerManager.java
/** *Rebootthedevice.Willnotreturniftherebootis *successful.Requiresthe{@linkandroid.Manifest.permission#REBOOT} *permission. * *@paramreasoncodetopasstothekernel(e.g.,"recovery")to *requestspecialbootmodes,ornull. */ publicvoidreboot(Stringreason) { try{ mService.reboot(reason); }catch(RemoteExceptione){ } }
mService为IPowerManagerBinder接口服务。
/** *{@hide} */ publicPowerManager(IPowerManagerservice,Handlerhandler) { mService=service; mHandler=handler; }
2.frameworks/base/core/java/android/os/IPowerManager.aidl
interfaceIPowerManager { ... voidreboot(Stringreason); ... }
3.frameworks/base/services/java/com/android/server/PowerManagerService.java
/** *Rebootthedeviceimmediately,passing'reason'(maybenull) *totheunderlying__rebootsystemcall.Shouldnotreturn. */ publicvoidreboot(Stringreason) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT,null); if(mHandler==null||!ActivityManagerNative.isSystemReady()){ thrownewIllegalStateException("Tooearlytocallreboot()"); } finalStringfinalReason=reason; Runnablerunnable=newRunnable(){ publicvoidrun(){ synchronized(this){ ShutdownThread.reboot(getUiContext(),finalReason,false); } } }; //ShutdownThreadmustrunonaloopercapableofdisplayingtheUI. mHandler.post(runnable); //PowerManager.reboot()isdocumentednottoreturnsojustwaitfortheinevitable. synchronized(runnable){ while(true){ try{ runnable.wait(); }catch(InterruptedExceptione){ } } } }
4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
/** *Requestacleanshutdown,waitingforsubsystemstocleanuptheir *stateetc.MustbecalledfromaLooperthreadinwhichitsUI *isshown. * *@paramcontextContextusedtodisplaytheshutdownprogressdialog. *@paramreasoncodetopasstothekernel(e.g."recovery"),ornull. *@paramconfirmtrueifuserconfirmationisneededbeforeshuttingdown. */ publicstaticvoidreboot(finalContextcontext,Stringreason,booleanconfirm){ mReboot=true; mRebootSafeMode=false; mRebootReason=reason; shutdownInner(context,confirm); }
这里说明是需要重启,且不是安全模式,重启参数为传递下来的reason,shutdownInner的confirm参数是用来设置是否有确认提示框的,通过reboot接口调用重启是没有的,为false。
重启的实现在run()中,因为ShutdownThread是Thread的扩展,所以run会自动运行。
/** *Makessurewehandletheshutdowngracefully. *Shutsoffpowerregardlessofradioandbluetoothstateiftheallotedtimehaspassed. */ publicvoidrun(){ BroadcastReceiverbr=newBroadcastReceiver(){ @OverridepublicvoidonReceive(Contextcontext,Intentintent){ //Wedon'tallowappstocancelthis,soignoretheresult. actionDone(); } }; /* *Writeasystempropertyincasethesystem_serverrebootsbeforewe *gettotheactualhardwarerestart.Ifthathappens,we'llretryat *thebeginningoftheSystemServerstartup. */ { Stringreason=(mReboot?"1":"0")+(mRebootReason!=null?mRebootReason:""); SystemProperties.set(SHUTDOWN_ACTION_PROPERTY,reason); } /* *Ifwearerebootingintosafemode,writeasystemproperty *indicatingso. */ if(mRebootSafeMode){ SystemProperties.set(REBOOT_SAFEMODE_PROPERTY,"1"); } ... rebootOrShutdown(mReboot,mRebootReason); }
在重启前会将重启原因写入sys.shutdown.requested,如果没有则为空,如果是安全模式还会将persist.sys.safemode置1,之后会进行一些关机前的预处理,关闭ActivityManager以及MountService,最终调用rebootOrShutdown进行关机操作。
/** *Donotcallthisdirectly.Use{@link#reboot(Context,String,boolean)} *or{@link#shutdown(Context,boolean)}instead. * *@paramreboottruetorebootorfalsetoshutdown *@paramreasonreasonforreboot */ publicstaticvoidrebootOrShutdown(booleanreboot,Stringreason){ if(reboot){ Log.i(TAG,"Rebooting,reason:"+reason); try{ PowerManagerService.lowLevelReboot(reason); }catch(Exceptione){ Log.e(TAG,"Rebootfailed,willattemptshutdowninstead",e); } }elseif(SHUTDOWN_VIBRATE_MS>0){ //vibratebeforeshuttingdown Vibratorvibrator=newSystemVibrator(); try{ vibrator.vibrate(SHUTDOWN_VIBRATE_MS); }catch(Exceptione){ //Failuretovibrateshouldn'tinterruptshutdown.Justlogit. Log.w(TAG,"Failedtovibrateduringshutdown.",e); } //vibratorisasynchronoussoweneedtowaittoavoidshuttingdowntoosoon. try{ Thread.sleep(SHUTDOWN_VIBRATE_MS); }catch(InterruptedExceptionunused){ } } //Shutdownpower Log.i(TAG,"Performinglow-levelshutdown..."); PowerManagerService.lowLevelShutdown(); } }
如果确认重启,则调用PowerManagerService的lowLevelReboot函数,参数就是传递下来的reason,稍后分析。如果不是重启,即mReboot=false,那就是需要关机了,在shutdown函数中就能够知道。
/** *Requestacleanshutdown,waitingforsubsystemstocleanuptheir *stateetc.MustbecalledfromaLooperthreadinwhichitsUI *isshown. * *@paramcontextContextusedtodisplaytheshutdownprogressdialog. *@paramconfirmtrueifuserconfirmationisneededbeforeshuttingdown. */ publicstaticvoidshutdown(finalContextcontext,booleanconfirm){ mReboot=false; mRebootSafeMode=false; shutdownInner(context,confirm); }
关机的时候需要震动,就是这里了SHUTDOWN_VIBRATE_MS,默认的定义是500ms。但是在代码上看,无论如何,最后都会调用一下lowLevelShutdown函数,也就是关机。逻辑上,这里可能是个问题,但是实际中,如果重启操作能够调用成功的话,整个系统都重启了,后边的代码当然不可能执行到了。
目光转回PowerManagerService
4.frameworks/base/services/java/com/android/server/PowerManagerService.java
/** *Low-levelfunctiontorebootthedevice. * *@paramreasoncodetopasstothekernel(e.g."recovery"),ornull. *@throwsIOExceptionifrebootfailsforsomereason(eg,lackof *permission) */ publicstaticvoidlowLevelReboot(Stringreason)throwsIOException{ nativeReboot(reason); } /** *Low-levelfunctionturnthedeviceoffimmediately,withouttrying *tobeclean.Mostpeopleshoulduse *{@linkcom.android.server.pm.internal.app.ShutdownThread}foracleanshutdown. */ publicstaticvoidlowLevelShutdown(){ nativeShutdown(); }
很熟悉的字样native,是JNI调用了:
privatestaticnativevoidnativeShutdown(); privatestaticnativevoidnativeReboot(Stringreason)throwsIOException;
5.frameworks/base/services/jni/com_android_server_PowerManagerService.cpp
staticJNINativeMethodgPowerManagerServiceMethods[]={ /*name,signature,funcPtr*/ ... {"nativeShutdown","()V", (void*)nativeShutdown}, {"nativeReboot","(Ljava/lang/String;)V", (void*)nativeReboot}, ... };
这两个好哥俩的实现也是在一起的:
staticvoidnativeShutdown(JNIEnv*env,jobjectclazz){ android_reboot(ANDROID_RB_POWEROFF,0,0); } staticvoidnativeReboot(JNIEnv*env,jobjectclazz,jstringreason){ if(reason==NULL){ android_reboot(ANDROID_RB_RESTART,0,0); }else{ constchar*chars=env->GetStringUTFChars(reason,NULL); android_reboot(ANDROID_RB_RESTART2,0,(char*)chars); env->ReleaseStringUTFChars(reason,chars);//Incaseitfails. } jniThrowIOException(env,errno); }
可以看到无论是关机还是重启,都是调用android_reboot来实现的,只是参数不一样而已。
6.system/core/libcutils/android_reboot.c
intandroid_reboot(intcmd,intflags,char*arg) { intret=0; intreason=-1; #ifdefRECOVERY_PRE_COMMAND if(cmd==(int)ANDROID_RB_RESTART2){ if(arg&&strlen(arg)>0){ charcmd[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){ caseANDROID_RB_RESTART: reason=RB_AUTOBOOT; break; caseANDROID_RB_POWEROFF: ret=reboot(RB_POWER_OFF); returnret; caseANDROID_RB_RESTART2: //REBOOT_MAGIC break; default: return-1; } #ifdefRECOVERY_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); returnret; }
以rebootrecovery为例,arg即为recovery,所在在第五步的时候会传入ANDROID_RB_RESTART2。到了android_reboot函数中,会看到这样的定义#ifdefRECOVERY_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
这里又出现一个#ifdefRECOVERY_PRE_COMMAND_CLEAR_REASON,如果定义了它,则无论上层传下来的参数是什么样的,最终都只是普通重启而已。定义它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON:=true,应该有厂商会喜欢这么做的,毕竟除了普通重启,都可能带给用户一定的风险。
最后会对reason进行一个检测,那么通过上边的分析,其实只有带参数的特殊重启才会为-1,而不等于-1的情况中有普通重启和关机,而关机已经自行解决了……所以,不等于-1的情况到了这里也只有普通重启了。最终这里就是区分普通重启与特殊重启的地方了。这里再插入一个问题,其他的几个cmd都是什么值呢?答案在bionic/libc/include/sys/reboot.h中:
#defineRB_AUTOBOOTLINUX_REBOOT_CMD_RESTART #defineRB_HALT_SYSTEMLINUX_REBOOT_CMD_HALT #defineRB_ENABLE_CADLINUX_REBOOT_CMD_CAD_ON #defineRB_DISABLE_CADLINUX_REBOOT_CMD_CAD_OFF #defineRB_POWER_OFFLINUX_REBOOT_CMD_POWER_OFF
而,LINUX_REBOOT_XXXX之类的在bionic/libc/kernel/common/linux/reboot.h中:
#defineLINUX_REBOOT_MAGIC10xfee1dead #defineLINUX_REBOOT_MAGIC2672274793 /*WARNING:DONOTEDIT,AUTO-GENERATEDCODE-SEETOPFORINSTRUCTIONS*/ #defineLINUX_REBOOT_MAGIC2A85072278 #defineLINUX_REBOOT_MAGIC2B369367448 #defineLINUX_REBOOT_MAGIC2C537993216 #defineLINUX_REBOOT_CMD_RESTART0x01234567 /*WARNING:DONOTEDIT,AUTO-GENERATEDCODE-SEETOPFORINSTRUCTIONS*/ #defineLINUX_REBOOT_CMD_HALT0xCDEF0123 #defineLINUX_REBOOT_CMD_CAD_ON0x89ABCDEF #defineLINUX_REBOOT_CMD_CAD_OFF0x00000000 #defineLINUX_REBOOT_CMD_POWER_OFF0x4321FEDC /*WARNING:DONOTEDIT,AUTO-GENERATEDCODE-SEETOPFORINSTRUCTIONS*/ #defineLINUX_REBOOT_CMD_RESTART20xA1B2C3D4 #defineLINUX_REBOOT_CMD_SW_SUSPEND0xD000FCE2 #defineLINUX_REBOOT_CMD_KEXEC0x45584543
至于为什么他们是这样奇怪的值这个问题,我只能说他们是magicnumber,魔法嘛,本来就是正常人不能够理解的,所以~~~放过他们吧,只要知道他们没有是-1的就OK啦。
先来看reboot函数,按照往常的经验,reboot最终一定会调用到__reboot的。
7.bionic/libc/unistd/reboot.c
intreboot(intmode) { 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} stmfdsp!,{r4,r7} ldrr7,=__NR_reboot swi#0 ldmfdsp!,{r4,r7} movsr0,r0 bxpllr 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函数映射,即
/*kernel/sys.c*/ #define__NR_setpriority140 __SYSCALL(__NR_setpriority,sys_setpriority) #define__NR_getpriority141 __SYSCALL(__NR_getpriority,sys_getpriority) #define__NR_reboot142 __SYSCALL(__NR_reboot,sys_reboot)
同时,能够发现如此温馨的一幕,内核已经指引我们下一步该去哪里寻找sys_reboot,即kernel/sys.c。
9.kernel/sys.c
在进入这个文件前,我们先去include/linux/syscalls.h中查看一下sys_reboot的定义:
asmlinkagelongsys_reboot(intmagic1,intmagic2,unsignedintcmd, void__user*arg);
与__reboot的调用参数一致。
进入sys.c文件后,并没有找到名为sys_reboot的函数,而通过仔细查找,发现一个很有趣的函数,其定义为SYSCALL_DEFINE4(reboot,int,magic1,int,magic2,unsignedint,cmd,void__user*,arg),对比__reboot的参数,能够符合。究竟是不是这个函数?
同样在include/linux/syscalls.h文件中,能够找到这样几个定义:
#defineSYSCALL_DEFINE1(name,...)SYSCALL_DEFINEx(1,_##name,__VA_ARGS__) #defineSYSCALL_DEFINE2(name,...)SYSCALL_DEFINEx(2,_##name,__VA_ARGS__) #defineSYSCALL_DEFINE3(name,...)SYSCALL_DEFINEx(3,_##name,__VA_ARGS__) #defineSYSCALL_DEFINE4(name,...)SYSCALL_DEFINEx(4,_##name,__VA_ARGS__) #defineSYSCALL_DEFINE5(name,...)SYSCALL_DEFINEx(5,_##name,__VA_ARGS__) #defineSYSCALL_DEFINE6(name,...)SYSCALL_DEFINEx(6,_##name,__VA_ARGS__) ... #defineSYSCALL_DEFINEx(x,sname,...)\ __SYSCALL_DEFINEx(x,sname,__VA_ARGS__) ... #define__SYSCALL_DEFINEx(x,name,...)\ asmlinkagelongsys##name(__SC_DECL##x(__VA_ARGS__))
整合后等价于:
#defineSYSCALL_DEFINE4(name,...)\ asmlinkagelongsys##_name(__SC_DECL##4(__VA_ARGS__))
这样就不难看出,SYSCALL_DEFINE4(reboot,int,magic1,int,magic2,unsignedint,cmd,void__user*,arg)就是sys_reboot,也就是上层调用的__reboot的最终实现。函数实现如下:
/* *Rebootsystemcall:forobviousreasonsonlyrootmaycallit, *andevenrootneedstosetupsomemagicnumbersintheregisters *sothatsomemistakewon'tmakethisrebootthewholemachine. *Youcanalsosetthemeaningofthectrl-alt-del-keyhere. * *rebootdoesn'tsync:dothatyourselfbeforecallingthis. */ SYSCALL_DEFINE4(reboot,int,magic1,int,magic2,unsignedint,cmd, void__user*,arg) { charbuffer[256]; intret=0; /*Weonlytrustthesuperuserwithrebootingthesystem.*/ if(!capable(CAP_SYS_BOOT)) return-EPERM; /*Forsafety,werequire"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; /*Insteadoftryingtomakethepower_offcodelooklike *haltwhenpm_power_offisnotsetdoittheeasyway. */ if((cmd==LINUX_REBOOT_CMD_POWER_OFF)&&!pm_power_off) cmd=LINUX_REBOOT_CMD_HALT; mutex_lock(&reboot_mutex); switch(cmd){ caseLINUX_REBOOT_CMD_RESTART: kernel_restart(NULL); break; caseLINUX_REBOOT_CMD_CAD_ON: C_A_D=1; break; caseLINUX_REBOOT_CMD_CAD_OFF: C_A_D=0; break; caseLINUX_REBOOT_CMD_HALT: kernel_halt(); do_exit(0); panic("cannothalt"); caseLINUX_REBOOT_CMD_POWER_OFF: kernel_power_off(); do_exit(0); break; caseLINUX_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; #ifdefCONFIG_KEXEC caseLINUX_REBOOT_CMD_KEXEC: ret=kernel_kexec(); break; #endif #ifdefCONFIG_HIBERNATION caseLINUX_REBOOT_CMD_SW_SUSPEND: ret=hibernate(); break; #endif default: ret=-EINVAL; break; } mutex_unlock(&reboot_mutex); returnret; }
在此函数中,首先会检测权限问题,只有超级用户才可以执行重启系统的操作:
/*Weonlytrustthesuperuserwithrebootingthesystem.*/ if(!capable(CAP_SYS_BOOT)) return-EPERM;
否则将返回权限错误。对应的权限列表在include/linux/capability.h中,重启操作为22.
随后对magicnumber进行了校验:
/*Forsafety,werequire"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;
如果数据传输过程中没有发生错误的话,这里也当然不会有问题,所以只是一个安全性校验,基本不会发生错误。
之后有一个很有趣的检查,如果用户要求关机,而pm_power_off为空的话,就把用户的关机命令转换为挂起:
/*Insteadoftryingtomakethepower_offcodelooklike *haltwhenpm_power_offisnotsetdoittheeasyway. */ if((cmd==LINUX_REBOOT_CMD_POWER_OFF)&&!pm_power_off) cmd=LINUX_REBOOT_CMD_HALT;
在arch/arm/kernel/process.c中可以找到它的定义:
/* *Functionpointerstooptionalmachinespecificfunctions */ 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互斥锁来进行保护的,以保证同一时间只可能有一个解析过程,避免冲突。
下边贴出所有关机重启相关的命令定义:
/* *Commandsacceptedbythe_reboot()systemcall. * *RESTARTRestartsystemusingdefaultcommandandmode. *HALTStopOSandgivesystemcontroltoROMmonitor,ifany. *CAD_ONCtrl-Alt-DelsequencecausesRESTARTcommand. *CAD_OFFCtrl-Alt-DelsequencesendsSIGINTtoinittask. *POWER_OFFStopOSandremoveallpowerfromsystem,ifpossible. *RESTART2Restartsystemusinggivencommandstring. *SW_SUSPENDSuspendsystemusingsoftwaresuspendifcompiledin. *KEXECRestartsystemusingapreviouslyloadedLinuxkernel */ #defineLINUX_REBOOT_CMD_RESTART0x01234567 #defineLINUX_REBOOT_CMD_HALT0xCDEF0123 #defineLINUX_REBOOT_CMD_CAD_ON0x89ABCDEF #defineLINUX_REBOOT_CMD_CAD_OFF0x00000000 #defineLINUX_REBOOT_CMD_POWER_OFF0x4321FEDC #defineLINUX_REBOOT_CMD_RESTART20xA1B2C3D4 #defineLINUX_REBOOT_CMD_SW_SUSPEND0xD000FCE2 #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(),先看关机:
/** *kernel_power_off-power_offthesystem * *Shutdowneverythingandperformacleansystempower_off. */ voidkernel_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"Powerdown.\n"); kmsg_dump(KMSG_DUMP_POWEROFF); machine_power_off(); } EXPORT_SYMBOL_GPL(kernel_power_off);
最了一系列准备工作,最终调用machine_power_off():
voidmachine_power_off(void) { machine_shutdown(); if(pm_power_off) pm_power_off(); }
之前找寻的pm_power_off在这里就有用处了,是关机的最后一步操作。关机完成,之后看下重启操作:
/** *kernel_restart-rebootthesystem *@cmd:pointertobuffercontainingcommandtoexecuteforrestart *or%NULL * *Shutdowneverythingandperformacleanreboot. *Thisisnotsafetocallininterruptcontext. */ voidkernel_restart(char*cmd) { kernel_restart_prepare(cmd); if(!cmd) printk(KERN_EMERG"Restartingsystem.\n"); else printk(KERN_EMERG"Restartingsystemwithcommand'%s'.\n",cmd); kmsg_dump(KMSG_DUMP_RESTART); machine_restart(cmd); } EXPORT_SYMBOL_GPL(kernel_restart);
同样的套路,也是会进行一些准备工作,之后调用machine_restart(cmd),如果是普通重启,那么中个cmd就为NULL,如果是特殊重启,那么这个cmd就是一层一层传递下来得那个arg了。
voidmachine_restart(char*cmd) { machine_shutdown(); arm_pm_restart(reboot_mode,cmd); } ... void(*arm_pm_restart)(charstr,constchar*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的具体实现也不细看了,跟前边说的一样,都是各家不一样,就几行代码:
staticvoidmsm_pm_restart(charstr,constchar*cmd) { msm_rpcrouter_close(); msm_proc_comm(PCOM_RESET_CHIP,&restart_reason,0); for(;;) ; }
但是细心的朋友可能会发现这里有一个restart_reason,这个并不是传递下来的参数。事实上,这个值已经在之前kernel_restart_prepare(cmd)的时候就已经设置好了。
voidkernel_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)
staticintmsm_reboot_call (structnotifier_block*this,unsignedlongcode,void*_cmd) { if((code==SYS_RESTART)&&_cmd){ char*cmd=_cmd; if(!strcmp(cmd,"bootloader")){ restart_reason=0x77665500; }elseif(!strcmp(cmd,"recovery")){ restart_reason=0x77665502; }elseif(!strcmp(cmd,"eraseflash")){ restart_reason=0x776655EF; }elseif(!strncmp(cmd,"oem-",4)){ unsignedcode=simple_strtoul(cmd+4,0,16)&0xff; restart_reason=0x6f656d00|code; }else{ restart_reason=0x77665501; } } returnNOTIFY_DONE; } staticstructnotifier_blockmsm_reboot_notifier={ .notifier_call=msm_reboot_call, }; ... staticint__initmsm_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
privatefinalvoidshutdownIfNoPower(){ //shutdowngracefullyifourbatteryiscriticallylowandwearenotpowered. //waituntilthesystemhasbootedbeforeattemptingtodisplaytheshutdowndialog. if(mBatteryLevel==0&&!isPowered()&&ActivityManagerNative.isSystemReady()){ Intentintent=newIntent(Intent.ACTION_REQUEST_SHUTDOWN); intent.putExtra(Intent.EXTRA_KEY_CONFIRM,false); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); }