[Android][Reboot/Shutdown] 重启/关机 分析
在Android系统中,sys.powerctl
是一个系统属性,用于控制设备的电源状态。通过设置 sys.powerctl
属性,可以触发设备的关机或重启操作。下面详细介绍 sys.powerctl
是如何实现重启(reboot)的。
实现原理
-
设置系统属性: 当你通过
SystemProperties.set("sys.powerctl", "reboot")
设置sys.powerctl
属性时,实际上是在向内核传递一个指令,告诉内核应该执行重启操作。 -
内核处理: 内核接收到这个系统属性的更改后,会触发相应的内核函数来处理重启请求。具体来说,内核会调用
reboot
系统调用,从而实现设备的重启。 -
reboot 系统调用:
reboot
是一个 Linux 内核提供的系统调用,用于重新启动或关闭计算机。在 Android 中,reboot
系统调用同样用于控制设备的重启或关机。
代码实现
下面是一个简化的示例,展示了如何在 Android 应用中通过设置 sys.powerctl
属性来实现重启:
import android.os.SystemProperties;
public class RebootManager {
public static void rebootWithReason(String reason) {
// 设置 sys.powerctl 属性为 "reboot" 并附带重启原因
SystemProperties.set("sys.powerctl", "reboot," + reason);
}
public static void performReboot() {
String reason = "User requested reboot";
rebootWithReason(reason);
}
}
内核处理
在内核层面,sys.powerctl
属性的更改会被内核监听并处理。具体的处理逻辑如下:
-
系统属性更改: 当
sys.powerctl
属性被更改时,内核中的相应代码会捕获这个更改。 -
触发重启: 内核会根据属性的值来决定下一步的动作。如果值为
reboot
,则会调用reboot
系统调用来重启设备。
内核代码示例
在 Linux 内核中,sys.powerctl
属性的处理逻辑通常位于 /drivers/staging/android/power.c
文件中。下面是一个简化的示例,展示了如何在内核中处理 sys.powerctl
属性的更改:
static int android_power_ctl(struct android_power *ap, char *buf, size_t len)
{
char *token;
char *reason;
token = strsep(&buf, ",");
if (token && !strcmp(token, "reboot")) {
reason = buf; // 获取重启原因
// 触发重启
do_reboot(reason);
} else if (token && !strcmp(token, "shutdown")) {
// 触发关机
do_shutdown();
}
return 0;
}
static ssize_t android_power_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", atomic_read(&power_state));
}
static ssize_t android_power_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
android_power_ctl(NULL, (char *)buf, count);
return count;
}
DEVICE_ATTR(power, S_IWUSR | S_IRUGO, android_power_show, android_power_store);
在这段代码中:
-
android_power_ctl
函数负责解析sys.powerctl
属性的值,并根据值执行相应的操作。 -
android_power_store
函数用于处理写入sys.powerctl
属性的操作。
注意事项
-
权限要求:
-
设置
sys.powerctl
属性通常需要 root 权限。因此,只有系统应用或具有 root 权限的应用才能执行这样的操作。
-
-
安全性:
-
直接通过系统属性重启设备可能会带来安全风险,因此在生产环境中应谨慎使用。
-
-
兼容性:
-
不同的 Android 设备和内核版本可能有不同的实现细节。因此,上述代码示例仅供参考,具体实现可能因设备而异。
-
总结
通过设置 sys.powerctl
属性为 reboot
可以触发设备的重启操作。这一过程涉及到应用程序设置系统属性、内核捕获并处理属性更改,最终通过 reboot
系统调用实现设备重启。在实际开发中,应注意权限和安全问题,并确保代码的稳定性和兼容性。
对应到目前在研的Android项目的源码中: frameworks/base/
services/core/java/com/android/server/power/ShutdownThread.java
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
String subsysProp;
subsysProp = SystemProperties.get("vendor.peripheral.shutdown_critical_list",
"ERROR");
//If we don't have the shutdown critical subsystem list we can't
//really do anything. Proceed with full system shutdown.
if (!subsysProp.equals("ERROR")) {
Log.i(TAG, "Shutdown critical subsyslist is :"+subsysProp+": ");
Log.i(TAG, "Waiting for a maximum of " +
(VENDOR_SUBSYS_MAX_WAIT_MS) + "ms");
String[] subsysList = subsysProp.split(" ");
int wait_count = 0;
boolean okToShutdown = true;
String subsysState;
int subsysListLength = subsysList.length;
do {
okToShutdown = true;
for (int i = 0; i < subsysListLength; i++) {
subsysState =
SystemProperties.get(
"vendor.peripheral." +
subsysList[i] +
".state",
"ERROR");
if(subsysState.equals("ONLINE")) {
//We only want to delay shutdown while
//one of the shutdown critical
//subsystems still shows as 'ONLINE'.
okToShutdown = false;
}
}
if (okToShutdown == false) {
SystemClock.sleep(VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS);
wait_count++;
}
} while (okToShutdown != true &&
wait_count < (VENDOR_SUBSYS_MAX_WAIT_MS/VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS));
if (okToShutdown != true) {
for (int i = 0; i < subsysList.length; i++) {
subsysState =
SystemProperties.get(
"vendor.peripheral." +
subsysList[i] +
".state",
"ERROR");
if(subsysState.equals("ONLINE")) {
Log.w(TAG, "Subsystem " + subsysList[i]+
"did not shut down within timeout");
}
}
} else {
Log.i(TAG, "Vendor subsystem(s) shutdown successful");
}
}
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);
}
frameworks/base/services/core/java/com/android/server/power/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.
*
* @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
*/
public static void lowLevelShutdown(String reason) {
if (reason == null) {
reason = "";
}
SystemProperties.set("sys.powerctl", "shutdown," + reason);
}
/**
* Low-level function to reboot the device. On success, this
* function doesn't return. If more than 20 seconds passes from
* the time a reboot is requested, this method returns.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
*/
public static void lowLevelReboot(String reason) {
if (reason == null) {
reason = "";
}
// If the reason is "quiescent", it means that the boot process should proceed
// without turning on the screen/lights.
// The "quiescent" property is sticky, meaning that any number
// of subsequent reboots should honor the property until it is reset.
if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
sQuiescent = true;
reason = "";
} else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
sQuiescent = true;
reason = reason.substring(0,
reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
}
if (reason.equals(PowerManager.REBOOT_RECOVERY)
|| reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
reason = "recovery";
}
if (sQuiescent) {
// Pass the optional "quiescent" argument to the bootloader to let it know
// that it should not turn the screen/lights on.
if (!"".equals(reason)) {
reason += ",";
}
reason = reason + "quiescent";
}
SystemProperties.set("sys.powerctl", "reboot," + reason);
try {
Thread.sleep(20 * 1000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
}