Platform: RK3368
OS: Android 6.0
Kernel: 3.10.0
想要禁止Android系统进入休眠,首先来了解一下Android系统提供的几个电源管理相关的配置,因为PowerManagerService会用到这些配置:
frameworks/base/core/res/res/values/config.xml
false
false
将配置的注释翻译一下大概意思:
config_powerDecoupleAutoSuspendModeFromDisplay
电源管理:指定是否将设备的自动挂起状态与显示器开/关状态分离。
如果为false,将在显示器打开之前调用autosuspend_disable ( )
并且在显示器关闭后将调用autosuspend_enable ( )。此模式为使用传统电源管理的设备提供最佳兼容性像
early suspend / late resume等功能。
如果为true,无论显示器是打开还是关闭,都会调用autosuspend _ display ( )和autosuspend _ enable ( )。此模式使电源管理器能够在显示器打开时暂停应用处理器。
该资源应设置为“true”,当指定了doz组件以最大限度地节省电能,但并非所有设备都支持时。
config_powerDecoupleInteractiveModeFromDisplay
电源管理:指定是否将设备的交互状态与显示器的开/关状态分离。
如果为false,将在显示器打开前调用setInteractive(…, true),并在显示器关闭后调用setInteractive(…, false)。
该模式为期望交互状态与显示状态相关联的设备提供最佳兼容性。
如果为true,不管显示器是打开还是关闭,将会独立调用setInteractive(…)。此模式使电源管理能够在显示器打开时减少时钟并禁用触控。
当指定了doz组件以最大限度地节省电能,但并非所有设备都支持时,该资源应设置为“真”。
代码位于frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java.
代码里面可以看到PowerManagerService对系统的电源管理配置读取:
mDecoupleHalAutoSuspendModeFromDisplayConfig = resources.getBoolean(
com.android.internal.R.bool.config_powerDecoupleAutoSuspendModeFromDisplay);
mDecoupleHalInteractiveModeFromDisplayConfig = resources.getBoolean(
com.android.internal.R.bool.config_powerDecoupleInteractiveModeFromDisplay);
在PowerManagerService一开始就初始化了两个wakelock,分别是mWakeLockSuspendBlocker和mDisplaySuspendBlocker,用于控制显示器开关的锁和控制系统挂起;
public PowerManagerService(Context context) {
......
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
通过查看/sys/power/wake_lock文件,可以看到这两个锁:
# cat /sys/power/wake_lock
PowerManagerService.Display PowerManagerService.WakeLocks
当Activity用户无操作活动超时,按下电源键,显示屏状态变化,电池状态变化,应用获得wakelock或者释放wakelock等等都会去更新PowerStateLocked从而影响挂起状态和交互状态。关于wakelock这里就不做过多的介绍了,将重点关注系统挂起相关的代码.
如果想实现关闭屏幕后系统不进入休眠,可以强制关闭自动挂起,加两个SystemProperties方便控制。
因为当前系统配置config_powerDecoupleAutoSuspendModeFromDisplay为false,所以当显示器状态变化时代码会通过onDisplayStateChange来设置自动挂起状态及交互状态,不会通过updateSuspendBlockerLocked来设置:
@Override
public void onDisplayStateChange(int state) {
// This method is only needed to support legacy display blanking behavior
// where the display's power state is coupled to suspend or to the power HAL.
// The order of operations matters here.
synchronized (mLock) {
if (mDisplayState != state) {
mDisplayState = state;
if (state == Display.STATE_OFF) {
if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
+ if (SystemProperties.getBoolean("ro.pms.interactive_mode", false)){
+ Slog.d(TAG, "enable interactive mode");
+ setHalInteractiveModeLocked(true);
+ } else {
setHalInteractiveModeLocked(false);
+ }
}
if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
+ if (!SystemProperties.getBoolean("ro.pms.auto_suspend", true)){
+ Slog.d(TAG, "disable auto suspend");
+ setHalAutoSuspendModeLocked(false);
+ } else {
setHalAutoSuspendModeLocked(true);
+ }
}
在onDisplayStateChange里面有两个很重要的函数,分别是setHalAutoSuspendModeLocked和setHalInteractiveModeLocked。对应的就是挂起状态和交互状态的设置,关系到系统是否挂起和cpu是否关闭(kill CPU),特别是多核心的soc。
private void setHalAutoSuspendModeLocked(boolean enable) {
if (enable != mHalAutoSuspendModeEnabled) {
if (DEBUG) {
Slog.d(TAG, "Setting HAL auto-suspend mode to " + enable);
}
mHalAutoSuspendModeEnabled = enable;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");
try {
nativeSetAutoSuspend(enable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
}
private void setHalInteractiveModeLocked(boolean enable) {
if (enable != mHalInteractiveModeEnabled) {
if (DEBUG) {
Slog.d(TAG, "Setting HAL interactive mode to " + enable);
}
mHalInteractiveModeEnabled = enable;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalInteractive(" + enable + ")");
try {
nativeSetInteractive(enable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
}
可以看到代码最后调用了JNI中的nativeSetAutoSuspend和nativeSetInteractive函数,来看看JNI代码实现:
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
if (gPowerModule) {
if (enable) {
ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");
gPowerModule->setInteractive(gPowerModule, true);
} else {
ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");
gPowerModule->setInteractive(gPowerModule, false);
}
}
}
static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
if (enable) {
ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off");
autosuspend_enable();
} else {
ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on");
autosuspend_disable();
}
}
nativeSetInteractive是通过调用power HAL来实现交互状态设置的。而nativeSetAutoSuspend是放在system core中实现的。所以需要将交互状态和自动挂起分开来分析了。
autosuspend_enable,autosuspend_disabled定义在system/core/libsuspend/autosuspend.c
int autosuspend_enable(void)
{
int ret;
ret = autosuspend_init();
if (ret) {
return ret;
}
ALOGV("autosuspend_enable\n");
if (autosuspend_enabled) {
return 0;
}
ret = autosuspend_ops->enable();
if (ret) {
return ret;
}
autosuspend_enabled = true;
return 0;
}
autosuspend_ops的实现实际上是通过写入"mem"或"on"到sysfs文件/sys/power/state文件中,让内核开始处理自动挂起的流程,网上有很多关于linux系统休眠相关的文章这里就不继续跟代码了。
system/core/libsuspend/autosuspend_earlysuspend.c
#define EARLYSUSPEND_SYS_POWER_STATE "/sys/power/state"
static const char *pwr_state_mem = "mem";
static const char *pwr_state_on = "on";
struct autosuspend_ops autosuspend_earlysuspend_ops = {
.enable = autosuspend_earlysuspend_enable,
.disable = autosuspend_earlysuspend_disable,
};
static int autosuspend_earlysuspend_enable(void)
{
char buf[80];
int ret;
ALOGV("autosuspend_earlysuspend_enable\n");
ret = write(sPowerStatefd, pwr_state_mem, strlen(pwr_state_mem));
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
goto err;
}
if (wait_for_earlysuspend) {
pthread_mutex_lock(&earlysuspend_mutex);
while (earlysuspend_state != EARLYSUSPEND_MEM) {
pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex);
}
pthread_mutex_unlock(&earlysuspend_mutex);
}
ALOGV("autosuspend_earlysuspend_enable done\n");
return 0;
err:
return ret;
}
在power HAL的实现中,一般是操作sysfs中的/sys/devices/system/cpu/cpuX/online文件来交互的。来看一下rk3368的power HAL。
hardware/rockchip/power/power.c
static void rk_power_set_interactive(struct power_module *module, int on)
{
......
sysfs_write("/sys/devices/system/cpu/cpu2/online", on ? "1" : "0");
sysfs_write("/sys/devices/system/cpu/cpu3/online", on ? "1" : "0");
sysfs_write("/sys/devices/system/cpu/cpu4/online", on ? "1" : "0");
sysfs_write("/sys/devices/system/cpu/cpu5/online", on ? "1" : "0");
sysfs_write("/sys/devices/system/cpu/cpu6/online", on ? "1" : "0");
sysfs_write("/sys/devices/system/cpu/cpu7/online", on ? "1" : "0");
}
struct power_module HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
module_api_version: POWER_MODULE_API_VERSION_0_2,
hal_api_version: HARDWARE_HAL_API_VERSION,
id: POWER_HARDWARE_MODULE_ID,
name: TARGET_BOARD_PLATFORM " Power HAL",
author: "Rockchip",
methods: &power_module_methods,
},
init: rk_power_init,
setInteractive: rk_power_set_interactive,
powerHint: rk_power_hint,
};
用户空间写“0”,“1”到/sys/devices/system/cpu/cpuX/online文件,内核就会做解析处理,看一下linux内核中的实现:
./drivers/base/cpu.c
static ssize_t __ref store_online(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct cpu *cpu = container_of(dev, struct cpu, dev);
int cpuid = cpu->dev.id;
int from_nid, to_nid;
ssize_t ret;
pr_info("%s %s\n", __func__, buf);
cpu_hotplug_driver_lock();
switch (buf[0]) {
case '0':
ret = cpu_down(cpuid);
if (!ret)
kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
break;
case '1':
from_nid = cpu_to_node(cpuid);
ret = cpu_up(cpuid);
......
return ret;
}
static DEVICE_ATTR(online, 0644, show_online, store_online);
可以看到用户空间写入’0’的时,会调用cpu_down去关闭CPU。cpu_down实现在kernel/cpu.c
int __ref cpu_down(unsigned int cpu)
{
int err;
cpu_maps_update_begin();
if (cpu_hotplug_disabled) {
err = -EBUSY;
goto out;
}
err = _cpu_down(cpu, 0);
out:
cpu_maps_update_done();
return err;
}
static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
{
......
__cpu_die(cpu);
/* CPU is completely dead: tell everyone. Too late to complain. */
cpu_notify_nofail(CPU_DEAD | mod, hcpu);
check_for_tasks(cpu);
out_release:
cpu_hotplug_done();
if (!err)
cpu_notify_nofail(CPU_POST_DEAD | mod, hcpu);
return err;
}
因为soc是ARM 64位处理器,所以会调用./arch/arm64/kernel/smp.c中的void __cpu_die(unsigned int cpu)
./arch/arm64/kernel/smp.c
void __cpu_die(unsigned int cpu)
{
pr_info("%s %d\n", __func__, cpu);
if (!wait_for_completion_timeout(&cpu_died, msecs_to_jiffies(5000))) {
pr_crit("CPU%u: cpu didn't die\n", cpu);
return;
}
#ifdef CONFIG_CPUQUIET_FRAMEWORK
if (system_state != SYSTEM_RUNNING)
#endif
pr_notice("CPU%u: shutdown\n", cpu);
/*
* Now that the dying CPU is beyond the point of no return w.r.t.
* in-kernel synchronisation, try to get the firwmare to help us to
* verify that it has really left the kernel before we consider
* clobbering anything it might still be using.
*/
if (!op_cpu_kill(cpu))
pr_warn("CPU%d may not have shut down cleanly\n", cpu);
}
static int op_cpu_kill(unsigned int cpu)
{
pr_info("%s %d\n", __func__, cpu);
/*
* If we have no means of synchronising with the dying CPU, then assume
* that it is really dead. We can only wait for an arbitrary length of
* time and hope that it's dead, so let's skip the wait and just hope.
*/
if (!cpu_ops[cpu]->cpu_kill)
return 1;
return cpu_ops[cpu]->cpu_kill(cpu);
}
cpu_ops是在内核启动时就初始化好的,代码在./arch/arm64/kernel/cpu_ops.c,rk3368的dts文件中定义了cpus里面cpu的enable-method = "psci"所以会用./arch/arm64/kernel/psci.c中的具体cpu_ops实现.
const struct cpu_operations cpu_psci_ops = {
.name = "psci",
#ifdef CONFIG_CPU_IDLE
.cpu_init_idle = cpu_psci_cpu_init_idle,
#endif
#ifdef CONFIG_SMP
.cpu_init = cpu_psci_cpu_init,
.cpu_prepare = cpu_psci_cpu_prepare,
.cpu_boot = cpu_psci_cpu_boot,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = cpu_psci_cpu_disable,
.cpu_die = cpu_psci_cpu_die,
.cpu_kill = cpu_psci_cpu_kill,
#endif
#endif
#ifdef CONFIG_ARM64_CPU_SUSPEND
.cpu_suspend = cpu_psci_cpu_suspend,
#endif
};
好啦!接下来就是cpu_kill了,会将cpu关闭。