在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 downVibrator 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 powerLog.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!");}