Android 关机
Android Framework层Power键关机流程(二,关机流程)
Android 关机(reboot)流程 -- "sys.powerctl"
一.framework
一.framework
1.1.按钮关机 mWindowManagerFuncs实现接口
frameworks\base\services\core\java\com\android\server\policy\GlobalActions.java
@Override public void onPress() { boolean isBox = "box".equals(SystemProperties.get("ro.target.product")); boolean notUseLegeacyWakeupRestartPlatform = "rk3399".equals(SystemProperties.get("ro.board.platform")); if(isBox && !notUseLegeacyWakeupRestartPlatform){ wakeup_restart_set(); Log.d(TAG,"don't shutdown here,only go to sleep for box!"); mPowerManager.goToSleep(SystemClock.uptimeMillis()); }else{ // shutdown by making sure radio and power are handled accordingly. mWindowManagerFuncs.shutdown(false /* confirm */); } } }
1.2.回调接口
frameworks\base\core\java\android\view\WindowManagerPolicy.java
/** * Interface for calling back in to the window manager that is private * between it and the policy. */ public interface WindowManagerFuncs { public void shutdown(boolean confirm);
1.3.实现接口 shutdown方法
frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java
// Called by window manager policy. Not exposed externally. @Override public void shutdown(boolean confirm) { ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); }
1.4.关机流程
frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java
/** *请求清除关闭,等待子系统清除它们的 *状态等。必须从其UI中的环形线程中调用 *显示。 * @param context用于显示关机进度对话框的上下文。 * @param原因代码传递给android_reboot()(例如“userrequested”),或null。 * @param confirm true如果在关闭之前需要用户确认。 */ public static void shutdown(final Context context, String reason, boolean confirm) { mReboot = false; mRebootSafeMode = false; mReason = reason; shutdownInner(context, confirm); }
//shutdownInner 主要还是看 beginShutdownSequence这个方法
static void shutdownInner(final Context context, boolean confirm) { // ensure that only one thread is trying to power down. // any additional calls are just returned synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Request to shutdown already running, returning."); return; } } final int longPressBehavior = context.getResources().getInteger( com.android.internal.R.integer.config_longPressOnPowerBehavior); final int resourceId = mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_confirm : (longPressBehavior == 2 ? com.android.internal.R.string.shutdown_confirm_question : com.android.internal.R.string.shutdown_confirm); Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); if (confirm) { final CloseDialogReceiver closer = new CloseDialogReceiver(context); if (sConfirmDialog != null) { sConfirmDialog.dismiss(); } sConfirmDialog = new AlertDialog.Builder(context) .setTitle(mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_title : com.android.internal.R.string.power_off) .setMessage(resourceId) .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { beginShutdownSequence(context); } }) .setNegativeButton(com.android.internal.R.string.no, null) .create(); closer.dialog = sConfirmDialog; sConfirmDialog.setOnDismissListener(closer); sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); sConfirmDialog.show(); } else { beginShutdownSequence(context); } }
//beginShutdownSequence 对话框 关机动画
// Used for shutdownanimation private static final String SYSTEM_SHUTDOWNANIMATION_FILE = "/system/media/shutdownanimation.zip"; private static void beginShutdownSequence(Context context) { synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Shutdown sequence already running, returning."); return; } sIsStarted = true; } // Throw up a system dialog to indicate the device is rebooting / shutting down. ProgressDialog pd = new ProgressDialog(context); // Path 1: Reboot to recovery for update // Condition: mReason == REBOOT_RECOVERY_UPDATE // // Path 1a: uncrypt needed // Condition: if /cache/recovery/uncrypt_file exists but // /cache/recovery/block.map doesn't. // UI: determinate progress bar (mRebootHasProgressBar == True) // // * Path 1a is expected to be removed once the GmsCore shipped on // device always calls uncrypt prior to reboot. // // Path 1b: uncrypt already done // UI: spinning circle only (no progress bar) // // Path 2: Reboot to recovery for factory reset // Condition: mReason == REBOOT_RECOVERY // UI: spinning circle only (no progress bar) // // Path 3: Regular reboot / shutdown // Condition: Otherwise // UI: spinning circle only (no progress bar) if (PowerManager.REBOOT_RECOVERY_UPDATE.equals(mReason)) { // We need the progress bar if uncrypt will be invoked during the // reboot, which might be time-consuming. mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists() && !(RecoverySystem.BLOCK_MAP_FILE.exists()); pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title)); if (mRebootHasProgressBar) { pd.setMax(100); pd.setProgress(0); pd.setIndeterminate(false); pd.setProgressNumberFormat(null); pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setMessage(context.getText( com.android.internal.R.string.reboot_to_update_prepare)); } else { pd.setIndeterminate(true); pd.setMessage(context.getText( com.android.internal.R.string.reboot_to_update_reboot)); } } else if (PowerManager.REBOOT_RECOVERY.equals(mReason)) { // Factory reset path. Set the dialog message accordingly. pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title)); pd.setMessage(context.getText( com.android.internal.R.string.reboot_to_reset_message)); pd.setIndeterminate(true); } else { 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(); sInstance.mProgressDialog = pd; sInstance.mContext = context; sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); // make sure we never fall asleep again sInstance.mCpuWakeLock = null; try { sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); sInstance.mCpuWakeLock.setReferenceCounted(false); sInstance.mCpuWakeLock.acquire(); } catch (SecurityException e) { Log.w(TAG, "No permission to acquire wake lock", e); sInstance.mCpuWakeLock = null; } // also make sure the screen stays on for better user experience sInstance.mScreenWakeLock = null; if (sInstance.mPowerManager.isScreenOn()) { try { sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); sInstance.mScreenWakeLock.setReferenceCounted(false); sInstance.mScreenWakeLock.acquire(); } catch (SecurityException e) { Log.w(TAG, "No permission to acquire wake lock", e); sInstance.mScreenWakeLock = null; } } // start the thread that initiates shutdown sInstance.mHandler = new Handler() { }; sInstance.start(); }
run
public void run() { //一大堆代码 if (checkAnimationFileExist()) {//跑开机动画 start_shutdownanim(); thaw_orien_shutdownanim(); } rebootOrShutdown(mContext, mReboot, mReason); }
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { if (reboot) { Log.i(TAG, "Rebooting, reason: " + reason); PowerManagerService.lowLevelReboot(reason); Log.e(TAG, "Reboot failed, will attempt shutdown instead"); reason = null; } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) { // vibrate before shutting down Vibrator vibrator = new SystemVibrator(context); 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(reason); }
1.5.PowerManagerService 设置关机属性
frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
public static void lowLevelShutdown(String reason) { if (reason == null) { reason = ""; } SystemProperties.set("sys.powerctl", "shutdown," + reason); }
二.kernel
2.1.system/core/rootdir/init.rc
on property:sys.powerctl=* powerctl ${sys.powerctl}
2.2.system\core\init\builtins.cpp 解析属性 showdown ANDROID_RB_POWEROFF
static int do_powerctl(const std::vector<std::string>& args) { const char* command = args[1].c_str(); int len = 0; unsigned int cmd = 0; const char *reboot_target = ""; void (*callback_on_ro_remount)(const struct mntent*) = NULL; if (strncmp(command, "shutdown", 8) == 0) { cmd = ANDROID_RB_POWEROFF; len = 8; } else if (strncmp(command, "reboot", 6) == 0) { cmd = ANDROID_RB_RESTART2; len = 6; } else { ERROR("powerctl: unrecognized command '%s'\n", command); return -EINVAL; } if (command[len] == ',') { if (cmd == ANDROID_RB_POWEROFF && !strcmp(&command[len + 1], "userrequested")) { // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED. // Run fsck once the file system is remounted in read-only mode. callback_on_ro_remount = unmount_and_fsck; } else if (cmd == ANDROID_RB_RESTART2) { reboot_target = &command[len + 1]; } } else if (command[len] != '\0') { ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]); return -EINVAL; } std::string timeout = property_get("ro.build.shutdown_timeout"); unsigned int delay = 0; if (android::base::ParseUint(timeout.c_str(), &delay) && delay > 0) { Timer t; // Ask all services to terminate. ServiceManager::GetInstance().ForEachService( [] (Service* s) { s->Terminate(); }); while (t.duration() < delay) { ServiceManager::GetInstance().ReapAnyOutstandingChildren(); int service_count = 0; ServiceManager::GetInstance().ForEachService( [&service_count] (Service* s) { // Count the number of services running. // Exclude the console as it will ignore the SIGTERM signal // and not exit. // Note: SVC_CONSOLE actually means "requires console" but // it is only used by the shell. if (s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) { service_count++; } }); if (service_count == 0) { // All terminable services terminated. We can exit early. break; } // Wait a bit before recounting the number or running services. usleep(kTerminateServiceDelayMicroSeconds); } NOTICE("Terminating running services took %.02f seconds", t.duration()); } return android_reboot_with_callback(cmd, 0, reboot_target, callback_on_ro_remount); }
2.3.看android_reboot_with_callback 这个回调
int android_reboot_with_callback( int cmd, int flags __unused, const char *arg, void (*cb_on_remount)(const struct mntent*)) { int ret; remount_ro(cb_on_remount); 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; } int android_reboot(int cmd, int flags, const char *arg) { return android_reboot_with_callback(cmd, flags, arg, NULL); }