Android系統(tǒng)關(guān)機(jī)的全流程解析
在PowerManager的API文檔中,給出了一個(gè)關(guān)機(jī)/重啟接口:
public void reboot (String reason)
對(duì)于這個(gè)接口的描述很簡(jiǎn)單,就是幾句話。
接口的作用就是重啟設(shè)備,而且,就算重啟成功了也沒(méi)有返回值。
需要包含REBOOT權(quán)限,也就是android.permission.REBOOT
唯一參數(shù)reason代表需要的特定重啟模式,比如recovery,當(dāng)然也可以為null。
一、上層空間
1.frameworks/base/core/java/android/os/PowerManager.java
/**
* Reboot the device. Will not return if the reboot is
* successful. Requires the {@link android.Manifest.permission#REBOOT}
* permission.
*
* @param reason code to pass to the kernel (e.g., "recovery") to
* request special boot modes, or null.
*/
public void reboot(String reason)
{
try {
mService.reboot(reason);
} catch (RemoteException e) {
}
}
mService為IPowerManager Binder接口服務(wù)。
/**
* {@hide}
*/
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
/**
* Reboot the device immediately, passing 'reason' (may be null)
* to the underlying __reboot system call. Should not return.
*/
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
/**
* Request a clean shutdown, waiting for subsystems to clean up their
* state etc. Must be called from a Looper thread in which its UI
* is shown.
*
* @param context Context used to display the shutdown progress dialog.
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
mRebootSafeMode = false;
mRebootReason = reason;
shutdownInner(context, confirm);
}
這里說(shuō)明是需要重啟,且不是安全模式,重啟參數(shù)為傳遞下來(lái)的reason,shutdownInner的confirm參數(shù)是用來(lái)設(shè)置是否有確認(rèn)提示框的,通過(guò)reboot接口調(diào)用重啟是沒(méi)有的,為false。
重啟的實(shí)現(xiàn)在run()中,因?yàn)镾hutdownThread是Thread的擴(kuò)展,所以run會(huì)自動(dòng)運(yùn)行。
/**
* Makes sure we handle the shutdown gracefully.
* Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
*/
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();
}
};
/*
* Write a system property in case the system_server reboots before we
* get to the actual hardware restart. If that happens, we'll retry at
* the beginning of the SystemServer startup.
*/
{
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
}
/*
* If we are rebooting into safe mode, write a system property
* indicating so.
*/
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
...
rebootOrShutdown(mReboot, mRebootReason);
}
在重啟前會(huì)將重啟原因?qū)懭雜ys.shutdown.requested,如果沒(méi)有則為空,如果是安全模式還會(huì)將persist.sys.safemode置1,之后會(huì)進(jìn)行一些關(guān)機(jī)前的預(yù)處理,關(guān)閉ActivityManager以及MountService,最終調(diào)用rebootOrShutdown進(jìn)行關(guān)機(jī)操作。
/**
* Do not call this directly. Use {@link #reboot(Context, String, boolean)}
* or {@link #shutdown(Context, boolean)} instead.
*
* @param reboot true to reboot or false to shutdown
* @param reason reason for reboot
*/
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();
}
}
如果確認(rèn)重啟,則調(diào)用PowerManagerService的lowLevelReboot函數(shù),參數(shù)就是傳遞下來(lái)的reason,稍后分析。如果不是重啟,即mReboot=false,那就是需要關(guān)機(jī)了,在shutdown函數(shù)中就能夠知道。
/**
* Request a clean shutdown, waiting for subsystems to clean up their
* state etc. Must be called from a Looper thread in which its UI
* is shown.
*
* @param context Context used to display the shutdown progress dialog.
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
shutdownInner(context, confirm);
}
關(guān)機(jī)的時(shí)候需要震動(dòng),就是這里了SHUTDOWN_VIBRATE_MS,默認(rèn)的定義是500ms。但是在代碼上看,無(wú)論如何,最后都會(huì)調(diào)用一下lowLevelShutdown函數(shù),也就是關(guān)機(jī)。邏輯上,這里可能是個(gè)問(wèn)題,但是實(shí)際中,如果重啟操作能夠調(diào)用成功的話,整個(gè)系統(tǒng)都重啟了,后邊的代碼當(dāng)然不可能執(zhí)行到了。
目光轉(zhuǎn)回PowerManagerService
4.frameworks/base/services/java/com/android/server/PowerManagerService.java
/**
* Low-level function to reboot the device.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
* @throws IOException if reboot fails for some reason (eg, lack of
* permission)
*/
public static void lowLevelReboot(String reason) throws IOException {
nativeReboot(reason);
}
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use
* {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown.
*/
public static void lowLevelShutdown() {
nativeShutdown();
}
很熟悉的字樣native,是JNI調(diào)用了:
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[] = {
/* name, signature, funcPtr */
...
{ "nativeShutdown", "()V",
(void*) nativeShutdown },
{ "nativeReboot", "(Ljava/lang/String;)V",
(void*) nativeReboot },
...
};
這兩個(gè)好哥倆的實(shí)現(xiàn)也是在一起的:
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);
}
可以看到無(wú)論是關(guān)機(jī)還是重啟,都是調(diào)用android_reboot來(lái)實(shí)現(xiàn)的,只是參數(shù)不一樣而已。
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,所在在第五步的時(shí)候會(huì)傳入ANDROID_RB_RESTART2。到了android_reboot函數(shù)中,會(huì)看到這樣的定義#ifdef RECOVERY_PRE_COMMAND,即屬于重啟前會(huì)執(zhí)行的命令,如果定義了就會(huì)執(zhí)行。
下面也是做了一些關(guān)機(jī)重啟前的預(yù)處理工作,sync()作用是將緩存中的信息寫(xiě)入磁盤(pán),以免程序異常結(jié)束導(dǎo)致文件被損壞,linux系統(tǒng)關(guān)機(jī)前會(huì)做幾次這樣的動(dòng)作;而remount_ro()作用是通過(guò)調(diào)用emergency_remount()強(qiáng)制將文件系統(tǒng)掛載為只讀,不再允許任何寫(xiě)入操作,同時(shí)會(huì)通過(guò)檢查/proc/mounts的設(shè)備狀態(tài)來(lái)確認(rèn)是否當(dāng)前的所有寫(xiě)入工作已經(jīng)完成,這個(gè)檢查過(guò)程是阻塞操作。
接下來(lái)才是對(duì)參數(shù)的解析處理:
1)普通重啟 ANDROID_RB_RESTART, reason = RB_AUTOBOOT;
2)關(guān)機(jī) ANDROID_RB_POWEROFF, 無(wú)需reason,直接調(diào)用reboot進(jìn)行關(guān)機(jī);
3)帶參數(shù)的特殊重啟 ANDROID_RB_RESTART2, reason 將為默認(rèn)值 -1
這里又出現(xiàn)一個(gè)#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON,如果定義了它,則無(wú)論上層傳下來(lái)的參數(shù)是什么樣的,最終都只是普通重啟而已。定義它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON := true,應(yīng)該有廠商會(huì)喜歡這么做的,畢竟除了普通重啟,都可能帶給用戶(hù)一定的風(fēng)險(xiǎn)。
最后會(huì)對(duì)reason進(jìn)行一個(gè)檢測(cè),那么通過(guò)上邊的分析,其實(shí)只有帶參數(shù)的特殊重啟才會(huì)為-1,而不等于-1的情況中有普通重啟和關(guān)機(jī),而關(guān)機(jī)已經(jīng)自行解決了……所以,不等于-1的情況到了這里也只有普通重啟了。最終這里就是區(qū)分普通重啟與特殊重啟的地方了。這里再插入一個(gè)問(wèn)題,其他的幾個(gè)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之類(lèi)的在bionic/libc/kernel/common/linux/reboot.h中:
#define LINUX_REBOOT_MAGIC1 0xfee1dead #define LINUX_REBOOT_MAGIC2 672274793 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define LINUX_REBOOT_MAGIC2A 85072278 #define LINUX_REBOOT_MAGIC2B 369367448 #define LINUX_REBOOT_MAGIC2C 537993216 #define LINUX_REBOOT_CMD_RESTART 0x01234567 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #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 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2 #define LINUX_REBOOT_CMD_KEXEC 0x45584543
至于為什么他們是這樣奇怪的值這個(gè)問(wèn)題,我只能說(shuō)他們是magic number,魔法嘛,本來(lái)就是正常人不能夠理解的,所以~~~放過(guò)他們吧,只要知道他們沒(méi)有是-1的就OK啦。
先來(lái)看reboot函數(shù),按照往常的經(jīng)驗(yàn),reboot最終一定會(huì)調(diào)用到__reboot的。
7.bionic/libc/unistd/reboot.c
int reboot (int mode)
{
return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );
}
Bingo!果然是這樣,如此說(shuō)來(lái)reboot(reason) -> reboot(RB_AUTOBOOT) -> __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART, NULL ),要是直接這樣寫(xiě)多好~~~免得繞這一層了。
二、KERNEL域
8.__reboot通過(guò)syscall來(lái)到內(nèi)核
這里用一些篇幅簡(jiǎn)要介紹syscall,以后遇到類(lèi)似的東西更好追蹤一些。
第七步中的__reboot在arm架構(gòu)的實(shí)現(xiàn)是這樣的(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)
可以看出來(lái),這里將__reboot的實(shí)現(xiàn)映射到了__NR_reboot, 而在bionic/libc/sys/linux-syscalls.h能夠找到:
#define __NR_reboot (__NR_SYSCALL_BASE + 88)
其被指定了一個(gè)固定的偏移量,在被調(diào)用的時(shí)候就是通過(guò)這個(gè)偏移量去內(nèi)核中尋找對(duì)應(yīng)的入口的,由此可見(jiàn),內(nèi)核中一定有著相同的定義,否則將不能成功調(diào)用。內(nèi)核中對(duì)syscall偏移量的定義在內(nèi)核源碼中的arch/arm/include/asm/unistd.h,相關(guān)信息完全一致。
已經(jīng)找到了內(nèi)核中的對(duì)應(yīng)映射,那么下一步就要去找尋真正的實(shí)現(xiàn)函數(shù)了,在include/asm-generic/unistd.h中可以找到內(nèi)核對(duì)__NR_reboot的syscall函數(shù)映射,即
/* kernel/sys.c */ #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)
同時(shí),能夠發(fā)現(xiàn)如此溫馨的一幕,內(nèi)核已經(jīng)指引我們下一步該去哪里尋找sys_reboot,即kernel/sys.c。
9.kernel/sys.c
在進(jìn)入這個(gè)文件前,我們先去include/linux/syscalls.h中查看一下sys_reboot的定義:
asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd,
void __user *arg);
與__reboot的調(diào)用參數(shù)一致。
進(jìn)入sys.c文件后,并沒(méi)有找到名為sys_reboot的函數(shù),而通過(guò)仔細(xì)查找,發(fā)現(xiàn)一個(gè)很有趣的函數(shù),其定義為SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),對(duì)比__reboot的參數(shù),能夠符合。究竟是不是這個(gè)函數(shù)?
同樣在include/linux/syscalls.h文件中,能夠找到這樣幾個(gè)定義:
#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__))
整合后等價(jià)于:
#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,也就是上層調(diào)用的__reboot的最終實(shí)現(xiàn)。函數(shù)實(shí)現(xiàn)如下:
/*
* Reboot system call: for obvious reasons only root may call it,
* and even root needs to set up some magic numbers in the registers
* so that some mistake won't make this reboot the whole machine.
* You can also set the meaning of the ctrl-alt-del-key here.
*
* reboot doesn't sync: do that yourself before calling this.
*/
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
char buffer[256];
int ret = 0;
/* We only trust the superuser with rebooting the system. */
if (!capable(CAP_SYS_BOOT))
return -EPERM;
/* For safety, we require "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;
/* Instead of trying to make the power_off code look like
* halt when pm_power_off is not set do it the easy way.
*/
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;
}
在此函數(shù)中,首先會(huì)檢測(cè)權(quán)限問(wèn)題,只有超級(jí)用戶(hù)才可以執(zhí)行重啟系統(tǒng)的操作:
/* We only trust the superuser with rebooting the system. */ if (!capable(CAP_SYS_BOOT)) return -EPERM;
否則將返回權(quán)限錯(cuò)誤。對(duì)應(yīng)的權(quán)限列表在include/linux/capability.h中,重啟操作為22.
隨后對(duì)magic number進(jìn)行了校驗(yàn):
/* For safety, we require "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;
如果數(shù)據(jù)傳輸過(guò)程中沒(méi)有發(fā)生錯(cuò)誤的話,這里也當(dāng)然不會(huì)有問(wèn)題,所以只是一個(gè)安全性校驗(yàn),基本不會(huì)發(fā)生錯(cuò)誤。
之后有一個(gè)很有趣的檢查,如果用戶(hù)要求關(guān)機(jī),而pm_power_off為空的話,就把用戶(hù)的關(guān)機(jī)命令轉(zhuǎn)換為掛起:
/* Instead of trying to make the power_off code look like * halt when pm_power_off is not set do it the easy way. */ if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off) cmd = LINUX_REBOOT_CMD_HALT;
在arch/arm/kernel/process.c中可以找到它的定義:
/* * Function pointers to optional machine specific functions */ void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off);
好的,只是一個(gè)函數(shù)指針,而且做了全局操作,整個(gè)kernel都可以調(diào)用它。以高通msm7x30為例,在arch/arm/mach-msm/pm2.c中對(duì)這個(gè)函數(shù)指針進(jìn)行了賦值:
pm_power_off = msm_pm_power_off;
msm_pm_power_off的具體實(shí)現(xiàn)就不再跟蹤了,各家的都不一樣,跟下去沒(méi)有太大意義?,F(xiàn)在只要知道,我分析的這個(gè)kernel是給了這個(gè)函數(shù)指針賦值的,所以不為空,關(guān)機(jī)命令將正常執(zhí)行。
接下來(lái)就是這個(gè)函數(shù)的正題了,對(duì)用戶(hù)命令進(jìn)行解析操作,同時(shí)這個(gè)過(guò)程是用reboot_mutex互斥鎖來(lái)進(jìn)行保護(hù)的,以保證同一時(shí)間只可能有一個(gè)解析過(guò)程,避免沖突。
下邊貼出所有關(guān)機(jī)重啟相關(guān)的命令定義:
/*
* Commands accepted by the _reboot() system call.
*
* RESTART Restart system using default command and mode.
* HALT Stop OS and give system control to ROM monitor, if any.
* CAD_ON Ctrl-Alt-Del sequence causes RESTART command.
* CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task.
* POWER_OFF Stop OS and remove all power from system, if possible.
* RESTART2 Restart system using given command string.
* SW_SUSPEND Suspend system using software suspend if compiled in.
* KEXEC Restart system using a previously loaded Linux kernel
*/
#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
注釋中的說(shuō)明很詳細(xì)了,比較陌生的就是關(guān)于CAD,其實(shí)就是用來(lái)想用Ctrl+Alt+Del操作的;然后SW_SYSPEND是軟件休眠;KEXEC就太高端了,屬于內(nèi)核的一個(gè)補(bǔ)丁,用來(lái)利用老內(nèi)核重啟,詳細(xì)資料:http://www.ibm.com/developerworks/cn/linux/l-kexec/?ca=dwcn-newsletter-linux
以上這些只有前六個(gè)命令被Android系統(tǒng)所使用,為什么這么說(shuō),可以去看bionic/libc/include/sys/reboot.h,上邊已經(jīng)貼出了。LINUX_REBOOT_CMD_HALT雖有定義,但是也沒(méi)有發(fā)現(xiàn)Android系統(tǒng)中哪里有調(diào)用,有高手找到的話,希望能夠告知一下。最終的最終,能夠用到的就只有三個(gè):
- RESTART
- POWER_OFF
- RESTART2
10.最終實(shí)現(xiàn)
重啟調(diào)用的是kernel_restart,區(qū)別是參數(shù)是不是空,關(guān)機(jī)則調(diào)用kernel_power_off(),先看關(guān)機(jī):
/**
* kernel_power_off - power_off the system
*
* Shutdown everything and perform a clean system 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);
最了一系列準(zhǔn)備工作,最終調(diào)用machine_power_off():
void machine_power_off(void)
{
machine_shutdown();
if (pm_power_off)
pm_power_off();
}
之前找尋的pm_power_off在這里就有用處了,是關(guān)機(jī)的最后一步操作。關(guān)機(jī)完成,之后看下重啟操作:
/**
* kernel_restart - reboot the system
* @cmd: pointer to buffer containing command to execute for restart
* or %NULL
*
* Shutdown everything and perform a clean reboot.
* This is not safe to call in interrupt context.
*/
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);
同樣的套路,也是會(huì)進(jìn)行一些準(zhǔn)備工作,之后調(diào)用machine_restart(cmd), 如果是普通重啟,那么中個(gè)cmd就為NULL,如果是特殊重啟,那么這個(gè)cmd就是一層一層傳遞下來(lái)得那個(gè)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嗎?在那里同樣對(duì)arm_pm_restart進(jìn)行了指針賦值:
arm_pm_restart = msm_pm_restart;
賦值的函數(shù)為msm_pm_init, 其調(diào)用為
late_initcall_sync(msm_pm_init);
late_initcall_sync的啟動(dòng)優(yōu)先級(jí)是最低的,為7。module_init其實(shí)是6的優(yōu)先級(jí),數(shù)字越大優(yōu)先級(jí)越低。所以,這樣推斷的話,最終arm_pm_restart這個(gè)函數(shù)指針會(huì)指向msm_pm_restart。關(guān)于msm_pm_restart的具體實(shí)現(xiàn)也不細(xì)看了,跟前邊說(shuō)的一樣,都是各家不一樣,就幾行代碼:
static void msm_pm_restart(char str, const char *cmd)
{
msm_rpcrouter_close();
msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
for (;;)
;
}
但是細(xì)心的朋友可能會(huì)發(fā)現(xiàn)這里有一個(gè)restart_reason,這個(gè)并不是傳遞下來(lái)的參數(shù)。事實(shí)上,這個(gè)值已經(jīng)在之前kernel_restart_prepare(cmd)的時(shí)候就已經(jīng)設(shè)置好了。
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機(jī)制,這個(gè)操作在之前的shutdown關(guān)機(jī)操作中也有,且是同一個(gè)list,都是reboot_notifier_list。也很容易理解,就是將注冊(cè)在reboot_notifier_list上的函數(shù)傳入相關(guān)參數(shù)后執(zhí)行,作為了解,看一下具體是怎么使用的:(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,萬(wàn)事大吉,在kernel_restart_prepare的時(shí)候msm_reboot_call會(huì)被首先調(diào)用,這個(gè)函數(shù)的作用就是根據(jù)用戶(hù)命令給restart_reason賦值,從而在之后調(diào)用msm_pm_restart的時(shí)候使用。這里我們發(fā)現(xiàn)在reboot的時(shí)候可以帶的參數(shù)不僅有recovery,bootloader,還有eraseflash和oem-???,字面上看應(yīng)該是用來(lái)擦除ROM和解鎖之類(lèi)的操作了。
三、關(guān)機(jī)怎么用?
本文的分析是由Android給出的reboot接口開(kāi)始的,但是分析來(lái)分析去,回頭想一想會(huì)發(fā)現(xiàn),Android給出的接口reboot就真的只能重啟而已,不能進(jìn)行關(guān)機(jī)操作,可以在跟蹤這個(gè)流程的過(guò)程中會(huì)發(fā)現(xiàn),確實(shí)是有存在關(guān)機(jī)的相關(guān)接口的。那么關(guān)機(jī)該怎么用呢?
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實(shí)現(xiàn)短按電源鍵關(guān)機(jī)的實(shí)現(xiàn)代碼
- Android平臺(tái)預(yù)置GMS包后關(guān)機(jī)鬧鐘失效問(wèn)題及解決方法
- Android實(shí)現(xiàn)關(guān)機(jī)后數(shù)據(jù)不會(huì)丟失問(wèn)題
- Android 6.0開(kāi)發(fā)實(shí)現(xiàn)關(guān)機(jī)菜單添加重啟按鈕的方法
- Android開(kāi)發(fā)實(shí)現(xiàn)長(zhǎng)按返回鍵彈出關(guān)機(jī)框功能
- Android仿蘋(píng)果關(guān)機(jī)界面實(shí)現(xiàn)代碼
- Android 修改系統(tǒng)關(guān)機(jī)動(dòng)畫(huà)的實(shí)現(xiàn)
- Android下的CMD命令之關(guān)機(jī)重啟及重啟recovery
- Android實(shí)現(xiàn)關(guān)機(jī)與重啟的幾種方式(推薦)
- Android 實(shí)現(xiàn)關(guān)機(jī)的多種方式
相關(guān)文章
Android貝塞爾曲線初步學(xué)習(xí)第三課 Android實(shí)現(xiàn)添加至購(gòu)物車(chē)的運(yùn)動(dòng)軌跡
這篇文章主要為大家詳細(xì)介紹了Android貝塞爾曲線初步學(xué)習(xí)第三課,Android實(shí)現(xiàn)添加至購(gòu)物車(chē)的運(yùn)動(dòng)軌跡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Flutter進(jìn)階之實(shí)現(xiàn)動(dòng)畫(huà)效果(九)
這篇文章主要為大家詳細(xì)介紹了Flutter進(jìn)階之實(shí)現(xiàn)動(dòng)畫(huà)效果的第九篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
Android?RecyclerView實(shí)現(xiàn)九宮格效果
這篇文章主要為大家詳細(xì)介紹了Android?RecyclerView實(shí)現(xiàn)九宮格效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
android通過(guò)google api獲取天氣信息示例
這篇文章主要介紹了android通過(guò)google api獲取天氣信息示例,需要的朋友可以參考下2014-04-04
Android實(shí)現(xiàn)手機(jī)拍照功能
這篇文章主要介紹了Android實(shí)現(xiàn)手機(jī)拍照功能,感興趣的小伙伴們可以參考一下2015-12-12
Android視頻懸浮窗口實(shí)現(xiàn)的示例代碼
這篇文章主要介紹了Android視頻懸浮窗口實(shí)現(xiàn)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Android系統(tǒng)進(jìn)程間通信Binder機(jī)制在應(yīng)用程序框架層的Java接口源代碼分析
本文主要介紹 Android系統(tǒng)進(jìn)程間通信Binder機(jī)制Java 接口源碼分析,這里詳細(xì)介紹了如何實(shí)現(xiàn)Binder 機(jī)制和Java接口直接的通信,有興趣的小伙伴可以參考下2016-08-08

