[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!");
    }
复制代码
相关推荐
一起搞IT吧2 小时前
相机Camera日志实例分析之五:相机Camx【萌拍闪光灯后置拍照】单帧流程日志详解
android·图像处理·数码相机
浩浩乎@2 小时前
【openGLES】安卓端EGL的使用
android
Kotlin上海用户组4 小时前
Koin vs. Hilt——最流行的 Android DI 框架全方位对比
android·架构·kotlin
zzq19964 小时前
Android framework 开发者模式下,如何修改动画过度模式
android
木叶丸4 小时前
Flutter 生命周期完全指南
android·flutter·ios
阿幸软件杂货间5 小时前
阿幸课堂随机点名
android·开发语言·javascript
没有了遇见5 小时前
Android 渐变色整理之功能实现<二>文字,背景,边框,进度条等
android
没有了遇见6 小时前
Android RecycleView 条目进入和滑出屏幕的渐变阴影效果
android
站在巨人肩膀上的码农6 小时前
去掉长按遥控器power键后提示关机、飞行模式的弹窗
android·安卓·rk·关机弹窗·power键·长按·飞行模式弹窗
呼啦啦--隔壁老王7 小时前
屏幕旋转流程
android