Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android L 關機流程圖

android L 關機流程圖

編輯:關於Android編程

下面是簡單的流程圖,從java到kernel層。

 

\

 

 

ShutdownThread.java文件

stop playing music,因為後面可能要playing shutdown music.

代碼如下:

 

 

private static void beginShutdownSequence(Context context) {
  ....
        //acquire audio focus to make the other apps to stop playing muisc
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mAudioManager.requestAudioFocus(null,
                AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);


 

show system dialog to indicate phone is shutting down,如果沒有關機動畫的話,要show一個關機提示出來。

代碼如下:

 

 

        if (!checkAnimationFileExist()) {
            // throw up an indeterminate system dialog to indicate radio is
            // shutting down.
            ProgressDialog pd = new ProgressDialog(context);
            pd.setTitle(context.getText(com.android.internal.R.string.power_off));
            pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
            pd.setIndeterminate(true);
            pd.setCancelable(false);
            pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

            pd.show();
        }

Hold the wakelock,make sure we never fall asleep again,抓鎖防止機器關機過程中休眠

 

代碼如下:

 

 

            sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                    PowerManager.PARTIAL_WAKE_LOCK, TAG + -cpu);//這個只是鎖住cpu不進入休眠,但screen是off的,需full鎖來保證screen常亮
            sInstance.mCpuWakeLock.setReferenceCounted(false);
            sInstance.mCpuWakeLock.acquire();


 

make sure the screen stays on,再抓一個full鎖,防止屏幕半暗

代碼如下:

 

                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                        PowerManager.FULL_WAKE_LOCK, TAG + -screen);//保持srceen常亮
                sInstance.mScreenWakeLock.setReferenceCounted(false);
                sInstance.mScreenWakeLock.acquire();

起一下新進程

 

sending shutdown broadcast,發出廣播,通知各app該保存數據趕緊的,我要關機了

代碼如下:

 

        Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        mContext.sendOrderedBroadcastAsUser(intent,//發廣播
                UserHandle.ALL, null, br, mHandler, 0, null, null);

shutdown activity manager,關閉activity manager,即關閉AppOpsService,UsageStatsService,BatteryStatsService

 

注意:android L 與KK在關閉UsageStatsService上有所區別

代碼如下:

[ActivityManagerService.java]

 

final IActivityManager am =
            ActivityManagerNative.asInterface(ServiceManager.checkService(activity));
        if (am != null) {
            try {
                am.shutdown(MAX_BROADCAST_TIME);
            } catch (RemoteException e) {
            }
        }

shutdown package manager,保存app使用時間到 disk裡,這是android L新增的功能。

 

代碼如下:

[PackageManagerService.java]

 

        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService(package);
        if (pm != null) {
            pm.shutdown();
        }

show shutdown animation,播放關機動畫了

 

代碼如下:

 

    private static void showShutdownAnimation() {
        /*
         * When boot completed, service.bootanim.exit property is set to 1.
         * Bootanimation checks this property to stop showing the boot animation.
         * Since we use the same code for shutdown animation, we
         * need to reset this property to 0. If this is not set to 0 then shutdown
         * will stop and exit after displaying the first frame of the animation
         */
        SystemProperties.set(service.bootanim.exit, 0);

        SystemProperties.set(ctl.start, bootanim);//也是用bootanim進程,跟開關動畫一樣的方式。
    }

shutdown radio[NFC,BT,MODEM],注意這裡關閉modem這塊與andorid KK的不一樣。

 

代碼如下:

shutdownRadios(MAX_RADIO_WAIT_TIME);


shutdown MountService,特別這裡會導致關機失敗。

代碼如下:

 

        // Set initial variables and time out time.
        mActionDone = false;
        final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
        synchronized (mActionDoneSync) {
            try {
                final IMountService mount = IMountService.Stub.asInterface(
                        ServiceManager.checkService(mount));
                if (mount != null) {
                    mount.shutdown(observer);
                } else {
                    Log.w(TAG, MountService unavailable for shutdown);
                }
            } catch (Exception e) {
                Log.e(TAG, Exception during MountService shutdown, e);
            }
            while (!mActionDone) {
                long delay = endShutTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, Shutdown wait timed out);
                    break;
                }
                try {
                    mActionDoneSync.wait(delay);
                } catch (InterruptedException e) {
                }
            }
        }

走完上層關機流程,下面就要執行關機動作了。

 

代碼如下:

 

 

    public static void rebootOrShutdown(boolean reboot, String reason) {
        deviceRebootOrShutdown(reboot, reason);
        if (reboot) {
            Log.i(TAG, Rebooting, reason:  + reason);
            PowerManagerService.lowLevelReboot(reason);//重啟
            Log.e(TAG, Reboot failed, will attempt shutdown instead);
        } else if (SHUTDOWN_VIBRATE_MS > 0) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator();
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
            } 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();//關機
    }

從代碼上看始終會走到lowLevelShutdown(),但如果是重啟就不會,lowLevelReboot()就停止了。

 

 

lowLevelShutdown()與lowLevelReboot()都在PowerManagerService.java實現,其實都只是設置一個屬性:SystemProperties.set(sys.powerctl, xxx);

正是這個動作觸發關機流程往下走,這涉及到init進程的4大功能,請參考我的另一篇文章Android的init進程

 

sys.powerctl屬性觸發開關在init.rc定義

 

on property:sys.powerctl=*
    powerctl ${sys.powerctl}

我們來解讀這句話,on property:sys.powerctl=*表示當屬性sys.powerctl設置為任何值是都會跑到這裡,觸發動作是powerctl ${sys.powerctl},這個動作的意思是調用powerctl指令,並把sys.powerctl的值傳給它。powerctl指令在init進程會執行。

 

從下面的表可知,powerctl對應的操作是do_powerctl

[system/core/init/keywords.h]

 

KEYWORD(powerctl,    COMMAND, 1, do_powerctl)

do_powerctl的實現

 

代碼如下:

[system/core/init/builtins.c]

 

int do_powerctl(int nargs, char **args)
{
....

    return android_reboot(cmd, 0, reboot_target);
}

它調用android_reboot()函數,實現如下:

 

 

[system/core/libcutils/android_reboot.c]

 

int android_reboot(int cmd, int flags UNUSED, char *arg)
{
    int ret;

    sync();
    remount_ro();

    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 = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;

        default:
            ret = -1;
    }

    return ret;
}

從這裡看出它的主要工作:

 

sync() 回寫block設備的內容,這是阻塞型操作。

remount_ro() 把block設備remount成ro,這裡有個關鍵LOG:SysRq : Emergency Remount R/O,這是在logkit所能看到的最後一句LOG,因為remount成ro了,後面的LOG要通過last kmsg技術導出來。

reboot()或者syscall(__NR_reboot....,這點與android KK不同,這邊直接用syscall功能,KK則通過匯編。

後面syscall(__NR_reboot...知道,直接調用了linux的__NR_reboot系統調用,這個系統調用會跑哪裡?後面會講。

reboot()這個函數實現如下:

[bionic/libc/bionic/reboot.cpp]

 

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

調用了__reboot,它在匯編實現 如下:

 

[bionic/libc/arch-arm/syscalls/__reboot.S]

 

ENTRY(__reboot)
    mov     ip, r7
    ldr     r7, =__NR_reboot//也跑到__NR_reboot系統調用
    swi     #0
    mov     r7, ip
    cmn     r0, #(MAX_ERRNO + 1)
    bxls    lr
    neg     r0, r0
    b       __set_errno_internal
END(__reboot)

 

 

__NR_reboot對應的內核入口在哪裡?

如下:

[bionic/libc/kernel/uapi/asm-generic/unistd.h]

 

 #define __NR_reboot 142

它在內核入口如下:

 

注:bionic/libc/kernel/uapi/asm-generic/unistd.h與kernel/include/uapi/asm-generic/unistd.h是對應的,方便以後代碼追蹤

[kernel/include/uapi/asm-generic/unistd.h]

 

#define __NR_reboot 142
__SYSCALL(__NR_reboot, sys_reboot)

 

 

__NR_reboot 映射到 sys_reboot

grep 下sys_reboot 找不到,其實在這裡

用SYSCALL_DEFINE定義

[kernel/kernel/sys.c]

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
		void __user *, arg)
{
	struct pid_namespace *pid_ns = task_active_pid_ns(current);
	char buffer[256];
	int ret = 0;

	/* We only trust the superuser with rebooting the system. */
	if (!ns_capable(pid_ns->user_ns, 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;

	/*
	 * If pid namespaces are enabled and the current task is in a child
	 * pid_namespace, the command is handled by reboot_pid_ns() which will
	 * call do_exit().
	 */
	ret = reboot_pid_ns(pid_ns, cmd);
	if (ret)
		return ret;

	/* 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] = '';

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

 

有很多分支,我們只關心kernel_power_off()和kernel_restart()兩函數就行

如下:

 

void kernel_power_off(void)
{
	kernel_shutdown_prepare(SYSTEM_POWER_OFF);//關閉外設
	if (pm_power_off_prepare)
		pm_power_off_prepare();
	migrate_to_reboot_cpu();
	syscore_shutdown();//關閉syscore
	printk(KERN_EMERG Power down.
);//關鍵打印
	kmsg_dump(KMSG_DUMP_POWEROFF);
	machine_power_off();
}
void kernel_restart(char *cmd)
{
	kernel_restart_prepare(cmd);//關閉外設
	migrate_to_reboot_cpu();
	syscore_shutdown();//關閉syscore
	if (!cmd)
		printk(KERN_EMERG Restarting system.
);//關鍵打印
	else
		printk(KERN_EMERG Restarting system with command '%s'.
, cmd);
	kmsg_dump(KMSG_DUMP_RESTART);
	machine_restart(cmd);
}

都執行XX_prepare()函數

 

 

static void kernel_shutdown_prepare(enum system_states state)
{
	blocking_notifier_call_chain(&reboot_notifier_list,
		(state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL);
	system_state = state;
	usermodehelper_disable();
	device_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();
}

除了前面不同,都調用了device_shutdown()函數,關閉外設。

 

machine_power_off() machine_resestart()函數實現

 

void machine_power_off(void)
{
	preempt_disable();
	smp_send_stop();

	if (pm_power_off)
		pm_power_off();//關機
}

void machine_restart(char *cmd)
{
	preempt_disable();
	smp_send_stop();

	/* Flush the console to make sure all the relevant messages make it
	 * out to the console drivers */
	arm_machine_flush_console();

	arm_pm_restart(reboot_mode, cmd);//重啟

	/* Give a grace period for failure to restart of 1s */
	mdelay(1000);

	/* Whoops - the platform was unable to reboot. Tell the user! */
	printk(Reboot failed -- System halted
);
	local_irq_disable();
	while (1);
}

pm_power_offf() arm_pm_restart()都是一個函數指針

 

賦值如下:

[kernel/drivers/power/reset/msm-poweroff.c]

 

pm_power_off = do_msm_poweroff;
arm_pm_restart = do_msm_restart;

高通平台的關機代碼與之前有所不同,現在文件msm-poweroff.c以前是restart.c。

 

do_msm_poweroff()與do_msm_restart()實現如下:

 

static void do_msm_poweroff(void)
{
....
	pr_notice(Powering off the SoC
);//關鍵打印
#ifdef CONFIG_MSM_DLOAD_MODE
	set_dload_mode(0);//關機,所以dloadmode是0
#endif
	qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN);//配置PMIC,是關機
.....
	/* MSM initiated power off, lower ps_hold */
	__raw_writel(0, msm_ps_hold);//拉 PS_HOLD,執行關機動作。

	mdelay(10000);
	pr_err(Powering off has failed
);
	return;
}

static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd)
...

	pr_notice(Going down for restart now
);//關鍵打印

	msm_restart_prepare(cmd);//重啟准備前動作

#ifdef CONFIG_MSM_DLOAD_MODE
	/*
	 * Trigger a watchdog bite here and if this fails,
	 * device will take the usual restart path.
	 */

	if (WDOG_BITE_ON_PANIC && in_panic)
		msm_trigger_wdog_bite();
#endif

....
	halt_spmi_pmic_arbiter();
	__raw_writel(0, msm_ps_hold);//拉PS_HOLD重啟

	mdelay(10000);
}

msm_restart_prepare()實現

 

 

static void msm_restart_prepare(const char *cmd)
{
#ifdef CONFIG_MSM_DLOAD_MODE

	/* Write download mode flags if we're panic'ing
	 * Write download mode flags if restart_mode says so
	 * Kill download mode if master-kill switch is set
	 */

	set_dload_mode(download_mode &&
			(in_panic || restart_mode == RESTART_DLOAD));//設置dload
#endif

	/* Hard reset the PMIC unless memory contents must be maintained. */
	if (get_dload_mode() || (cmd != NULL && cmd[0] != ''))
		qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);//設置PIMC為熱重啟
	else
		qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET);//設置PIMC為硬重啟

	if (cmd != NULL) {
		if (!strncmp(cmd, bootloader, 10)) {
			__raw_writel(0x77665500, restart_reason);//寫一些東東到IMEM,用於bootloader,recovery等
		} else if (!strncmp(cmd, recovery, 8)) {
			__raw_writel(0x77665502, restart_reason);
		} else if (!strcmp(cmd, rtc)) {
			__raw_writel(0x77665503, restart_reason);
		} else if (!strncmp(cmd, oem-, 4)) {
			unsigned long code;
			int ret;
			ret = kstrtoul(cmd + 4, 16, &code);
			if (!ret)
				__raw_writel(0x6f656d00 | (code & 0xff),
					     restart_reason);
		} else if (!strncmp(cmd, edl, 3)) {
			enable_emergency_dload_mode();
		} else {
			__raw_writel(0x77665501, restart_reason);
		}
	}

.....

}

 

 

do_msm_poweroff()與do_msm_restart()都設置了dload,PMIC,唯一不同的是do_msm_restart()裡多了一個__raw_writel的動作,即reason寫入IMEM,目的在於重啟進入sbl1時判斷應該進入那種模式,如我們開發用的bootloader模式,恢復出廠設置的recovery模式等。


 



 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved