Android電源管理機制剖析
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)
{
if (tag == null) {
throw new NullPointerException("tag is null in PowerManager.newWakeLock");
}
return new WakeLock(flags, tag);
}
public void userActivity(long when, boolean noChangeLights)
{
try {
mService.userActivity(when, noChangeLights);
} catch (RemoteException e) {
}
}
public void goToSleep(long time)
{
try {
mService.goToSleep(time);
} catch (RemoteException e) {
}
}
public void setBacklightBrightness(int brightness)
{
try {
mService.setBacklightBrightness(brightness);
} catch (RemoteException e) {
}
}
public int getSupportedWakeLockFlags()
{
try {
return mService.getSupportedWakeLockFlags();
} catch (RemoteException e) {
return 0;
}
}
public boolean isScreenOn()
{
try {
return mService.isScreenOn();
} catch (RemoteException e) {
return false;
}
}
public void reboot(String reason)
{
try {
mService.reboot(reason);
} catch (RemoteException e) {
}
}
private PowerManager()
{
}
public PowerManager(IPowerManager service, Handler handler)
{
mService = service;
mHandler = handler;
}
IPowerManager mService;
Handler mHandler;
}
該部分程式碼中宣告的內容並不多,成員變數中只有一些關於WakeLock喚醒鎖的標誌位,此處相關設定不做詳細分析,具體引數與對應的工作狀態間API。該內中聲明瞭幾個重要的成員方法。內部類WakeLock是整個喚醒鎖的核心。並提供了acquire()和release()操作。成員方法WakeLock newWakeLock(int flags, String tag)是建立喚醒鎖的核心方法,返回一個喚醒鎖物件。接下來幾個方法均有PowerManagerService.java去實現:userActivity(long
when, boolean noChangeLights)用來響應使用者操作事件,包括使用者點選後喚醒螢幕等;goToSleep(long time) 強制進入睡眠狀態,為使用者按下Power鍵後的操作,在com_android_server_InputManager.cpp中呼叫,其詳細操作見PMS中的實現;setBacklightBrightness(int brightness)設定背光操作;isScreenOn()判斷當前螢幕點亮狀態的介面,比較實用。
上述簡單的介紹了PowerManager.java,接下來著重分析PowerManagerService.java,該類負責電源管理方面的工作,作為系統基礎服務之一,簡稱PMS。PMS與系統其它服務均有互動,且與HAL層有著緊密的聯絡,所以PMS的整個系統更加複雜。
從PowerManagerService extends IPowerManager.Stub implementsLocalPowerManager, Watchdog.Monitor{}可以看出,PowerManagerService實現的介面以及繼承的類關係。
第一個實現的IPowerManager.Stub,即一個Binder的基礎實現,該實現完成了PMS的Binder通訊,即客戶端與服務端的通訊,見AIDL實現機制。此處為IPowerManager.aidl。
interface IPowerManager
{
void acquireWakeLock(int flags, IBinder lock, String tag, in WorkSource ws);
void updateWakeLockWorkSource(IBinder lock, in WorkSource ws);
void goToSleep(long time);
void goToSleepWithReason(long time, int reason);
void releaseWakeLock(IBinder lock, int flags);
void userActivity(long when, boolean noChangeLights);
void userActivityWithForce(long when, boolean noChangeLights, boolean force);
void clearUserActivityTimeout(long now, long timeout);
void setPokeLock(int pokey, IBinder lock, String tag);
int getSupportedWakeLockFlags();
void setStayOnSetting(int val);
void setMaximumScreenOffTimeount(int timeMs);
void preventScreenOn(boolean prevent);
boolean isScreenOn();
void reboot(String reason);
void crash(String message);
void setBacklightBrightness(int brightness);
void setAttentionLight(boolean on, int color);
}
該介面中聲明瞭AIDL公開的方法,應用層使用PMS的方法與PowerManager不一樣。
IPowerManager mPowerManagerService;=IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
該方法獲得PMS的本地代理,可呼叫PMS中在IPowerManager.aidl介面中公開的方法。
第二個實現的介面為LocalPowerManager.java
public interface LocalPowerManager {
// Note: be sure to update BatteryStats if adding or modifying event constants.
public static final int OTHER_EVENT = 0;
public static final int BUTTON_EVENT = 1;
public static final int TOUCH_EVENT = 2;
public static final int POKE_LOCK_IGNORE_TOUCH_EVENTS = 0x1;
public static final int POKE_LOCK_SHORT_TIMEOUT = 0x2;
public static final int POKE_LOCK_MEDIUM_TIMEOUT = 0x4;
public static final int POKE_LOCK_TIMEOUT_MASK = 0x6;
void goToSleep(long time);
// notify power manager when keyboard is opened/closed
void setKeyboardVisibility(boolean visible);
// when the keyguard is up, it manages the power state, and userActivity doesn't do anything.
void enableUserActivity(boolean enabled);
// the same as the method on PowerManager
void userActivity(long time, boolean noChangeLights, int eventType);
boolean isScreenOn();
void setScreenBrightnessOverride(int brightness);
void setButtonBrightnessOverride(int brightness);
}
另外一個實現的是Watchdog,此處不做功能性的分析。
PMS的建立在SystemServer中,有ServerThread執行緒建立,跟PMS有關的操作見下:
PowerManagerService power = null;
power = new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);
power.init(context, lights, ActivityManagerService.self(), battery);
Watchdog.getInstance().init(context, battery, power, alarm,ActivityManagerService.self());
power.systemReady();
接下來正式分析PMS系統。先看下其成員變數,重要變數已做註釋。
private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK
| PowerManager.SCREEN_DIM_WAKE_LOCK
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK
| PowerManager.FULL_WAKE_LOCK
| PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
// time since last state: time since last event:
// The short keylight delay comes from secure settings; this is the default.
private static final int SHORT_KEYLIGHT_DELAY_DEFAULT = 6000; // t+6 sec
private static final int MEDIUM_KEYLIGHT_DELAY = 15000; // t+15 sec
private static final int LONG_KEYLIGHT_DELAY = 6000; // t+6 sec
private static final int LONG_DIM_TIME = 7000; // t+N-5 sec
// How long to wait to debounce light sensor changes in milliseconds
private static final int LIGHT_SENSOR_DELAY = 2000;//光線感測器時延
// light sensor events rate in microseconds
private static final int LIGHT_SENSOR_RATE = 1000000;//光線感測器頻率
// For debouncing the proximity sensor in milliseconds
private static final int PROXIMITY_SENSOR_DELAY = 1000;//距離感測器時延
// trigger proximity if distance is less than 5 cm
private static final float PROXIMITY_THRESHOLD = 5.0f;//距離感測器距離範圍
// Cached secure settings; see updateSettingsValues()
private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT;//鍵盤燈短暫時延
// Default timeout for screen off, if not found in settings database = 15 seconds.
private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000;//預設螢幕超時時間,從Settings中獲取
// flags for setPowerState
private static final int SCREEN_ON_BIT = 0x00000001;
private static final int SCREEN_BRIGHT_BIT = 0x00000002;
private static final int BUTTON_BRIGHT_BIT = 0x00000004;
private static final int KEYBOARD_BRIGHT_BIT = 0x00000008;
private static final int BATTERY_LOW_BIT = 0x00000010;
// values for setPowerState
// SCREEN_OFF == everything off
private static final int SCREEN_OFF = 0x00000000;//螢幕滅掉,進入睡眠狀態
// SCREEN_DIM == screen on, screen backlight dim
private static final int SCREEN_DIM = SCREEN_ON_BIT;//螢幕滅掉,依然在工作狀態
// SCREEN_BRIGHT == screen on, screen backlight bright
private static final int SCREEN_BRIGHT = SCREEN_ON_BIT | SCREEN_BRIGHT_BIT;//螢幕亮,處於工作狀態
// SCREEN_BUTTON_BRIGHT == screen on, screen and button backlights bright
private static final int SCREEN_BUTTON_BRIGHT = SCREEN_BRIGHT | BUTTON_BRIGHT_BIT;//螢幕亮,按鍵燈亮
// SCREEN_BUTTON_BRIGHT == screen on, screen, button and keyboard backlights bright
private static final int ALL_BRIGHT = SCREEN_BUTTON_BRIGHT | KEYBOARD_BRIGHT_BIT;//按鍵燈亮,鍵盤燈亮
// used for noChangeLights in setPowerState()
private static final int LIGHTS_MASK = SCREEN_BRIGHT_BIT | BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT;//螢幕亮,按鍵燈亮,鍵盤燈亮
boolean mAnimateScreenLights = true;
static final int ANIM_STEPS = 60/4;
// Slower animation for autobrightness changes
static final int AUTOBRIGHTNESS_ANIM_STEPS = 60;
// These magic numbers are the initial state of the LEDs at boot. Ideally
// we should read them from the driver, but our current hardware returns 0
// for the initial value. Oops!
static final int INITIAL_SCREEN_BRIGHTNESS = 255;//螢幕初始狀態 亮
static final int INITIAL_BUTTON_BRIGHTNESS = Power.BRIGHTNESS_OFF;//按鍵燈初始狀態 滅
static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF;//鍵盤燈初始狀態 滅
private final int MY_UID;
private final int MY_PID;
private boolean mDoneBooting = false;
private boolean mBootCompleted = false;//開機完成標誌位
private int mStayOnConditions = 0;
private final int[] mBroadcastQueue = new int[] { -1, -1, -1 };
private final int[] mBroadcastWhy = new int[3];
private boolean mPreparingForScreenOn = false;
private boolean mSkippedScreenOn = false;
private boolean mInitialized = false;
private int mPartialCount = 0;
private int mPowerState;
// mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER,
// WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT or WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR
private int mScreenOffReason;
private int mUserState;
private boolean mKeyboardVisible = false;
private int mStartKeyThreshold = 0;
private boolean mUserActivityAllowed = true;
private int mProximityWakeLockCount = 0;
private boolean mProximitySensorEnabled = false;//距離感測器是否可用
private boolean mProximitySensorActive = false;//當前距離感測器是否工作
private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active
private long mLastProximityEventTime;
private int mScreenOffTimeoutSetting;//螢幕超時設定
private int mMaximumScreenOffTimeout = Integer.MAX_VALUE;
private int mKeylightDelay;
private int mDimDelay;
private int mScreenOffDelay;
private int mWakeLockState;
private long mLastEventTime = 0;
private long mScreenOffTime;
private volatile WindowManagerPolicy mPolicy;
private final LockList mLocks = new LockList();
private Intent mScreenOffIntent;
private Intent mScreenOnIntent;
private LightsService mLightsService;//系統LightsService
private Context mContext;
private LightsService.Light mLcdLight;//屏
private LightsService.Light mButtonLight;//按鍵燈
private LightsService.Light mKeyboardLight;//鍵盤燈(若有實體輸入法按鍵)
private LightsService.Light mAttentionLight;//通知等(若有訊號燈)
private UnsynchronizedWakeLock mBroadcastWakeLock;//廣播同步鎖
private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock;
private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock;
private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
private UnsynchronizedWakeLock mProximityPartialLock;
private HandlerThread mHandlerThread;
private HandlerThread mScreenOffThread;
private Handler mScreenOffHandler;
private Handler mHandler;
//計時器執行緒,主要完成管理螢幕超時操作,如當有使用者點選螢幕時
//該計時器重新開始計時,直到無任何操作,且到螢幕時延最大時間,將螢幕滅掉
private final TimeoutTask mTimeoutTask = new TimeoutTask();
private final BrightnessState mScreenBrightness
= new BrightnessState(SCREEN_BRIGHT_BIT);//亮度管理
private boolean mStillNeedSleepNotification;
private boolean mIsPowered = false;
private IActivityManager mActivityService;
private IBatteryStats mBatteryStats;
private BatteryService mBatteryService;//電池服務
private SensorManager mSensorManager;//Sensor管理器
private Sensor mProximitySensor;//距離感測器
private Sensor mLightSensor;//光線感測器
private Sensor mLightSensorKB;//光線感測器
private boolean mLightSensorEnabled;//光線感測器是否可用
private float mLightSensorValue = -1;
private boolean mProxIgnoredBecauseScreenTurnedOff = false;
private int mHighestLightSensorValue = -1;
private boolean mLightSensorPendingDecrease = false;
private boolean mLightSensorPendingIncrease = false;
private float mLightSensorPendingValue = -1;
private int mLightSensorScreenBrightness = -1;
private int mLightSensorButtonBrightness = -1;
private int mLightSensorKeyboardBrightness = -1;
private boolean mDimScreen = true;
private boolean mIsDocked = false;
private long mNextTimeout;
private volatile int mPokey = 0;
private volatile boolean mPokeAwakeOnSet = false;
private volatile boolean mInitComplete = false;
private final HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>();
// mLastScreenOnTime is the time the screen was last turned on
private long mLastScreenOnTime;
private boolean mPreventScreenOn;
private int mScreenBrightnessOverride = -1;
private int mButtonBrightnessOverride = -1;
private int mScreenBrightnessDim;
private boolean mUseSoftwareAutoBrightness;
private boolean mAutoBrightessEnabled;
private int[] mAutoBrightnessLevels;
private int[] mLcdBacklightValues;
private int[] mButtonBacklightValues;
private int[] mKeyboardBacklightValues;
private int mLightSensorWarmupTime;
boolean mUnplugTurnsOnScreen;
private int mWarningSpewThrottleCount;
private long mWarningSpewThrottleTime;
private int mAnimationSetting = ANIM_SETTING_OFF;
// Must match with the ISurfaceComposer constants in C++.
private static final int ANIM_SETTING_ON = 0x01;
private static final int ANIM_SETTING_OFF = 0x10;
// Used when logging number and duration of touch-down cycles
private long mTotalTouchDownTime;
private long mLastTouchDown;
private int mTouchCycles;
// could be either static or controllable at runtime
private static final boolean mSpew = false;
private static final boolean mDebugProximitySensor = (false || mSpew);
private static final boolean mDebugLightSensor = (false || mSpew);
private native void nativeInit();
private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
private native void nativeStartSurfaceFlingerAnimation(int mode);
本地方法的實現是在com_android_server_PowerManagerService.cpp中。
首先看其構造方法:
PowerManagerService() {
// Hack to get our uid... should have a func for this.
long token = Binder.clearCallingIdentity();
MY_UID = Process.myUid();//獲取本程序的UID以及PID
MY_PID = Process.myPid();
Binder.restoreCallingIdentity(token);
//設定超時時間為1周。Power類封裝了同Linux互動的介面。
// XXX remove this when the kernel doesn't timeout wake locks
Power.setLastUserActivityTimeout(7*24*3600*1000); // one week
//初始化兩個狀態變數
// assume nothing is on yet
mUserState = mPowerState = 0;
//將自己新增到看門狗Watchdog的監控管理佇列中
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
}
構造方法比較簡單,接著分析init()函式,還函式完成了一些重要的初始化操作。
//電源管理的初始化操作函式,完成一些賦值操作,執行緒管理等
void init(Context context, LightsService lights, IActivityManager activity,
BatteryService battery) {
mLightsService = lights;
mContext = context;
mActivityService = activity;
mBatteryStats = BatteryStatsService.getService();
mBatteryService = battery;
mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT);
mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS);
mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD);
mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
nativeInit();//本地JNI方法
synchronized (mLocks) {
updateNativePowerStateLocked();
}
mInitComplete = false;//初始化未完成標誌
//滅屏操作執行緒
//該操作的作用,手機一開機,螢幕亮度並不是立即就達到使用者設定的亮度值
//而是有一個緩慢的變化過程,經過一段漸變之後才達到使用者設定的亮度值,該現象在此處完成
mScreenOffThread = new HandlerThread("PowerManagerService.mScreenOffThread") {
@Override
protected void onLooperPrepared() {
mScreenOffHandler = new Handler();
synchronized (mScreenOffThread) {
mInitComplete = true;
mScreenOffThread.notifyAll();
}
}
};
mScreenOffThread.start();
synchronized (mScreenOffThread) {
while (!mInitComplete) {
try {
mScreenOffThread.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
mInitComplete = false;
//Handler 執行緒,初始化其他的一些執行緒,此處呼叫initInThread()函式
mHandlerThread = new HandlerThread("PowerManagerService") {
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
initInThread();
}
};
mHandlerThread.start();
//此處為典型的執行緒A建立執行緒B,然後執行緒A等待執行緒B建立完成
synchronized (mHandlerThread) {
while (!mInitComplete) {
try {
mHandlerThread.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
nativeInit();
//當用戶有任何操作可強制喚醒睡眠狀態
synchronized (mLocks) {
updateNativePowerStateLocked();
// We make sure to start out with the screen on due to user activity.
// (They did just boot their device, after all.)
forceUserActivityLocked();
mInitialized = true;
}
}
關鍵部分已經添加註釋,接下來另外一個初始化操作函式是initInThread(),被init()呼叫。
//執行緒初始化操作,被init()呼叫
void initInThread() {
mHandler = new Handler();
mBroadcastWakeLock = new UnsynchronizedWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "sleep_broadcast", true);
mStayOnWhilePluggedInScreenDimLock = new UnsynchronizedWakeLock(
PowerManager.SCREEN_DIM_WAKE_LOCK, "StayOnWhilePluggedIn Screen Dim", false);
mStayOnWhilePluggedInPartialLock = new UnsynchronizedWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "StayOnWhilePluggedIn Partial", false);
mPreventScreenOnPartialLock = new UnsynchronizedWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "PreventScreenOn Partial", false);
mProximityPartialLock = new UnsynchronizedWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "Proximity Partial", false);
mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
Resources resources = mContext.getResources();
mAnimateScreenLights = resources.getBoolean(
com.android.internal.R.bool.config_animateScreenLights);
mUnplugTurnsOnScreen = resources.getBoolean(
com.android.internal.R.bool.config_unplugTurnsOnScreen);
mScreenBrightnessDim = resources.getInteger(
com.android.internal.R.integer.config_screenBrightnessDim);
// read settings for auto-brightness
mUseSoftwareAutoBrightness = resources.getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
if (mUseSoftwareAutoBrightness) {
mAutoBrightnessLevels = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevels);
mLcdBacklightValues = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
mButtonBacklightValues = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessButtonBacklightValues);
mKeyboardBacklightValues = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessKeyboardBacklightValues);
mLightSensorWarmupTime = resources.getInteger(
com.android.internal.R.integer.config_lightSensorWarmupTime);
}
ContentResolver resolver = mContext.getContentResolver();
Cursor settingsCursor = resolver.query(Settings.System.CONTENT_URI, null,
"(" + Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?)",
new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN,
SCREEN_BRIGHTNESS_MODE, WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE},
null);
mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler);
SettingsObserver settingsObserver = new SettingsObserver();
mSettings.addObserver(settingsObserver);
// pretend that the settings changed so we will get their initial state
settingsObserver.update(mSettings, null);
// register for the battery changed notifications
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mContext.registerReceiver(new BatteryReceiver(), filter);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(new BootCompletedReceiver(), filter);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(new DockReceiver(), filter);
// Listen for secure settings changes
mContext.getContentResolver().registerContentObserver(
Settings.Secure.CONTENT_URI, true,
new ContentObserver(new Handler()) {
public void onChange(boolean selfChange) {
updateSettingsValues();
}
});
updateSettingsValues();
synchronized (mHandlerThread) {
mInitComplete = true;
mHandlerThread.notifyAll();
}
}
至此,PMS的初始化建立已經完成。接下來分下幾個重要的函式。
void systemReady() {
//建立一個SensorManager 用於和系統的感測器系統互動,該部分多為native方法
mSensorManager = new SensorManager(mHandlerThread.getLooper());
mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
// don't bother with the light sensor if auto brightness is handled in hardware
if (mUseSoftwareAutoBrightness) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
mLightSensorKB = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if(mSensorManager != null && mLightSensorKB != null){
mSensorManager.registerListener(mLightListenerKB, mLightSensorKB,
LIGHT_SENSOR_RATE);
}
// wait until sensors are enabled before turning on screen.
// some devices will not activate the light sensor properly on boot
// unless we do this.
if (mUseSoftwareAutoBrightness) {
// turn the screen on
setPowerState(SCREEN_BRIGHT);
} else {//不考慮軟體自動亮度調節,所以執行此處
// turn everything on
setPowerState(ALL_BRIGHT);//設定手機電源狀態為ALL_BRIGHT,即螢幕按鍵燈都開啟
}
synchronized (mLocks) {
Slog.d(TAG, "system ready!");
mDoneBooting = true;
//根據情況啟動LightSensor
enableLightSensorLocked(mUseSoftwareAutoBrightness && mAutoBrightessEnabled);
long identity = Binder.clearCallingIdentity();
try {//通知BatteryStatsService 他將統計相關的電量使用情況
mBatteryStats.noteScreenBrightness(getPreferredBrightness());
mBatteryStats.noteScreenOn();
} catch (RemoteException e) {
// Nothing interesting to do.
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
systemReady()完成的主要操作:感測器操作;電源狀態設定;BatteryStatsService管理。
bootCompleted()開機完成之後呼叫的函式。
void bootCompleted() {
Slog.d(TAG, "bootCompleted");
synchronized (mLocks) {
mBootCompleted = true;
//此時將重新計算螢幕的超時時間
userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true);
//根據當前的手機狀態判斷是否進入睡眠狀態(如插上USB充電狀態等)
updateWakeLockLocked();
mLocks.notifyAll();
}
}
內部類WakeLock是android提供給應用程式獲取電力資源的的唯一方法,只要還有地方使用WakeLock,系統就不會進入休眠狀態。PMS中實現了PowerManager.java中夫人相應定義和方法。該類是電源管理系統喚醒與睡眠機制的核心。此處不再做詳細分析。後續會專題研究。
根據前面的分析,PMS有事需要進行點亮螢幕,開啟按鍵燈等操作,為此android提供了Power類以及LightService滿足PMS的要求。這兩個類比較簡單,但是背後的Kernel層相對複雜些。這些Light點亮操作均為LightService通過JNI讀寫檔案節點,通過設定1或0來操作。此處不再詳細研究。
PMS中的userActivity()分析。關於userActivity()的作用。比如開啟手機,並解鎖進入桌面,如果在規定的時間內不操作手機,那麼螢幕將變暗,最後關閉。如果在此過程中,觸碰螢幕,螢幕又會重新亮起,這個觸動螢幕的操作將導致userActivity()最終被呼叫。
//觸動螢幕時,該函式將被呼叫,由PhoneWindowManager.java 等呼叫
private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
int eventType, boolean force) {
//mPokey和輸入事件處理策略有關,如果此處的條件滿足則表示忽略TOUCH_EVENTS
if (((mPokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0) && (eventType == TOUCH_EVENT)) {
if (false) {
Slog.d(TAG, "dropping touch mPokey=0x" + Integer.toHexString(mPokey));
}
return;
}
synchronized (mLocks) {
if (mSpew) {
Slog.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time
+ " mUserActivityAllowed=" + mUserActivityAllowed
+ " mUserState=0x" + Integer.toHexString(mUserState)
+ " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)
+ " mProximitySensorActive=" + mProximitySensorActive
+ " timeoutOverride=" + timeoutOverride
+ " force=" + force);
}
// ignore user activity if we are in the process of turning off the screen
if (isScreenTurningOffLocked()) {
Slog.d(TAG, "ignoring user activity while turning off screen");
return;
}
// Disable proximity sensor if if user presses power key while we are in the
// "waiting for proximity sensor to go negative" state.
if (mProximitySensorActive && mProximityWakeLockCount == 0) {
mProximitySensorActive = false;//控制接近感測器
}
if (mLastEventTime <= time || force) {
mLastEventTime = time;
if ((mUserActivityAllowed ) || force) {
// Only turn on button backlights if a button was pressed
// and auto brightness is disabled
if (eventType == BUTTON_EVENT && !mUseSoftwareAutoBrightness) {
mUserState = SCREEN_BRIGHT; //設定使用者事件導致的mUserState
} else {
// don't clear button/keyboard backlights when the screen is touched.
mUserState |= SCREEN_BRIGHT;
}
int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
//通知BatteryStatsService進行電量統計
mBatteryStats.noteUserActivity(uid, eventType);
} catch (RemoteException e) {
// Ignore
} finally {
Binder.restoreCallingIdentity(ident);
}
//重新計算WakeLock狀態
mWakeLockState = mLocks.reactivateScreenLocksLocked();
setPowerState(mUserState | mWakeLockState, noChangeLights,
WindowManagerPolicy.OFF_BECAUSE_OF_USER);
if(mProximitySensorActive){
//重新開始螢幕計時
setTimeoutLocked(time, timeoutOverride, SCREEN_OFF);
}
else
{
//重新開始螢幕計時
setTimeoutLocked(time,timeoutOverride,SCREEN_BRIGHT);
}
}
}
}
if (mPolicy != null) {
//mPolicy指向PhoneWindowManager,用於和WindowManagerService互動
mPolicy.userActivity();
}
}
該函式重點在於重置計時器超時函式setTimeoutLocked(),並由setPowerState()真正去設定螢幕狀態,同時螢幕狀態切換由TimeoutTask完成,此處不再詳細研究。
另外PMS系統中,與其互動的兩個重要Service:BatteryService和BatteryStatsService。BatteryService提供介面用於獲取電池資訊,充電狀態等。BatteryStatsService主要用於用電統計,通過它可知誰是系統中的耗電大戶。
至此,PMS分析結束。
接下來,簡單介紹下框架層其他幾個相關類。
首先,Power.java和andriod_os_Power.cpp
PowerManagerSerivive.java中呼叫了一些本地方法,該檔案作為這些方法的java層與jni的中間層,聲明瞭本地介面。該本分實現是在Power.java中。
public static native void acquireWakeLock(int lock, String id);
public static native void releaseWakeLock(String id);
public static native int setScreenState(boolean on);
public static native int setLastUserActivityTimeout(long ms);
@Deprecated
public static native void shutdown();
public static void reboot(String reason) throws IOException
power.c該檔案作為Android系統的最底層,與Linux核心的power manager互動。
static int64_t systemTime();
static int open_file_descriptors(const char * const paths[]);
static inline void initialize_fds(void);
int acquire_wake_lock(int lock, const char* id);
int set_last_user_activity_timeout(int64_t delay);
int set_screen_state(int on);
框架層,與電源管理相關的類還有一些,介於篇幅,不再一一分析了。
2,核心層分析
接下來簡單看下核心層的相關檔案,檔案列表上述已經列出。
其主要程式碼在下列位置:
drivers/android/power.c
其對Kernel 提供的介面函式有
EXPORT_SYMBOL(android_init_suspend_lock); //初始化Suspendlock,在使用前必須做初始化
EXPORT_SYMBOL(android_uninit_suspend_lock); //釋放suspendlock 相關的資源
EXPORT_SYMBOL(android_lock_suspend); //申請lock,必須呼叫相應的unlock 來釋放它
EXPORT_SYMBOL(android_lock_suspend_auto_expire);//申請partial wakelock, 定時時間到後會自動釋放
EXPORT_SYMBOL(android_unlock_suspend); //釋放lock
EXPORT_SYMBOL(android_power_wakeup); //喚醒系統到on
EXPORT_SYMBOL(android_register_early_suspend); //註冊earlysuspend 的驅動
EXPORT_SYMBOL(android_unregister_early_suspend); //取消已經註冊的early suspend 的驅動
提供給Android Framework 層的proc 檔案如下:
"/sys/android_power/acquire_partial_wake_lock" //申請partial wake lock
"/sys/android_power/acquire_full_wake_lock" //申請full wakelock
"/sys/android_power/release_wake_lock" //釋放相應的wake lock
"/sys/android_power/request_state" //請求改變系統狀態,進standby 和回到wakeup 兩種狀態
"/sys/android_power/state" //指示當前系統的狀態
Android 的電源管理主要是通過Wake lock 來實現的,在最底層主要是通過如下三個佇列來實現其管理:
static LIST_HEAD(g_inactive_locks);
static LIST_HEAD(g_active_partial_wake_locks);
static LIST_HEAD(g_active_full_wake_locks);
所有初始化後的lock 都會被插入到g_inactive_locks 的佇列中,而當前活動的partial wake lock 都會被插入到g_active_partial_wake_locks 佇列中, 活動的full wake lock 被插入到g_active_full_wake_locks 佇列中, 所有的partial wakelock 和full wake lock 在過期後或unlock 後都會被移到inactive的佇列,等待下次的呼叫.
在Kernel 層使用wake lock 步驟如下:
1.呼叫函式android_init_suspend_lock 初始化一個wake lock
2.呼叫相關申請lock 的函式android_lock_suspend 或android_lock_suspend_auto_expire 請求lock,這裡只能申請partial wake lock, 如果要申請Full wake lock,則需要呼叫函式android_lock_partial_suspend_auto_expire(該函式沒有EXPORT出來),這個命名有點奇怪,不要跟前面的android_lock_suspend_auto_expire 搞混了.
3.如果是auto expire 的wake lock 則可以忽略,不然則必須及時的把相關的wake lock 釋放掉,否則會造成系統長期執行在高功耗的狀態.
4.在驅動解除安裝或不再使用Wake lock 時請記住及時的呼叫android_uninit_suspend_lock 釋放資源.
系統的狀態:
USER_AWAKE, //Full on status
USER_NOTIFICATION, //Early suspended driver but CPU keep on
USER_SLEEP // CPU enter sleep mode
接著分析下,Kernel的wake lock喚醒操作。
框架層acquireWakeLock()-- >android_os_Power.cpp-- >acquireWakeLock()-- >power.c-- >acquire_wake_lock()。
int acquire_wake_lock(int lock, const char* id)
{
initialize_fds();
// LOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id);
if (g_error) return g_error;
int fd;
if (lock == PARTIAL_WAKE_LOCK) {
fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
}
else {
return EINVAL;
}
return write(fd, id, strlen(id));
}
到現在為止,我們的程式碼流程已經走了一大半了,我們一開始介紹的android的上面幾層framework層、JNI層、HAL層都已經介紹了就剩下Kernel層了。下面就應該是和kernel層進行互動了。但是在android/hardware/libhardware_legacy/power/power.c中的acquire_wake_lock()函式似乎沒法和kernel層進行通訊,在這個函式的最後不是還有一個返回語句return write(fd, id, strlen(id)),該write方法為重點。此時我們先跳過power.c中的acquire_wake_lock(),先分析/kernel/kernel/power/main.c中的相關方法,然後再回頭分析power.c中的acquire_wake_lock()中的write(fd, id, strlen(id))。這樣整個流程就能順利連線起來。
/kernel/kernel/power/main.c
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/resume-trace.h>
#include <linux/workqueue.h>
#include "power.h"
DEFINE_MUTEX(pm_mutex);
#ifdef CONFIG_PM_SLEEP
/* Routines for PM-transition notifications */
static BLOCKING_NOTIFIER_HEAD(pm_chain_head);
int register_pm_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&pm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(register_pm_notifier);
int unregister_pm_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&pm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(unregister_pm_notifier);
int pm_notifier_call_chain(unsigned long val)
{
return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
== NOTIFY_BAD) ? -EINVAL : 0;
}
/* If set, devices may be suspended and resumed asynchronously. */
int pm_async_enabled = 1;
static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", pm_async_enabled);
}
static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned long val;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
if (val > 1)
return -EINVAL;
pm_async_enabled = val;
return n;
}
power_attr(pm_async);
#ifdef CONFIG_PM_DEBUG
int pm_test_level = TEST_NONE;
static const char * const pm_tests[__TEST_AFTER_LAST] = {
[TEST_NONE] = "none",
[TEST_CORE] = "core",
[TEST_CPUS] = "processors",
[TEST_PLATFORM] = "platform",
[TEST_DEVICES] = "devices",
[TEST_FREEZER] = "freezer",
};
static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
int level;
for (level = TEST_FIRST; level <= TEST_MAX; level++)
if (pm_tests[level]) {
if (level == pm_test_level)
s += sprintf(s, "[%s] ", pm_tests[level]);
else
s += sprintf(s, "%s ", pm_tests[level]);
}
if (s != buf)
/* convert the last space to a newline */
*(s-1) = '\n';
return (s - buf);
}
static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
const char * const *s;
int level;
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
mutex_lock(&pm_mutex);
level = TEST_FIRST;
for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++)
if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
pm_test_level = level;
error = 0;
break;
}
mutex_unlock(&pm_mutex);
return error ? error : n;
}
power_attr(pm_test);
#endif /* CONFIG_PM_DEBUG */
#endif /* CONFIG_PM_SLEEP */
struct kobject *power_kobj;
static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
#ifdef CONFIG_SUSPEND
int i;
for (i = 0; i < PM_SUSPEND_MAX; i++) {
if (pm_states[i] && valid_state(i))
s += sprintf(s,"%s ", pm_states[i]);
}
#endif
#ifdef CONFIG_HIBERNATION
s += sprintf(s, "%s\n", "disk");
#else
if (s != buf)
/* convert the last space to a newline */
*(s-1) = '\n';
#endif
return (s - buf);
}
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPEND
suspend_state_t state = PM_SUSPEND_ON;
#else
suspend_state_t state = PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
/* First, check if we are requested to hibernate */
if (len == 4 && !strncmp(buf, "disk", len)) {
error = hibernate();
goto Exit;
}
#ifdef CONFIG_SUSPEND
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
break;
}
if (state < PM_SUSPEND_MAX && *s)
#ifdef CONFIG_EARLYSUSPEND
if (state == PM_SUSPEND_ON || valid_state(state)) {
error = 0;
request_suspend_state(state);
}
#else
error = enter_state(state);
#endif
#endif
Exit:
return error ? error : n;
}
power_attr(state);
#ifdef CONFIG_PM_SLEEP
static ssize_t wakeup_count_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
unsigned int val;
return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR;
}
static ssize_t wakeup_count_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned int val;
if (sscanf(buf, "%u", &val) == 1) {
if (pm_save_wakeup_count(val))
return n;
}
return -EINVAL;
}
power_attr(wakeup_count);
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM_TRACE
int pm_trace_enabled;
static ssize_t pm_trace_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", pm_trace_enabled);
}
static ssize_t
pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int val;
if (sscanf(buf, "%d", &val) == 1) {
pm_trace_enabled = !!val;
return n;
}
return -EINVAL;
}
power_attr(pm_trace);
static ssize_t pm_trace_dev_match_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return show_trace_dev_match(buf, PAGE_SIZE);
}
static ssize_t
pm_trace_dev_match_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
return -EINVAL;
}
power_attr(pm_trace_dev_match);
#endif /* CONFIG_PM_TRACE */
#ifdef CONFIG_USER_WAKELOCK
power_attr(wake_lock);
power_attr(wake_unlock);
#endif
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
&pm_trace_dev_match_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
&wakeup_count_attr.attr,
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr,
#endif
#ifdef CONFIG_USER_WAKELOCK
&wake_lock_attr.attr,
&wake_unlock_attr.attr,
#endif
#endif
NULL,
};
static struct attribute_group attr_group = {
.attrs = g,
};
#ifdef CONFIG_PM_RUNTIME
struct workqueue_struct *pm_wq;
EXPORT_SYMBOL_GPL(pm_wq);
static int __init pm_start_workqueue(void)
{
pm_wq = alloc_workqueue("pm", WQ_FREEZABLE, 0);
return pm_wq ? 0 : -ENOMEM;
}
#else
static inline int pm_start_workqueue(void) { return 0; }
#endif
static int __init pm_init(void)
{
int error = pm_start_workqueue();
if (error)
return error;
hibernate_image_size_init();
hibernate_reserved_size_init();
power_kobj = kobject_create_and_add("power", NULL);
if (!power_kobj)
return -ENOMEM;
return sysfs_create_group(power_kobj, &attr_group);
}
core_initcall(pm_init);
這段程式碼雖然簡短,但看起來是不是還是比較費勁,沒關係,我們倒過來看就比較清楚了。上面程式碼中的最後一個函式pm_init(void)的返回值為 sysfs_create_group(power_kobj, &attr_group);的意思就是當我們在對sysfs/下相對的節點進行操作的時候會呼叫與attr_group 裡的相關函式。還是上面一個檔案main.c。
#ifdef CONFIG_USER_WAKELOCK
power_attr(wake_lock);
power_attr(wake_unlock);
#endif
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
&pm_trace_dev_match_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
&wakeup_count_attr.attr,
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr,
#endif
#ifdef CONFIG_USER_WAKELOCK
&wake_lock_attr.attr,
&wake_unlock_attr.attr,
#endif
#endif
NULL,
};
static struct attribute_group attr_group = {
.attrs = g,
};
再往上面看其實就是指&wake_lock_attr.attr(對不同情況的操作會呼叫不同的attr_group)。power_attr(wake_lock)就是使具體的操作函式與其掛鉤。我們現在來看一看這個掛鉤過程是怎麼實現的。
power_attr(name)的定義是在/kernel/kernel/power/power.h
#define power_attr(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = __stringify(_name), \
.mode = 0644, \
}, \
.show = _name##_show, \
.store = _name##_store, \
}
在該函式中##的作用通俗點講就是“連線”的意思。比如power_attr(wake_lock):
static struct kobj_attribute wake_lock_attr = { \
.attr = { \
.name = __stringify(wake_lock), \
.mode = 0644, \
}, \
.show = wake_lock_show, \
.store = wake_lock_store, \
}
函式wake_lock_store和wake_lock_show就定義在android/kernel/kernel/power/userwakelock.c中。因此當我們對/sys/power/wake_lock進行操作的時候就會呼叫到userwakelock.c中定義的wake_lock_store()函式。好了,我們該回到原來我們產生疑問的地方了,在 power.c中我們將重新研究一下這這段程式碼,這時我們還得關注其中的另一個函式initialize_fds()。
initialize_fds(void)
{
// XXX: should be this:
//pthread_once(&g_initialized, open_file_descriptors);
// XXX: not this:
if (g_initialized == 0) {
if(open_file_descriptors(NEW_PATHS) < 0) {
open_file_descriptors(OLD_PATHS);
on_state = "wake";
off_state = "standby";
}
g_initialized = 1;
}
}
其實這個函式中最核心的步驟就是open_file_descriptors(NEW_PATHS) ;而
const char * const NEW_PATHS[] = {