Android關機流程解析

在PowerManager的API文檔中,給出瞭一個關機/重啟接口:

public void reboot (String reason)
對於這個接口的描述很簡單,就是幾句話。

接口的作用就是重啟設備,而且,就算重啟成功瞭也沒有返回值。

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

唯一參數reason代表需要的特定重啟模式,比如recovery,當然也可以為null。

——————————–上層空間——————————–

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

[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) { 
    }    
}  

    /**
     * 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接口服務。

[java] 
/**
 * {@hide}
 */ 
public PowerManager(IPowerManager service, Handler handler) 

    mService = service; 
    mHandler = handler; 

    /**
     * {@hide}
     */
    public PowerManager(IPowerManager service, Handler handler)
    {
        mService = service;
        mHandler = handler;
    }

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

[java]
interface IPowerManager 

… 
void reboot(String reason); 
… 

interface IPowerManager
{

void reboot(String reason);

}

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

[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) {  
            }     
        }     
    }     

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

[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); 

    /**
     * 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會自動運行。

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

    /**
     * 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進行關機操作。

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

    /**
     * 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函數中就能夠知道。

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

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

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

    /** 
     * 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調用瞭:

[java] 
private static native void nativeShutdown(); 
private static native void nativeReboot(String reason) throws IOException; 

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

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

[cpp] 
static JNINativeMethod gPowerManagerServiceMethods[] = {  
    /* name, signature, funcPtr */ 
    … 
    { "nativeShutdown", "()V", 
            (void*) nativeShutdown }, 
    { "nativeReboot", "(Ljava/lang/String;)V", 
            (void*) nativeReboot }, 
    … 
}; 

static JNINativeMethod gPowerManagerServiceMethods[] = {
    /* name, signature, funcPtr */
    …
    { "nativeShutdown", "()V",
            (void*) nativeShutdown },
    { "nativeReboot", "(Ljava/lang/String;)V",
            (void*) nativeReboot },
    …
};
這兩個好哥倆的實現也是在一起的:

[cpp] 
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); 

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

[cpp] 
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; 

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

[cpp] 
#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 

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

[cpp]
#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 

#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

[cpp]
int reboot (int  mode)  

    return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL ); 

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)

[plain]
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) 

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能夠找到:

[plain]
#define __NR_reboot                       (__NR_SYSCALL_BASE + 88) 

#define __NR_reboot                       (__NR_SYSCALL_BASE + 88)
其被指定瞭一個固定的偏移量,在被調用的時候就是通過這個偏移量去內核中尋找對應的入口的,由此可見,內核中一定有著相同的定義,否則將不能成功調用。內核中對syscall偏移量的定義在內核源碼中的arch/arm/include/asm/unistd.h,相關信息完全一致。

已經找到瞭內核中的對應映射,那麼下一步就要去找尋真正的實現函數瞭,在include/asm-generic/unistd.h中可以找到內核對__NR_reboot的syscall函數映射,即

[cpp] 
/* 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) 

/* 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的定義:

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

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文件中,能夠找到這樣幾個定義:

[cpp]
#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_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__))
整合後等價於:
[cpp
#define SYSCALL_DEFINE4(name, …) \  
    asmlinkage long sys##_name(__SC_DECL##4(__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的最終實現。函數實現如下:
[cpp] 
/*
 * 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; 

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

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

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

    /* We only trust the superuser with rebooting the system. */
    if (!capable(CAP_SYS_BOOT))
        return -EPERM;
否則將返回權限錯誤。對應的權限列表在include/linux/capability.h中,重啟操作為22.

隨後對magic number進行瞭校驗:

[cpp] 
/* 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; 

    /* 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為空的話,就把用戶的關機命令轉換為掛起:

[cpp] 
/* 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; 

    /* 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中可以找到它的定義:

[cpp]
/*
 * Function pointers to optional machine specific functions
 */ 
void (*pm_power_off)(void); 
EXPORT_SYMBOL(pm_power_off); 

/*
 * 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中對這個函數指針進行瞭賦值:

[cpp] 
pm_power_off = msm_pm_power_off; 

     pm_power_off = msm_pm_power_off;

msm_pm_power_off的具體實現就不再跟蹤瞭,各傢的都不一樣,跟下去沒有太大意義。現在隻要知道,我分析的這個kernel是給瞭這個函數指針賦值的,所以不為空,關機命令將正常執行。

接下來就是這個函數的正題瞭,對用戶命令進行解析操作,同時這個過程是用reboot_mutex互斥鎖來進行保護的,以保證同一時間隻可能有一個解析過程,避免沖突。

下邊貼出所有關機重啟相關的命令定義:

[cpp] 
/*
 * 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 

/*
 * 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(),先看關機:

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

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

[cpp] 
void machine_power_off(void) 
{    
    machine_shutdown(); 
    if (pm_power_off) 
        pm_power_off(); 

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

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

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

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

[cpp]
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); 

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進行瞭指針賦值:

[cpp] 
arm_pm_restart = msm_pm_restart; 

arm_pm_restart = msm_pm_restart;
賦值的函數為msm_pm_init, 其調用為

[cpp] 
late_initcall_sync(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的具體實現也不細看瞭,跟前邊說的一樣,都是各傢不一樣,就幾行代碼:
[cpp] 
static void msm_pm_restart(char str, const char *cmd) 
{        
    msm_rpcrouter_close(); 
    msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0); 
 
    for (;;) 
        ; 
}  

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)的時候就已經設置好瞭。

[cpp] v
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(); 

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)

 

[cpp] 
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); 
… 

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
[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); 

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

這樣就可以瞭,不用多說瞭吧。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *