[Android][Reboot/Shutdown] 重启/关机 分析

在Android系统中,sys.powerctl 是一个系统属性,用于控制设备的电源状态。通过设置 sys.powerctl 属性,可以触发设备的关机或重启操作。下面详细介绍 sys.powerctl 是如何实现重启(reboot)的。

实现原理

  1. 设置系统属性: 当你通过 SystemProperties.set("sys.powerctl", "reboot") 设置 sys.powerctl 属性时,实际上是在向内核传递一个指令,告诉内核应该执行重启操作。

  2. 内核处理: 内核接收到这个系统属性的更改后,会触发相应的内核函数来处理重启请求。具体来说,内核会调用 reboot 系统调用,从而实现设备的重启。

  3. reboot 系统调用: reboot 是一个 Linux 内核提供的系统调用,用于重新启动或关闭计算机。在 Android 中,reboot 系统调用同样用于控制设备的重启或关机。

代码实现

下面是一个简化的示例,展示了如何在 Android 应用中通过设置 sys.powerctl 属性来实现重启:

java 复制代码
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 属性的更改会被内核监听并处理。具体的处理逻辑如下:

  1. 系统属性更改: 当 sys.powerctl 属性被更改时,内核中的相应代码会捕获这个更改。

  2. 触发重启: 内核会根据属性的值来决定下一步的动作。如果值为 reboot,则会调用 reboot 系统调用来重启设备。

内核代码示例

在 Linux 内核中,sys.powerctl 属性的处理逻辑通常位于 /drivers/staging/android/power.c 文件中。下面是一个简化的示例,展示了如何在内核中处理 sys.powerctl 属性的更改:

objectivec 复制代码
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 属性的操作。

注意事项

  1. 权限要求:

    1. 设置 sys.powerctl 属性通常需要 root 权限。因此,只有系统应用或具有 root 权限的应用才能执行这样的操作。
  2. 安全性:

    1. 直接通过系统属性重启设备可能会带来安全风险,因此在生产环境中应谨慎使用。
  3. 兼容性:

    1. 不同的 Android 设备和内核版本可能有不同的实现细节。因此,上述代码示例仅供参考,具体实现可能因设备而异。

总结

通过设置 sys.powerctl 属性为 reboot 可以触发设备的重启操作。这一过程涉及到应用程序设置系统属性、内核捕获并处理属性更改,最终通过 reboot 系统调用实现设备重启。在实际开发中,应注意权限和安全问题,并确保代码的稳定性和兼容性。

对应到目前在研的Android项目的源码中: frameworks/base/ services/core/java/com/android/server/power/ShutdownThread.java

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

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!");
    }
复制代码
相关推荐
Winston Wood6 分钟前
Android Parcelable和Serializable的区别与联系
android·序列化
清风徐来辽11 分钟前
Android 项目模型配置管理
android
帅得不敢出门37 分钟前
Gradle命令编译Android Studio工程项目并签名
android·ide·android studio·gradlew
problc1 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
帅得不敢出门12 小时前
安卓设备adb执行AT指令控制电话卡
android·adb·sim卡·at指令·电话卡
我又来搬代码了13 小时前
【Android】使用productFlavors构建多个变体
android
德育处主任15 小时前
Mac和安卓手机互传文件(ADB)
android·macos
芦半山15 小时前
Android“引用们”的底层原理
android·java
迃-幵16 小时前
力扣:225 用队列实现栈
android·javascript·leetcode
大风起兮云飞扬丶16 小时前
Android——从相机/相册获取图片
android