国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android系統關機的全流程解析

2020-04-11 10:46:05
字體:
來源:轉載
供稿:網友

在PowerManager的API文檔中,給出了一個關機/重啟接口:
public void reboot (String reason)

對于這個接口的描述很簡單,就是幾句話。
接口的作用就是重啟設備,而且,就算重啟成功了也沒有返回值。
需要包含REBOOT權限,也就是android.permission.REBOOT
唯一參數reason代表需要的特定重啟模式,比如recovery,當然也可以為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接口服務。

/**  * {@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); } 

這里說明是需要重啟,且不是安全模式,重啟參數為傳遞下來的reason,shutdownInner的confirm參數是用來設置是否有確認提示框的,通過reboot接口調用重啟是沒有的,為false。
重啟的實現在run()中,因為ShutdownThread是Thread的擴展,所以run會自動運行。

/**  * 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); }  

在重啟前會將重啟原因寫入sys.shutdown.requested,如果沒有則為空,如果是安全模式還會將persist.sys.safemode置1,之后會進行一些關機前的預處理,關閉ActivityManager以及MountService,最終調用rebootOrShutdown進行關機操作。

 

  /**    * 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();   } } 

如果確認重啟,則調用PowerManagerService的lowLevelReboot函數,參數就是傳遞下來的reason,稍后分析。如果不是重啟,即mReboot=false,那就是需要關機了,在shutdown函數中就能夠知道。

/**  * 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); } 

關機的時候需要震動,就是這里了SHUTDOWN_VIBRATE_MS,默認的定義是500ms。但是在代碼上看,無論如何,最后都會調用一下lowLevelShutdown函數,也就是關機。邏輯上,這里可能是個問題,但是實際中,如果重啟操作能夠調用成功的話,整個系統都重啟了,后邊的代碼當然不可能執行到了。
目光轉回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調用了:

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 },   ... }; 

這兩個好哥倆的實現也是在一起的:

static void nativeShutdown(JNIEnv *env, jobject clazz) {   android_reboot(ANDROID_RB_POWEROFF, 0, 0); }  static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {   if (reason == NULL) {     android_reboot(ANDROID_RB_RESTART, 0, 0);   } else {     const char *chars = env->GetStringUTFChars(reason, NULL);     android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);     env->ReleaseStringUTFChars(reason, chars); // In case it fails.   }   jniThrowIOException(env, errno); } 

可以看到無論是關機還是重啟,都是調用android_reboot來實現的,只是參數不一樣而已。

6.system/core/libcutils/android_reboot.c

int android_reboot(int cmd, int flags, char *arg) {   int ret = 0;   int reason = -1;  #ifdef RECOVERY_PRE_COMMAND   if (cmd == (int) ANDROID_RB_RESTART2) {     if (arg && strlen(arg) > 0) {       char cmd[PATH_MAX];       sprintf(cmd, RECOVERY_PRE_COMMAND " %s", arg);       system(cmd);     }   } #endif    if (!(flags & ANDROID_RB_FLAG_NO_SYNC))     sync();    if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))     remount_ro();    switch (cmd) {     case ANDROID_RB_RESTART:       reason = RB_AUTOBOOT;       break;      case ANDROID_RB_POWEROFF:       ret = reboot(RB_POWER_OFF);       return ret;      case ANDROID_RB_RESTART2:       // REBOOT_MAGIC       break;      default:       return -1;   }  #ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON   reason = RB_AUTOBOOT; #endif    if (reason != -1)     ret = reboot(reason);   else     ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,               LINUX_REBOOT_CMD_RESTART2, arg);    return ret; } 

以reboot recovery為例,arg即為recovery,所在在第五步的時候會傳入ANDROID_RB_RESTART2。到了android_reboot函數中,會看到這樣的定義#ifdef RECOVERY_PRE_COMMAND,即屬于重啟前會執行的命令,如果定義了就會執行。
下面也是做了一些關機重啟前的預處理工作,sync()作用是將緩存中的信息寫入磁盤,以免程序異常結束導致文件被損壞,linux系統關機前會做幾次這樣的動作;而remount_ro()作用是通過調用emergency_remount()強制將文件系統掛載為只讀,不再允許任何寫入操作,同時會通過檢查/proc/mounts的設備狀態來確認是否當前的所有寫入工作已經完成,這個檢查過程是阻塞操作。
接下來才是對參數的解析處理:
1)普通重啟 ANDROID_RB_RESTART, reason = RB_AUTOBOOT;
2)關機 ANDROID_RB_POWEROFF, 無需reason,直接調用reboot進行關機;
3)帶參數的特殊重啟 ANDROID_RB_RESTART2, reason 將為默認值 -1
這里又出現一個#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON,如果定義了它,則無論上層傳下來的參數是什么樣的,最終都只是普通重啟而已。定義它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON := true,應該有廠商會喜歡這么做的,畢竟除了普通重啟,都可能帶給用戶一定的風險。
最后會對reason進行一個檢測,那么通過上邊的分析,其實只有帶參數的特殊重啟才會為-1,而不等于-1的情況中有普通重啟和關機,而關機已經自行解決了……所以,不等于-1的情況到了這里也只有普通重啟了。最終這里就是區分普通重啟與特殊重啟的地方了。這里再插入一個問題,其他的幾個cmd都是什么值呢?答案在bionic/libc/include/sys/reboot.h中:

#define RB_AUTOBOOT   LINUX_REBOOT_CMD_RESTART #define RB_HALT_SYSTEM LINUX_REBOOT_CMD_HALT #define RB_ENABLE_CAD  LINUX_REBOOT_CMD_CAD_ON #define RB_DISABLE_CAD LINUX_REBOOT_CMD_CAD_OFF #define RB_POWER_OFF  LINUX_REBOOT_CMD_POWER_OFF 

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

#define LINUX_REBOOT_MAGIC1 0xfee1dead #define LINUX_REBOOT_MAGIC2 672274793 /* 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 

至于為什么他們是這樣奇怪的值這個問題,我只能說他們是magic number,魔法嘛,本來就是正常人不能夠理解的,所以~~~放過他們吧,只要知道他們沒有是-1的就OK啦。
先來看reboot函數,按照往常的經驗,reboot最終一定會調用到__reboot的。

7.bionic/libc/unistd/reboot.c

int reboot (int mode)  {   return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL ); } 

Bingo!果然是這樣,如此說來reboot(reason) -> reboot(RB_AUTOBOOT) -> __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART, NULL ),要是直接這樣寫多好~~~免得繞這一層了。

二、KERNEL域
8.__reboot通過syscall來到內核
這里用一些篇幅簡要介紹syscall,以后遇到類似的東西更好追蹤一些。
第七步中的__reboot在arm架構的實現是這樣的(bionic/libc/arch-arm/syscalls/__reboot.S)

ENTRY(__reboot)   .save  {r4, r7}    stmfd  sp!, {r4, r7}   ldr   r7, =__NR_reboot   swi   #0    ldmfd  sp!, {r4, r7}   movs  r0, r0   bxpl  lr    b    __set_syscall_errno END(__reboot) 

可以看出來,這里將__reboot的實現映射到了__NR_reboot, 而在bionic/libc/sys/linux-syscalls.h能夠找到:

#define __NR_reboot            (__NR_SYSCALL_BASE + 88) 

其被指定了一個固定的偏移量,在被調用的時候就是通過這個偏移量去內核中尋找對應的入口的,由此可見,內核中一定有著相同的定義,否則將不能成功調用。內核中對syscall偏移量的定義在內核源碼中的arch/arm/include/asm/unistd.h,相關信息完全一致。
已經找到了內核中的對應映射,那么下一步就要去找尋真正的實現函數了,在include/asm-generic/unistd.h中可以找到內核對__NR_reboot的syscall函數映射,即

/* 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) 

同時,能夠發現如此溫馨的一幕,內核已經指引我們下一步該去哪里尋找sys_reboot,即kernel/sys.c。

9.kernel/sys.c
在進入這個文件前,我們先去include/linux/syscalls.h中查看一下sys_reboot的定義:

asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd,         void __user *arg); 

與__reboot的調用參數一致。
進入sys.c文件后,并沒有找到名為sys_reboot的函數,而通過仔細查找,發現一個很有趣的函數,其定義為SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),對比__reboot的參數,能夠符合。究竟是不是這個函數?
同樣在include/linux/syscalls.h文件中,能夠找到這樣幾個定義:

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) #define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) ...  #define SYSCALL_DEFINEx(x, sname, ...)       /   __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) ...  #define __SYSCALL_DEFINEx(x, name, ...)         /   asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)) 

整合后等價于:

#define SYSCALL_DEFINE4(name, ...) /   asmlinkage long sys##_name(__SC_DECL##4(__VA_ARGS__)) 

這樣就不難看出,SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)就是sys_reboot,也就是上層調用的__reboot的最終實現。函數實現如下:

/*  * 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; } 

在此函數中,首先會檢測權限問題,只有超級用戶才可以執行重啟系統的操作:

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

否則將返回權限錯誤。對應的權限列表在include/linux/capability.h中,重啟操作為22.
隨后對magic number進行了校驗:

/* 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; 

如果數據傳輸過程中沒有發生錯誤的話,這里也當然不會有問題,所以只是一個安全性校驗,基本不會發生錯誤。
之后有一個很有趣的檢查,如果用戶要求關機,而pm_power_off為空的話,就把用戶的關機命令轉換為掛起:

/* 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); 

好的,只是一個函數指針,而且做了全局操作,整個kernel都可以調用它。以高通msm7x30為例,在arch/arm/mach-msm/pm2.c中對這個函數指針進行了賦值:

pm_power_off = msm_pm_power_off; 

msm_pm_power_off的具體實現就不再跟蹤了,各家的都不一樣,跟下去沒有太大意義。現在只要知道,我分析的這個kernel是給了這個函數指針賦值的,所以不為空,關機命令將正常執行。
接下來就是這個函數的正題了,對用戶命令進行解析操作,同時這個過程是用reboot_mutex互斥鎖來進行保護的,以保證同一時間只可能有一個解析過程,避免沖突。
下邊貼出所有關機重啟相關的命令定義:

/*  * 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 

注釋中的說明很詳細了,比較陌生的就是關于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_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); 

最了一系列準備工作,最終調用machine_power_off():

void machine_power_off(void) {     machine_shutdown();   if (pm_power_off)     pm_power_off(); } 

之前找尋的pm_power_off在這里就有用處了,是關機的最后一步操作。關機完成,之后看下重啟操作:

/**  * 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); 

同樣的套路,也是會進行一些準備工作,之后調用machine_restart(cmd), 如果是普通重啟,那么中個cmd就為NULL,如果是特殊重啟,那么這個cmd就是一層一層傳遞下來得那個arg了。

void machine_restart(char *cmd) {   machine_shutdown();   arm_pm_restart(reboot_mode, cmd); } ... void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart; EXPORT_SYMBOL_GPL(arm_pm_restart); 

而還記得剛才的pm2.c嗎?在那里同樣對arm_pm_restart進行了指針賦值:

arm_pm_restart = msm_pm_restart; 

賦值的函數為msm_pm_init, 其調用為

late_initcall_sync(msm_pm_init); 

late_initcall_sync的啟動優先級是最低的,為7。module_init其實是6的優先級,數字越大優先級越低。所以,這樣推斷的話,最終arm_pm_restart這個函數指針會指向msm_pm_restart。關于msm_pm_restart的具體實現也不細看了,跟前邊說的一樣,都是各家不一樣,就幾行代碼:

static void msm_pm_restart(char str, const char *cmd) {       msm_rpcrouter_close();   msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);    for (;;)     ; }  

但是細心的朋友可能會發現這里有一個restart_reason,這個并不是傳遞下來的參數。事實上,這個值已經在之前kernel_restart_prepare(cmd)的時候就已經設置好了。

void kernel_restart_prepare(char *cmd) {     blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);   system_state = SYSTEM_RESTART;   usermodehelper_disable();   device_shutdown();   syscore_shutdown(); } 

就是blocking_notifier機制,這個操作在之前的shutdown關機操作中也有,且是同一個list,都是reboot_notifier_list。也很容易理解,就是將注冊在reboot_notifier_list上的函數傳入相關參數后執行,作為了解,看一下具體是怎么使用的:(arch/arm/mach-msm/pm2.c)

static int msm_reboot_call   (struct notifier_block *this, unsigned long code, void *_cmd) {     if ((code == SYS_RESTART) && _cmd) {     char *cmd = _cmd;     if (!strcmp(cmd, "bootloader")) {       restart_reason = 0x77665500;     } else if (!strcmp(cmd, "recovery")) {       restart_reason = 0x77665502;     } else if (!strcmp(cmd, "eraseflash")) {       restart_reason = 0x776655EF;     } else if (!strncmp(cmd, "oem-", 4)) {       unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;       restart_reason = 0x6f656d00 | code;     } else {       restart_reason = 0x77665501;      }     }       return NOTIFY_DONE; }            static struct notifier_block msm_reboot_notifier = {   .notifier_call = msm_reboot_call, };  ...  static int __init msm_pm_init(void) { ...   register_reboot_notifier(&msm_reboot_notifier); ... } 

OK,萬事大吉,在kernel_restart_prepare的時候msm_reboot_call會被首先調用,這個函數的作用就是根據用戶命令給restart_reason賦值,從而在之后調用msm_pm_restart的時候使用。這里我們發現在reboot的時候可以帶的參數不僅有recovery,bootloader,還有eraseflash和oem-???,字面上看應該是用來擦除ROM和解鎖之類的操作了。

三、關機怎么用?
本文的分析是由Android給出的reboot接口開始的,但是分析來分析去,回頭想一想會發現,Android給出的接口reboot就真的只能重啟而已,不能進行關機操作,可以在跟蹤這個流程的過程中會發現,確實是有存在關機的相關接口的。那么關機該怎么用呢?
frameworks/base/services/java/com/android/serverBatteryService.java

private final void shutdownIfNoPower() { // shut down gracefully if our battery is critically low and we are not powered. // wait until the system has booted before attempting to display the shutdown dialog. if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {   Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);   intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);   intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);   mContext.startActivity(intent); } 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 沂南县| 舒兰市| 册亨县| 股票| 肇源县| 济源市| 南平市| 和林格尔县| 迭部县| 万全县| 濮阳县| 丹巴县| 高要市| 桑植县| 长岛县| 洪泽县| 巴林右旗| 大丰市| 迁安市| 厦门市| 天气| 九龙坡区| 湖南省| 丰台区| 三门县| 巍山| 沾益县| 堆龙德庆县| 蒙城县| 东方市| 永顺县| 麻阳| 紫云| 平谷区| 绥宁县| 呼伦贝尔市| 惠水县| 土默特右旗| 石城县| 无为县| 万宁市|