Android 的电源管理也是很重要的一部分。比如在待机的时候关掉不用的设备,timeout之后的屏幕和键盘背光的关闭,用户操作的时候该打开多少设备等等,这些都直接关系到产品的待机时间,以及用户体验。
一,电源管理相关文件
1,框架层文件
/frameworks/base/services/java/com/android/server/PowerManagerService.java
/frameworks/base/services/jni/com_android_server_PowerManagerService.cpp
/frameworks/base/services/java/com/android/server/LightsService.java
/frameworks/base/services/jni/com_android_server_LightsService.cpp
/frameworks/base/services/java/com/android/server/BatteryService.java
/frameworks/base/services/jni/com_android_server_BatteryService.cpp
/frameworks/base/core/java/android/os/Power.java
/frameworks/base/core/jni/android_os_Power.cpp
/frameworks/base/core/java/android/os/PowerManager.java
/frameworks/base/services/powermanager/IPowerManager.cpp
/frameworks/base/core/java/android/os/IPowerManager.aidl
/frameworks/base/core/java/android/os/LocalPowerManager.java
/frameworks/base/services/jni/com_android_server_InputManager.cpp
/hardware/libhardware_legacy/power/power.c
2,驱动层文件:
kernel/kernel/power/main.c
kernel/power/earlysuspend.c
kernel/kernel/power/suspend.c
kernel/kernel/power/wakelock.c
kernel/kernel/power/userwakelock.c
二,电源管理系统总体流程
下图从应用层-- >框架层-- >抽象层-- >内核层 简单描述了电源管理系统的睡眠与唤醒代码流程。
下图PowerManagerService的处理过程,该服务是整个电源管理系统的核心服务。
三,电源管理系统机制分析
接下来我们将以上图的框架结构为主线,将进行详细地从最上层到最底层的跟踪。本文的主旨主要就是读者从Android最上层(Java写的应用程序)一步一步的往下跟进,经过Java、C++和C语言写的Framework层、JNI层、HAL层最后到达android的最底层(Kernel层)。通过本文的阅读,您将对android的整体有更加深入、宏观的理解和把握。
1,框架层分析
Android框架层的电源管理主要在framework层实现,其中battery的管理包括充放电状态、电量显示等,但这部分暂不在调研范围之间。该部分调研的重点在于LCD以及相关设备LED状态的切换。相关代码见上述列表。
Android 的电源管理机制只要是通过锁和定时器来切换系统的状态,是系统的功耗降至最低。在应用层:android 提供了android.os.PowerManager类供应程序使用,用于控制设备的电源状态切换。在框架层 :是再java中通过JNI 访问C++函数->HAL层->sysfs文件系统->调用内核提供的支持来实现。
整个状态切换流程是:系统正常开机后进入到awake状态,backlight会从最亮慢慢调节到用户设定的亮度,系统screenoff timer(settings->sound and display-> display settings ->screen timeout)开始计时,在计时时间到之前,如果有任何的activity事件发生,如touchclick,keyboard pressed等事件,则将resetscreen off timer, 系统保持在awake状态.如果有应用程序在这段时间内申请了fullwake lock,那么系统也将保持在awake状态,除非用户按下powerkey.。在awake状态下如果电池电量低或者是用AC供电screenoff timer时间到并且选中Keepscreen on while pluged in选项,backlight会被强制调节到DIM的状态。如果screenoff timer时间到并且没有fullwake lock或者用户按了powerkey,那么系统状态将被切换到notification,并且调用所有已经注册的g_early_suspend_handlers函数,通常会把LCD和Backlight驱动注册成earlysuspend类型,如有需要也可以把别的驱动注册成earlysuspend,这样就会在第一阶段被关闭.接下来系统会判断是否有partialwake lock acquired, 如果有则等待其释放,在等待的过程中如果有useractivity事件发生,系统则马上回到awake状态;如果没有partialwake lock acquired, 则系统会马上调用函数pm_suspend关闭其它相关的驱动,让CPU进入休眠状态.系统在Sleep状态时如果检测到任何一个wakeupsource,则CPU会从sleep状态被唤醒,并且调用相关的驱动的resume函数,接下来马上调用前期注册的earlysuspend驱动的resume函数,最后系统状态回到awake状态.
上图中,我们可以看到power manager的核心代码在PowerManagerService.java中,该文件通过利用PowerManager.java提供的类,android_os_Power.cpp提供的一些本地方法以及power.c对底层的调用,完成了android系统power manager的各自服务。
在应用程序框架层中,PowerManager类是面向上层应用程序的接口类,提供了Wake Lock机制(同时也是睡眠唤醒子系统)的基本接口(唤醒锁的获取和释放)。上层应用程序通过调用这些接口,实现对系统电源状态的监控。PowerManager类通过IBinder这种Android中特有的通信模式,与PowerManagerService 类进行通信。PowerManagerService 是PowerManager 类中定义的接口的具体实现,并进一步调用Power 类来与下一层进行通信。PowerManagerService 类是WakeLock 机制在应用程序框架层的核心,他们对应用程调用PowerManager类接口时所传递的参数进行初步的分析和对应的设置,并管理一个唤醒锁队列,然后配合其他模块(例如WatchDog、BatteryService、ShutdownThread 等)的状态信息,做出决策,调用Power类的对应接口,最终通过JNI 接口,调用到硬件抽象层中的函数,对sysfs的用户接口进行操作,从而触发内核态实现的用。
在分析框架层相关文件之前,我们必须先清楚应用层相关文件之间的相互调用关系。
PowerManagerService.java ---- >PowerManagerService extends IPowerManager.Stub implements LocalPowerManager, Watchdog.Monitor{}
PowerManager.java---- >PowerManager{}---- >PowerManager(IPowerManagerservice, Handler handler){}
LocalPowerManager.java---- >interface LocalPowerManager{}
IPowerManager.aidl---- >interface IPowerManager{}
IPowerManager.cpp---- >class BpPowerManager : public BpInterface<IPowerManager>
首先分析:PowerManager.java这个类是框架层留给应用层的接口,PowerManager.java 电源管理工具类,其为一个公共类提供了较多的公共接口。获取对象方法:PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);应用层可以通过pm对象进行相关的管理操作。根据上面分析的相互调用关系可知,获取PowerManager在getSystemService(Context.POWER_SERVICE)获取对象的时候是通过构造函数PowerManager(IPowerManagerservice, Handler handler){}来创建的,而此处IPowerManager 则是创建PowerManager实例的核心,而IPowerManager则是由PowerManagerService实现,所以PowerManager大部分方法实质还是有PowerManagerService来实现的,清楚了这点后面的分析要简单很多。下面看下PowerManager.java的源码:
public class PowerManager
{
private static final String TAG = "PowerManager";
private static final int WAKE_BIT_CPU_STRONG = 1;
private static final int WAKE_BIT_CPU_WEAK = 2;
private static final int WAKE_BIT_SCREEN_DIM = 4;
private static final int WAKE_BIT_SCREEN_BRIGHT = 8;
private static final int WAKE_BIT_KEYBOARD_BRIGHT = 16;
private static final int WAKE_BIT_PROXIMITY_SCREEN_OFF = 32;
private static final int LOCK_MASK = WAKE_BIT_CPU_STRONG
| WAKE_BIT_CPU_WEAK
| WAKE_BIT_SCREEN_DIM
| WAKE_BIT_SCREEN_BRIGHT
| WAKE_BIT_KEYBOARD_BRIGHT
| WAKE_BIT_PROXIMITY_SCREEN_OFF;
public static final int PARTIAL_WAKE_LOCK = WAKE_BIT_CPU_STRONG;
public static final int FULL_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT
| WAKE_BIT_KEYBOARD_BRIGHT;
@Deprecated
public static final int SCREEN_BRIGHT_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT;
public static final int SCREEN_DIM_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_DIM;
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = WAKE_BIT_PROXIMITY_SCREEN_OFF;
public static final int WAIT_FOR_PROXIMITY_NEGATIVE = 1;
public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
public static final int ON_AFTER_RELEASE = 0x20000000;
public class WakeLock
{
static final int RELEASE_WAKE_LOCK = 1;
Runnable mReleaser = new Runnable() {
public void run() {
release();
}
};
int mFlags;
String mTag;
IBinder mToken;
int mCount = 0;
boolean mRefCounted = true;
boolean mHeld = false;
WorkSource mWorkSource;
WakeLock(int flags, String tag)
{
switch (flags & LOCK_MASK) {
case PARTIAL_WAKE_LOCK:
case SCREEN_DIM_WAKE_LOCK:
case SCREEN_BRIGHT_WAKE_LOCK:
case FULL_WAKE_LOCK:
case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
break;
default:
throw new IllegalArgumentException();
}
mFlags = flags;
mTag = tag;
mToken = new Binder();
}
public void setReferenceCounted(boolean value)
{
mRefCounted = value;
}
public void acquire()
{
synchronized (mToken) {
acquireLocked();
}
}
public void acquire(long timeout) {
synchronized (mToken) {
acquireLocked();
mHandler.postDelayed(mReleaser, timeout);
}
}
private void acquireLocked() {
if (!mRefCounted || mCount++ == 0) {
mHandler.removeCallbacks(mReleaser);
try {
mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource);
} catch (RemoteException e) {
}
mHeld = true;
}
}
public void release() {
release(0);
}
public void release(int flags) {
synchronized (mToken) {
if (!mRefCounted || --mCount == 0) {
mHandler.removeCallbacks(mReleaser);
try {
mService.releaseWakeLock(mToken, flags);
} catch (RemoteException e) {
}
mHeld = false;
}
if (mCount < 0) {
throw new RuntimeException("WakeLock under-locked " + mTag);
}
}
}
public boolean isHeld()
{
synchronized (mToken) {
return mHeld;
}
}
public void setWorkSource(WorkSource ws) {
synchronized (mToken) {
if (ws != null && ws.size() == 0) {
ws = null;
}
boolean changed = true;
if (ws == null) {
mWorkSource = null;
} else if (mWorkSource == null) {
changed = mWorkSource != null;
mWorkSource = new WorkSource(ws);
} else {
changed = mWorkSource.diff(ws);
if (changed) {
mWorkSource.set(ws);
}
}
if (changed && mHeld) {
try {
mService.updateWakeLockWorkSource(mToken, mWorkSource);
} catch (RemoteException e) {
}
}
}
}
public String toString() {
synchronized (mToken) {
return "WakeLock{"
+ Integer.toHexString(System.identityHashCode(this))
+ " held=" + mHeld + ", refCount=" + mCount + "}";
}
}
@Override
protected void finalize() throws Throwable
{
synchronized (mToken) {
if (mHeld) {
Log.wtf(TAG, "WakeLock finalized while still held: " + mTag);
try {
mService.releaseWakeLock(mToken, 0);
} catch (RemoteException e) {
}
}
}
}
}
/**
* Get a wake lock at the level of the flags parameter. Call
* {@link WakeLock#acquire() acquire()} on the object to acquire the
* wake lock, and {@link WakeLock#release release()} when you are done.
*
* {@samplecode
*PowerManager pm = (PowerManager)mContext.getSystemService(
* Context.POWER_SERVICE);
*PowerManager.WakeLock wl = pm.newWakeLock(
* PowerManager.SCREEN_DIM_WAKE_LOCK
* | PowerManager.ON_AFTER_RELEASE,
* TAG);
*wl.acquire();
* // ...
*wl.release();
* }
* @see WakeLock#acquire()
* @see WakeLock#release()
*/
public WakeLock newWakeLock(int flags, String tag)