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

浙公网安备 33010602011771号