一 简述
手机不同于PC,手机使用的是可移动电源,由于电源的电量有限,因此如何做到既让“马儿跑又要马儿不吃草”,电源管理系统尤为重要。
本文主要从上层应用入手,介绍安卓系统如何进行电源系统的管理和优化,提高手机的待机能力。
先看一下手机的几个耗电大户,分别是:
1)显示屏
2)AP的cpu和modem的cpu
3)其他的硬件外设
所以,电源优化一般是根据上述几个耗电环节进行优化。
1)显示屏一般技术有:自动熄屏、自动亮度调节、黑白显示等
2)CPU占用上 多核系统的hotplug、动态调频DVFS、PELT或者EAS调度策略、进程冻结技术等,在无交互时cpu自动休眠等
3)硬件外设 在SOC的设计中,可能为每个外设单元设置单独的电源子系统,在空闲状态时,可以自动休眠,设备不运行节省电源的消耗。
二 应用开发中牵扯到的电源管理部分
1) 使屏幕保持开启状态
在Activity 中使用 FLAG_KEEP_SCREEN_ON
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
在应用的布局 XML 文件中,使用 android:keepScreenOn 属性:
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
...
2) CPU占用
如果需要使 CPU 保持运行状态,需要 WAKE_LOCK来保持CPU一直处于唤醒状态
持有wakeLock可以避免CPU进入休眠状态
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"MyApp::MyWakelockTag");
wakeLock.acquire();
使用可使设备保持唤醒状态的广播接收器
WakefulBroadcastReceiver 是一种特殊类型的广播接收器,可以为应用或者service保持WAKE_LOCK,使用方法如下:
最后用completeWakefulIntent(intent);完成WAKE_LOCK的释放
public class MyWakefulReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Start the service, keeping the device awake while the service is
// launching. This is the Intent to deliver to the service.
Intent service = new Intent(context, MyIntentService.class);
startWakefulService(context, service);
}
}
public class MyIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
private NotificationManager notificationManager;
NotificationCompat.Builder builder;
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
// Do the work that requires your app to keep the CPU running.
// ...
// Release the wake lock provided by the WakefulBroadcastReceiver.
MyWakefulReceiver.completeWakefulIntent(intent);
}
}
三 PM在FW部分
在frameworks/base/services/core/java/com/android/server/power/目录下,PowerManagerService.java和ThermalManagerService.java
PMS主要调用底层HAL的两处
hardware/interfaces/power/
system/hardware/interfaces/suspend/
hardware/interfaces/power/ 主要是power总的控制开关,可以从相关aidl文件可以看出
interface IPower {
oneway void setMode(in Mode type, in boolean enabled);
boolean isModeSupported(in Mode type);
oneway void setBoost(in Boost type, in int durationMs);
boolean isBoostSupported(in Boost type);
}
而system/hardware/interfaces/suspend/主要来控制“挂起”相关,以及提供WAKE_LOCK相关控制,其HAL层主要是通过与内核文件的交互来完成相关控制
/sys/power/wake_lock
/sys/power/wake_unlock
/sys/power/wakeup_count
/sys/power/state
主要通过往/sys/power/state写"mem"后系统进入休眠
四 PM内核部分
Android的电源管理系统主要实现在内核的如下目录
kernel/power
drivers/base/power
arch/xxx/mach-xxx/
在我们往/sys/power/state写mem时进入如下函数
kernel/power/main.c
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
suspend_state_t state;
int error;
error = pm_autosleep_lock();
if (error)
return error;
if (pm_autosleep_state() > PM_SUSPEND_ON) {
error = -EBUSY;
goto out;
}
state = decode_state(buf, n);
if (state < PM_SUSPEND_MAX) {
if (state == PM_SUSPEND_MEM)
state = mem_sleep_current;
error = pm_suspend(state);
} else if (state == PM_SUSPEND_MAX) {
error = hibernate();
} else {
error = -EINVAL;
}
out:
pm_autosleep_unlock();
return error ? error : n;
}
power_attr(state);
通过调用pm_suspend进入suspend state并挂起系统
系统还可以自动挂起,主要实现在kernel/power/autosleep.c中,如果系统不能自动挂起的话,多半是wake_lock没有空导致
static void try_to_suspend(struct work_struct *work)
{
unsigned int initial_count, final_count;
if (!pm_get_wakeup_count(&initial_count, true))
goto out;
mutex_lock(&autosleep_lock);
if (!pm_save_wakeup_count(initial_count) ||
system_state != SYSTEM_RUNNING) {
mutex_unlock(&autosleep_lock);
goto out;
}
if (autosleep_state == PM_SUSPEND_ON) {
mutex_unlock(&autosleep_lock);
return;
}
if (autosleep_state >= PM_SUSPEND_MAX)
hibernate();
else
pm_suspend(autosleep_state);
mutex_unlock(&autosleep_lock);
if (!pm_get_wakeup_count(&final_count, false))
goto out;
/*
* If the wakeup occured for an unknown reason, wait to prevent the
* system from trying to suspend and waking up in a tight loop.
*/
if (final_count == initial_count)
schedule_timeout_uninterruptible(HZ / 2);
out:
queue_up_suspend_work();
}