2014년 1월 8일 수요일

[Android] Device Power Off Flow


전원키를 통한 안드로이드 디바이스 Power Off의 경우 아래와 같은 flow를 통해 전원 컨트롤이 이루어진다.
PowerManageService ( ShutdownThread :: rebootOrShutdown(boolean reboot, String reason))
-> JNI -> System Call
-> kernel -> PMIC

============================================================
Android Frameworks
============================================================
PowerManagerService.java

    /**
     * Reboots the device.
     */
    public void reboot(boolean confirm, String reason, boolean wait) {
        try {
            shutdownOrRebootInternal(false, confirm, reason, wait);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    /**
     * Shuts down the device.
     */
    @Override // Binder call
    public void shutdown(boolean confirm, boolean wait) {
        try {
            shutdownOrRebootInternal(true, confirm, null, wait);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
            final String reason, boolean wait) {

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    if (shutdown) {
                        ShutdownThread.shutdown(mContext, confirm);
                    } else {
                        ShutdownThread.reboot(mContext, reason, confirm);
                    }
                }
            }
        };
         ......
    }


ShutdownThread.java
    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);
            }
            ......
        }

        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown();
    }

PowerManagerService.java
    /**
     * Low-level function turn the device off immediately, without trying
     * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
     */
    public static void lowLevelShutdown() {
        nativeShutdown();
    }

============================================================
JNI / Native
============================================================
com_android_server_power_PowerManagerService.cpp
static void nativeShutdown(JNIEnv *env, jclass clazz) {
    android_reboot(ANDROID_RB_POWEROFF, 0, 0);
}

static JNINativeMethod gPowerManagerServiceMethods[] = {
    /* name, signature, funcPtr */
    ......
    { "nativeShutdown", "()V",
            (void*) nativeShutdown },
    ......
};

Android_reboot.c
int android_reboot(int cmd, int flags, char *arg)
{
    switch (cmd) {
        case ANDROID_RB_RESTART:
            ret = reboot(RB_AUTOBOOT);
            break;

        case ANDROID_RB_POWEROFF:
            ret = reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2:
            ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;

        default:
            ret = -1;
    }

reboot.h
#define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF

============================================================
Kernel
============================================================
kernel/sys.c
/*
 * 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)
{
    ......
switch (cmd) {
        ......
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;
        ......
       }
}