轉自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=30510400&id=5569393
概述
一直以來,電源管理是電子產品設計中非常重要的環節,也是任何電子設備中最為重要的系統模塊之一,優秀的電源管理方案,能夠提供持久的續航能力,良好的用戶體驗,更能提升電子產品的競爭力。
移動設備的電量主要由兩種元件消耗:CPU和顯示屏,因此設法降低這兩種元件的耗電量就是電源管理的關鍵,為移動設備設計的CPU大多有兩種工作頻率,為了省電,大部分時間內cpu都工作在降低頻率下,只有進行密集計算時,如視頻解碼才會切換到高頻狀態,而顯示屏省電的方法是盡量減少亮屏時間,但是顯示屏的開關和應用有很大的關系,因此系統中需要有一套機制來控制顯示屏的開關和亮度,這也是電源管理的主要工作。
電源管理架構
Android的電源管理主要是通過wakelock機制來管理系統的狀態,整個android電源管理,可以分為四個層次:應用接口層(PowerManager.java),Framework層(PowerManagerService.java),HAL層(Power.c),和內核層(kernel/Power)。
應用接口層:PowerManager中開放給應用一系列接口,應用可以調用PM的接口申請wakelock,喚醒系統,使系統進入睡眠等操作;
Framework層:應用調用PowerManager開放的接口,來對系統進行一些列的操作是在PowerManagerService中完成的,PowerManagerService計算系統中和Power相關的計算,是整個電源管理的決策系統。同時協調Power如何與系統其它模塊的交互,比如亮屏,暗屏,系統睡眠,喚醒等等。
HAL層:該層只有一個power.c文件,該文件通過上層傳下來的參數,向/sys/power/wake_lock或者/sys/power/wake_unlock文件節點寫數據來與kernel進行通信,主要功能是申請/釋放鎖,維持屏幕亮滅
Kernel層:內核層實現電源管理的方案主要包含三個部分:
1、Kernel/power/:實現了系統電源管理框架機制。
2、Arch/arm(ormips or powerpc)/mach-XXX/pm.c:實現對特定板的處理器電源管理。
3、drivers/power:是設備電源管理的基礎框架,為驅動提供了電源管理接口。
Android電源管理框架如下圖:
電源管理服務——PowerManagerService
PowerManagerServcie是android系統電源管理的核心服務,它在Framework層建立起一個策略控制方案,向下決策HAL層以及kernel層來控制設備待機狀態,控制顯示屏,背光燈,距離傳感器,光線傳感器等硬件設備的狀態。向上提供給應用程序相應的操作接口,比如聽音樂時持續保持系統喚醒,應用通知來臨喚醒手機屏幕等場景
啟動過程
SystemServer在系統啟動的時候會啟動三類服務:引導關鍵服務,核心服務,其他服務;PowerManagerService是在SystemServer中創建的,並將其作為一個系統服務加入到ServiceManager中:
mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
在啟動引導關鍵服務調用startBootstrapServices(),其中各種服務都是通過SystemServiceManager中的startService()函數來啟動:
public <t extends< t systemservice> t startservice(class serviceclass) {<="" p="">
final String name = serviceClass.getName();
final T service;
Constructor constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);
mServices.add(service);//注冊服務到服務列表中去
service.onStart();//啟動服務
}
在啟動PowerManagerService時,傳入的參數類是PowerManagerService,在startService()中首先調用PowerManagerService的構造函數,然后調用其onStart()函數
PowerManagerServcie的構造函數:
mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
mDisplaySuspendBlocker.acquire();
mWakefulness = WAKEFULNESS_AWAKE;
nativeInit();
創建一個處理消息和發送消息的線程mHandler,並且兩種標志flag的wakelock鎖:PowerManagerService.WakeLocks和PowerManagerService.Display,前者是傳入到底層是控制cpu喚醒狀態,后者則是控制屏幕亮滅。在構造函數最后調用nativeInit();在native層初始化相關資源。將mWakefulness 置成WAKEFULNESS_AWAKE狀態,mWakefulness 標識系統當前狀態共有四種定義:
WAKEFULNESS_ASLEEP:表示系統當前處於休眠狀態,只能被wakeUp()調用喚醒。
WAKEFULNESS_AWAKE:表示系統目前處於正常運行狀態。
WAKEFULNESS_DREAMING:表示系統當前正處於屏保的狀態。
WAKEFULNESS_DOZING:表示系統正處於“doze”狀態。這種狀態下只有低耗電的“屏保”可以運行,其他應用進程都被掛起。
在SystemServer中startService中調用到PowerManagerService構造函數做完初始化操作之后便會調用PowerManagerService的onStart()函數:
publishBinderService(Context.POWER_SERVICE, new BinderService());
publishLocalService(PowerManagerInternal.class, new LocalService());
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
Onstart完成的工作就是將POWER_SERVICE作為Binder的服務端,注冊到SystemService中去;將PowerManagerInternal注冊到本地服務中,將自己加到watchdog的監控隊列中去;將之前在構造函數中創建的mHandler對象加入到watchdog的中,用於監視mHandler的looper是否空閑;
系統准備工作
SystemServer在調用PowerManagerService之后還會調用其SystemReady,相當於在系統准備就緒后對PowerManagerService再進行一些初始化工作。SystemReady()方法代碼如下:
mAppOps = appOps;
mDreamManager = getLocalService(DreamManagerInternal.class);
mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
mPolicy = getLocalService(WindowManagerPolicy.class);
mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
獲取與PowerManager相關的本地服務,比如屏保(mDreamManager),屏幕顯示(mDisplayManagerInternal),窗口策略(mPolicy),電池電量(mBatteryManagerInternal)等服務,然后初始化屏幕最大亮度,最小亮度,和默認亮度;
SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
mBatteryStats = BatteryStatsService.getService();
mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
mPolicy);
mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"), mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mLightsManager = getLocalService(LightsManager.class);
mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
創建sensorManager 對象,用於與sensor交互,比如距離傳感器,光線傳感器,加速度傳感器(doze上使用)。獲取電池狀態服務,和背光服務;
創建mNotifier 對象,在通過mNotifier 發送通知時候,會傳入底層申請PowerManagerService.Broadcasts的wakelock鎖。
創建mSettingsObserver 監聽系統設置變化,比如亮屏時間,自動背光,屏幕亮度,屏保,低電模式等等
總而言之在SystemReady方法中完成的主要工作如下:
獲取與PowerManagerServcie相關的系統服務以及本地服務;
獲取屏幕最大,最小以及默認亮度值;
創建SensorManager 對象,用於和SensorService交互;
創建Notifier對象,用於通知系統中電源狀態的改變;
創建WirelessChargerDetector對象,用於檢測無線充電的傳感器(市面上支持的手機較少)
調用DisplayManagerService的initPowerManagement()方法來初始化Power顯示模塊。
注冊SettingsObserver監聽系統設置的變化
PowerManagerServcie的啟動初始化過程如下:
相關接口
PowerManager向應用提供了相應的接口,以供應用程序調用,來改變系統待機狀態,屏幕狀態,屏幕亮度等,PowerManager是PowerManagerService的代理類,PowerManager向上層應用提供交互的接口,具體的處理工作在PowerManagerService中完成。下面介紹PowerManager中提供的相應接口作用:
Wakeup():強制系統從睡眠狀態喚醒,此接口對應用是不開放的,應用想喚醒系統必須通過設置亮屏標志(后面即將講到);
gotoSleep():強制系統進入到睡眠狀態,此接口也是應用不開放。
userActivity():向PowerManagerService報告影響系統休眠的用戶活動,重計算滅屏時間,背光亮度等,例如觸屏,划屏,power鍵等用戶活動;
Wakelock:wakelock是PowerManager的一個內部類,提供了相關的接口來操作wakelock鎖,比如newWakeLock()方法來創建wakelock鎖,acquire()和release()方法來申請和釋放鎖。
isDeviceIdleMode():返回設備當前的狀態,如果處於Idle狀態,則返回true,Idle狀態是在手機長時間沒有被使用以及沒有運動的情況下,手機進入到一種Doze低功耗的模式下,這種狀態下手機可能會關掉網絡數據訪問,可以通過監視DEVICE_IDLE_MODE_CHANGED這個廣播信息,來監控手機狀態的改變
喚醒——wakeup
PowerManager的wakeup接口屬性是@hide的,所以對於上層應用是不可見的,上層應用要喚醒系統大都依靠兩種方式:1.在應用啟動Activity時候設置相應的window的flags,通過WMS來喚醒系統;2.在應用申請wakelock鎖時附帶ACQUIRE_CAUSES_WAKEUP標志;
Wakeup流程如下圖所示
PowerManager的wakeup接口,可供應用程序調用,來強制喚醒系統,如果該設備處於睡眠狀態,調用該接口會立即喚醒系統,比如按Power鍵,來電,鬧鍾等場景都會調用該接口。喚醒系統需要android.Manifest.permission#DEVICE_POWER的權限;
我們來看看PowerManagerServcie中wakeup接口的代碼:
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
Wakeup接口中僅僅是對調用者的權限進行檢查;然后放到wakeUpInternal()中處理,wakeUpInternal()中沒有做操作,只是調用wakeUpNoUpdateLocked()函數,然后更新調用updatePowerStateLocked()更新電源狀態
wakeUpNoUpdateLocked()關鍵代碼
mLastWakeTime = eventTime;
setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);
userActivityNoUpdateLocked(
eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
return true;
setWakefulnessLocked()函數將mWakefulness賦值為wakefulness和mDirty |= DIRTY_WAKEFULNESS;這兩個標志在后面更新電源狀態時有重要作用,同時調用到mNotifier中的onWakefulnessChangeStarted調用到handleEarlyInteractiveChange調用到PhoneWindowManager的startedWakingUp函數,來通知到PhoneWindowManager屏幕開始啟動;
調用mNotifier向系統中通知系統被喚醒;
更新用戶活動,將mDirty |= DIRTY_USER_ACTIVITY置位;來重新計算亮屏時間。
在updatePowerStateLocked()中更新電源狀態,updatePowerStateLocked為PowerManagerService的核心函數,后面會詳細介紹,這里簡單介紹在wakeup中的流程。在updatePowerStateLocked()中的updateDisplayPowerStateLocked()函數中將mDisplayPowerRequest.policy設置成POLICY_BRIGHT;然后調用:
mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,mRequestWaitForNegativeProximity);
通過調用到DisplayManagerService中對屏幕狀態作出相應的改變,通過去與WindowManagerService執行亮屏之前的屏幕繪制過程,與LightServcie交互來點亮屏幕背光燈。其調用過程和具體邏輯在亮屏流程文檔中有詳細分析。
睡眠——goToSleep
PowerManager的gotoSleep()接口是@hide屬性,因此對於上層應用是不開放的,設備強制進入睡眠狀態,在處理一些滅屏按鍵事件時,會通過WMS來調用PowerManager的gotoSleep接口,一般在系統一段時間沒有被操作時,系統將會自動調用gotoSleep函數,讓其進入到睡眠模式;
與wakeup喚醒一樣,PowerManager的gotoSleep()在PowerManagerService中處理,PMS中的gotoSleep()首先檢查調用者是否擁有android.Manifest.permission.DEVICE_POWER權限。然后調用到goToSleepInternal()中處理:
if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
updatePowerStateLocked();
在goToSleepNoUpdateLocked()中完成發送了將要休眠的通知,然后修改了Wakefulness,將其置成WAKEFULNESS_DOZING,將mDirty |= DIRTY_WAKEFULNESS置位,更多的實際工作在updatePowerStateLocked()中完成。在updateDreamLocked中完成真正進入睡眠的過程;
其調用過程大致如下:
在reallyGoToSleepNoUpdateLocked中將mWakefulness置成WAKEFULNESS_ASLEEP,在updateWakeLockSummaryLocked中有如下:
if (mWakefulness == WAKEFULNESS_ASLEEP
|| (mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
mWakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
| WAKE_LOCK_BUTTON_BRIGHT);
將mWakeLockSummary列表中的wakelock鎖所形成的集合變量mWakeLockSummary 中將WAKE_LOCK_SCREEN_BRIGHT,WAKE_LOCK_SCREEN_DIM,WAKE_LOCK_BUTTON_BRIGHT三種wakelock鎖置為無效,再次調用updatePowerStateLocked更新電源狀態時候,會在updateDisplayPowerStateLocked中做滅屏操作,其流程與wakeup喚醒系統亮屏操作流程大致一樣。
用戶活動——userActivity
userActivity()接口用於用戶進程向PowerManagerService報告用戶影響系統休眠的活動。例如, 用戶點擊屏幕時,系統會調用該方法來告訴PowerManagerService用戶點擊的時間,這樣PowerManagerService將更新內部保存的時間值,從而推遲系統休眠的時間。userActivity()方法主要通過調用內部的userActivityInternal()方法來完成工作。
在userActivityInternal()中並沒有做任何操作,僅僅是將mLastUserActivityTime 更新為當前event的時間eventTime,mDirty |= DIRTY_USER_ACTIVITY;置位操作。具體操作仍然是在PowerManagerService中的核心函數updatePowerStateLocked()中完成;
在updateUserActivitySummaryLocked()中
final int sleepTimeout = getSleepTimeoutLocked();
final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
mUserActivitySummary = 0;
if (mLastUserActivityTime >= mLastWakeTime) {
nextTimeout = mLastUserActivityTime
+ screenOffTimeout - screenDimDuration;
if (now < nextTimeout) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
} else {
nextTimeout = mLastUserActivityTime + screenOffTimeout;
if (now < nextTimeout) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
}
}
重新計算睡眠超時時間,滅屏超時時間,暗屏超時時間,將mUserActivitySummary 置為0,通過計算上一次的用戶事件時間與超時時間作對比,來判斷將屏幕置為亮屏 (USER_ACTIVITY_SCREEN_BRIGHT)還是暗屏(USER_ACTIVITY_SCREEN_DIM),前提是手機處於非睡眠狀態。
if (mUserActivitySummary != 0 && nextTimeout >= 0) {
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextTimeout);
}
如果時間還沒到,則返回發送一個MSG_USER_ACTIVITY_TIMEOUT的定時消息,當處理時間到了,會在消息的處理方法handleUserActivityTimeout中重新調用updatePowerStateLocked()電源狀態。再次調用時會根據當前的狀態重新計算mUserActivitySummary 的值。
控制系統休眠
Android設備的休眠和喚醒主要基於WakeLock機制。WakeLock是一種上鎖機制,只要有進程獲得了WakeLock鎖系統就不會進 入休眠。例如,在下載文件或播放音樂時,即使休眠時間到了,系統也不能進行休眠。WakeLock可以設置超時,超時后會自動解鎖。
應用使用WakeLock功能前,需要先使用new WakeLock()接口創建一個WakeLock類對象,然后調用它的acquire()方法禁止系統休眠,應用完成工作后調用release()方法來恢復休眠機制,否則系統將無法休眠,直到耗光所有電量。
WakeLock類中實現acquire()和release()方法實際上是調用了PowerManagerService的acquireWakeLock()和releaseWakeLock()方法。
updatePowerStateLocked為PowerManagerService的核心函數;在執行完申請鎖,釋放鎖,用戶事件,強制喚醒/睡眠等操作都需要調用updatePowerStateLocked()來更新電源狀態,
wakelock
Wakelock是android系統上特有的電源管理機制,只要有應用拿着這個鎖,系統就不能進入睡眠狀態,在上層不同的應用程序可以持有多個不同的wakelock鎖,但是反映到底層就只有三種:控制系統休眠PowerManagerService.WakeLock,控制屏幕顯示的PowerManagerService.Display和控制電源狀態改變通知的PowerManagerService.Broadcasts。
PowerManagerService有加鎖和解鎖兩種狀態,加鎖有兩種方式:
第一種是永久的鎖住,這樣的鎖除非顯式的放開,否則是不會解鎖的,所以這種鎖用起來要非常的小心(默認)。
第二種鎖是超時鎖,這種鎖會在鎖住后一段時間解鎖。
相關接口
newWakeLock():創建wakelock鎖,當外界創建wakelock之前需要創建PowerManager的服務對象,然后其創建wakelock鎖:
setReferenceCounted()設置計數鎖和非計數鎖;wakelock分為計數鎖和非計數鎖兩種:計數鎖是應用調用一次acquire申請必定會對應一個release來釋放;非計數鎖應用調用多次acquire,調用一次release就可釋放前面acquire的鎖。在申請wakelock時默認申請的是計數鎖。
isHeld()判斷一個wakelock鎖是否acquire申請了,但是沒有release釋放;
acquire()和release()方法來申請和獲取鎖,acquire申請鎖有兩種:
acquire():申請wakelock永久鎖(默認),需要手動release
acquire(long timeout) :申請wakelock超時鎖,timeout為設置的超時時間,超時自動release掉該wakelock。
Andoid的控制系統休眠是用wakelock機制,應用程序在使用wakelock前,必須在其manifest.xml文件中注冊android.permission.WAKE_LOCK權限,應用要使用wakelock,需先調用newWakeLock()創建wakelock,然后acquire()申請該鎖,從而阻止系統休眠,在處理完事物之后要及時調用release()來釋放wakelock,否則系統始終無法進入睡眠狀態,直到電量耗光。
Wakelock類型
鎖類型 |
cpu |
screen |
Keyboard |
電源鍵影響 |
應用情景 |
備注 |
PARTIAL_WAKE_LOCK = 0x00000001 |
On |
Off |
Off |
不受 |
聽音樂,后台下載等 |
|
SCREEN_DIM_WAKE_LOCK = 0x00000006 |
On |
Dim |
Off |
受 |
即將進入滅屏休眠狀態時 |
這三種類型的鎖,在6.0以后版本會逐漸被拋棄使用,改用WindowManager. LayoutParams 的一個參數FLAG_KEEP_SCREEN_ON 來替換上述三種類型的鎖,因為它將由平台被准確地管理用戶應用程序之間的動作,並且不需要特殊的權限。 |
SCREEN_BRIGHT_WAKE_LOCK = 0x0000000a |
On |
Bright |
Off |
受 |
看電子書,看視頻,操作屏幕沒有操作到鍵盤等 |
|
FULL_WAKE_LOCK = 0x0000001a |
On |
Bright |
On |
受 |
來電話,鬧鍾觸發等 |
|
PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020 |
Off |
Bright/Off |
Off |
受 |
打電話靠近或遠離手機時 |
需要設備支持距離傳感器,不能和ACQUIRE_CAUSES_WAKEUP一起用 |
DOZE_WAKE_LOCK = 0x00000040 |
Off |
Off |
Off |
受 |
低電狀態,doze模式下,允許cpu進入suspend狀態 |
系統支持doze |
DRAW_WAKE_LOCK = 0x00000080 |
On |
Off |
Off |
不受 |
保持設備喚醒,能正常進行繪圖 |
windowManager允許應用在dozing狀態繪制屏幕 |
ACQUIRE_CAUSES_WAKEUP = 0x10000000 |
說明:正常情況下,獲取wakelock是不會喚醒設備的,加上該標志之后,acquire wakelock也會喚醒設備,該標志常用於鬧鍾觸發,藍牙鏈接提醒等場景。 |
不能和PARTIAL_WAKE_LOCK 一起用 |
||||
ON_AFTER_RELEASE = 0x20000000 |
說明:和用戶體驗有關,當wakelock釋放后如果沒有該標志,屏幕會立即黑屏,如果有該標志,屏幕會亮一小會然后在黑屏。 |
不能和PARTIAL_WAKE_LOCK 一起用 |
申請鎖——acquire
應用創建鎖之后,必須通過acquire申請鎖才能持有wakelock鎖,才能保證系統處於喚醒狀態來使用電力資源。PowerManager和PowerManagerService中都有wakelock內部類,在PowerManagerService啟動之時便將其注冊到Binder服務端,其客戶端代理調用由PowerManager來完成,故PowerManager中acquire申請鎖具體操作實現在服務端PowerManagerService中的acquireWakeLock()。
申請鎖流程圖如下
在acquireWakeLock中檢查完權限后,調用到acquireWakeLockInternal()中
int index = findWakeLockIndexLocked(lock);
boolean notifyAcquire;
if (index >= 0) {
wakeLock = mWakeLocks.get(index);
if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName, uid, pid, ws, historyTag);
wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid);
}
notifyAcquire = false;
} else {
wakeLock = new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid);
acquireWakeLockInternal()方法首先檢查mWakeLock列表中是否已經存在有相同的wakelock,如果存在那么調用wakeLock.updateProperties()更新該wakelock的屬性值,如果不存在,那么創建wakelock鎖,然后更新電源狀態:
applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
在申請wakelock鎖時候applyWakeLockFlagsOnAcquireLocked()檢查申請鎖是否帶有ACQUIRE_CAUSES_WAKEUP標志,若帶有該標志,則則會直接調用到wakeup調用線程上,執行喚醒操作,該標志在鬧鍾,藍牙鏈接,來電以及短信窗口提醒等功能中有應用;mDirty記錄申請鎖的操作,最后調用到updatePowerStateLocked中更新電源狀態。
釋放鎖——release
在應用持有wakelock鎖執行完相應的事物之后,要及時調用release(),來執行釋放wakelock操作,否則會導致設備保持喚醒,遲遲進入不了睡眠狀態,嚴重影響手機功耗。正常情況下,每個wakelock的acquire都應該對應一個release操作,release操作和acquire流程相似。
其流程如下圖所示
接口是PowerManager的release()接口,具體實現在PowerManagerService的releaseWakeLock(),releaseWakeLock()檢查完權限之后,到releaseWakeLockInternal()處理
int index = findWakeLockIndexLocked(lock);
WakeLock wakeLock = mWakeLocks.get(index);
wakeLock.mLock.unlinkToDeath(wakeLock, 0);
removeWakeLockLocked(wakeLock, index);
}
}
獲取要釋放的wakelock在mWakeLocks列表中的下標值,然后調用removeWakeLockLocked()刪除該wakelock,刪除該wakelock的操作如下;
private void removeWakeLockLocked(WakeLock wakeLock, int index) {
mWakeLocks.remove(index);
notifyWakeLockReleasedLocked(wakeLock);
applyWakeLockFlagsOnReleaseLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
}
首先在mWakeLocks列表中將該wakelock刪除,然后通知系統該wakelock已經被刪除,applyWakeLockFlagsOnReleaseLocked()中判斷該wakelock是否有ON_AFTER_RELEASE標志,如果帶有這個標志釋放wakelock鎖后系統不會立即進入黑屏狀態,而是屏幕繼續亮一小會再滅屏,在藍牙連接,小區廣播,來電等功能場景下有應用,由於wakelock被釋放了,所以mDirty置為DIRTY_WAKE_LOCKS。再次調用到updatePowerStateLocked()更新電源狀態。
Wakelock在流程反映
先看一張wakelock工作的流程圖
SuspendBlocker是用來保持CPU喚醒的一種鎖,前面講到,上層應用可以申請許多不同的wakelock鎖,所有的wakelock鎖放到mWakelocks的一個列表中,在updateWakeLockSummaryLocked中將所有的wakelock通過置位的方法統計到一個集合變量mWakeLockSummary中,列表中有多個相同類型的wakelock,反映在相應的二進制位上是一樣的,所以只需要一個wakelock就可以保證CPU喚醒,
我們回到PowerManagerService的構造函數,
mWakeLockSuspendBlocker=createSuspendBlockerLocked("PowerManagerService.WakeLocks");
mDisplaySuspendBlocker=createSuspendBlockerLocked("PowerManagerService.Display");
里面創建了兩個變量mWakeLockSuspendBlocker和mDisplaySuspendBlocker,在PowerManagerService啟動時調用SystemReady時有:
mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
mPolicy);
mWirelessChargerDetector = new WirelessChargerDetector(sensorManager, createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"), mHandler);
創建兩個SuspendBlocker對象mNotifier 和mWirelessChargerDetector ,mNotifier 是在電池屏幕亮度發生迅速提升時(如暗屏觸摸),應用點亮屏幕時或者使用無線充電時,申請PowerManagerService.Broadcasts的SuspendBlocker鎖。當檢測到用無線充電時,會申請PowerManagerService.WirelessChargerDetector標志的SuspendBlocker鎖
SuspendBlocker是一個抽象類。其創建的對象是其實現類SuspendBlockerImpl對象,SuspendBlockerImpl類中維護了一個計數器mReferenceCount,調用acquire時候,計數器加1,當計數器值為1時,調用到底層的nativeAcquireSuspendBlocker方法,申請wakelock;調用release時,計數器減1,當計數器為0時候調用底層的nativeReleaseSuspendBlocker方法,釋放wakelock;這兩個方法如下
static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) {
ScopedUtfChars name(env, nameStr);
acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}
static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) {
ScopedUtfChars name(env, nameStr);
release_wake_lock(name.c_str());
}
其 acquire_wake_lock(release_wake_lock)直接調用到HAL層的power.c向系統文件節點/sys/power/wake_lock(/sys/power/unwake_lock)中寫數據,這里寫數據就是前面構造函數和Systemready中創建變量時的參數”PowerManagerService.WakeLocks”,”PowerManagerService.Display”等參數
因此,Android實現防止系統休眠的功能是通過向設備文件“sys/power/wake_lock”中寫數據來完成的,如果寫的是“PowerManagerService.WakeLocks”,系統將不能進入休眠狀態,但是屏幕會關閉;如果寫的是“PowerManagerService.Display”,則屏幕不會關閉。如果系統要恢復休眠,再向設備文件“sys/power/wake_unlock”中寫入同樣的字符串就行了。
電源管理核心——updatePowerStateLocked
updatePowerSateLocked()方法為PowerManagerService之核心,前面分析了接口調用都是更新成員變量值,最后都是需要調用到updatePowerSateLocked()來更新電源狀態。
private void updatePowerStateLocked() {
if (!mSystemReady || mDirty == 0) {
return;
}
if (!Thread.holdsLock(mLock)) {
Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");
try {
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
updateScreenBrightnessBoostLocked(mDirty);
final long now = SystemClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
updateDreamLocked(dirtyPhase2, displayBecameReady);
finishWakefulnessChangeIfNeededLocked();
updateSuspendBlockerLocked();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
updatePowerStateLocked()方法並不長,但是其涉及調用的方法較為復雜,還是不容以理解,下面作詳細分析:
電源狀態更新,最重要的標志變量為mDirty,當與電源相關的狀態改變,都會通過置位的方法反映在mDirty集合變量了,比如充電狀態,屏幕亮度,電源設置,喚醒狀態等發生改變都會在mDirty中反映出來
1).updateIsPoweredLocked():該方法主要是通過調用BatteryService更新電池狀態,包括電池充電,電量等級等狀態。
mIsPowered=mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
mPlugType = mBatteryManagerInternal.getPlugType();
mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
mIsPowered表示是否在充電,mPlugType 表示充電類型,mBatteryLevel 表示當前電量等級,mBatteryLevelLow 表示是否為低電水平;
2).updateStayOnLocked()來更新變量mStayOn的值,如果mStayOn如果為true,則屏幕長亮,在Setting中可以設置充電時候屏幕長亮,如果Setting中設置了該選項,updateIsPoweredLocked檢查到正在充電,會將mStayOn置為true。
3).接下來就是一個無限循環,但是這個循環最多執行兩次便退出了,這一點將在后面詳細分析,循環中最先調用updateWakeLockSummaryLocked,來將系統中所有的wakelock鎖更新到一個集合變量mWakeLockSummary中,也就是不管系統中創建了多少個wakelock,一個便足以阻止系統進入睡眠狀態,因此這里將所有的wakelock總結后通過置位的方法保存到一個變量中,應用創建wakelock時會指定wakelock的類型,不同的wakelock類型置於不同的位。
4)循環調用的第二個方法是updateUserActivitySummaryLocked,在方法中根據系統最后一次調用userActivity()方法的時間計算現在是否可以將屏幕狀態的變量mUserActivitySummary置成USER_ACTIVITY_SCREEN_BRIGHT(亮屏)還是USER_ACTIVITY_SCREEN_DIM(暗屏)等,這在上面已經分析過了,這里就不多贅述了。
5).循環中調用的第三個方法是updateWakefulnessLocked(),這個方法是循環結束的關鍵,如果它的返回值為true,則表示wakefulness的狀態發生改變了,降繼續循環重新調用前面兩個方法更新userActivity和Wakelock集合變量。如果能第二次調用updateWakefulnessLocked()一定會返回false,繼而跳出循環,方法實現為
private boolean updateWakefulnessLocked(int dirty) {
boolean changed = false;
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
| DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
| DIRTY_DOCK_STATE)) != 0) {
if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
if (DEBUG_SPEW) {
}
final long time = SystemClock.uptimeMillis();
if (shouldNapAtBedTimeLocked()) {
changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
} else {
changed = goToSleepNoUpdateLocked(time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
}
}
}
return changed;
}
第一個if非常用以滿足,第二個條件要求mWakefulness為WAKEFULNESS_AWAKE,且isItBedTimeYetLocked()為true,此函數官方解釋為當系統馬上要進入睡眠狀態時會返回true,也就是當系統一直處於活躍狀態,則其返回false所以,updateWakefulnessLocked()方法返回值為false,那么這個死循環只用執行一次就跳出了,這里假定系統一段時間未被操作,即將接下來就要調用進入睡眠狀態,則isItBedTimeYetLocked()函數返回true,接下來就該調用shouldNapAtBedTimeLocked()方法了,該方法檢查又沒有設置睡眠之前啟動動態屏保或者插在座充上啟動屏保,如果設置了,調用napNoUpdateLocked(),沒有設置則調用gotoSleepNoUpdateLocked()。
napNoUpdateLocked方法主要代碼如下:
if (eventTime < mLastWakeTime || mWakefulness != WAKEFULNESS_AWAKE
|| !mBootCompleted || !mSystemReady) {
return false;
}
try {
mSandmanSummoned = true;
setWakefulnessLocked(WAKEFULNESS_DREAMING, 0);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;
如果if語句中有一項成立則返回false,則跳出死循環,當時如果第一次調用該方法,正常情況下當為false,如果第二次調用到此肯定會返回false,因為第二次調用時mWakefulness 為,WAKEFULNESS_DREAMING。而gotoSleepNoUpdateLocked前面已作分析,這里就不多講了。
6).跳出循環則調用到updateDisplayPowerStateLocked()更新屏幕顯示,當前面如果申請了亮屏鎖和更新userActivity時,mUserActivitySummary帶有USER_ACTIVITY_SCREEN_BRIGHT標志,則會將mDisplayPowerRequest.policy置為POLICY_BRIGHT,這個標志在DisplayPowerController中會將屏幕從滅屏狀態下喚醒。
7).接下來調用updateDreamLocked(),更新屏保模式,具體處理在handleSandman()函數中,該函數是當系統進入/退出屏保狀態或者Dozing下狀態調用,下面詳細分析該函數代碼:
synchronized (mLock) {
mSandmanScheduled = false;
wakefulness = mWakefulness;
if (mSandmanSummoned && mDisplayReady) {
startDreaming = canDreamLocked() || canDozeLocked();
mSandmanSummoned = false;
} else {
startDreaming = false;
}
}
這里主要更新變量startDreaming,當前狀態如果可以進入屏保或者Dozing狀態,則置為true,否則置為false。,如果設置了屏保,則滅屏之后會進入一段時間屏保,然后滅屏;如果沒有設置屏保則默認進入dozing(打盹)狀態。
final boolean isDreaming;
if (mDreamManager != null) {
mDreamManager.stopDream(false /*immediate*/);
mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
}
isDreaming = mDreamManager.isDreaming();
} else {
isDreaming = false;
}
此處判斷屏保服務是否啟動了,而在屏保服務實在系統啟動時在StartOtherService中隨系統啟動,在次重啟屏保服務
if (isItBedTimeYetLocked()) {
goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
} else {
wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), "android.server.power:DREAM",
Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID);
updatePowerStateLocked();
} else if (wakefulness == WAKEFULNESS_DOZING) {
if (isDreaming) {
return; // continue dozing
}
如果屏保結束,判斷是進入喚醒狀態還是進入睡眠狀態,然后更新電源狀態。若是還為到時,且wakefulness為WAKEFULNESS_DOZING則返回,繼續處於dozing狀態。
reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
updatePowerStateLocked();
最后時間及到,真正進入睡眠狀態調用reallyGoToSleepNoUpdateLocked,將mWakefulness置為WAKEFULNESS_ASLEEP,再次更新電源狀態時將wakelock鎖盡數釋放,屏幕完全滅屏,進入睡眠狀態。
8).finishWakefulnessChangeIfNeededLocked()方法未作實際操作,僅僅通知系統mWakefulness改變更新已經完成。
9)最后一個函數updateSuspendBlockerLocked();由於系統中可能需要釋放最后一個維持CPU喚醒或者維持屏幕亮滅的Blocker,所以必須將所有事物處理完成,再執行該操作。由於該函數是由PowerManagerService調用到底層的唯一入口,所以十分重要:
private void updateSuspendBlockerLocked() {
final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
final boolean autoSuspend = !needDisplaySuspendBlocker;
final boolean interactive = mDisplayPowerRequest.isBrightOrDim();
needWakeLockSuspendBlocker變量判斷wakelock是否帶有WAKE_LOCK_CPU標志來決定是否為需要保持CPU喚醒,其中能維持CPU喚醒的wakelock類型有:PARTIAL_WAKE_LOCK,FULL_WAKE_LOCK,SCREEN_BRIGHT_WAKE_LOCK,SCREEN_DIM_WAKE_LOCK,DRAW_WAKE_LOCK。needDisplaySuspendBlocker 則是表示是否維持屏幕亮滅的變量,true表示維持屏幕亮,false表示可以關閉屏幕。
if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
mWakeLockSuspendBlocker.acquire();
mHoldingWakeLockSuspendBlocker = true;
}
if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
mDisplaySuspendBlocker.acquire();
mHoldingDisplaySuspendBlocker = true;
}
........
if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
mWakeLockSuspendBlocker.release();
mHoldingWakeLockSuspendBlocker = false;
}
if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
mDisplaySuspendBlocker.release();
mHoldingDisplaySuspendBlocker = false;
}
上面兩段代碼是向下申請PowerManagerService.WakeLocks和PowerManagerService.Display類型的wakelock鎖的入口,mHoldingWakeLockSuspendBlocker表示當前是否持有cpu喚醒鎖,如果持有則不必向下繼續申請鎖,如果維持有,且需要維持CPU喚醒則需要,申請CPU喚醒鎖。(Display邏輯與其相似,就不做分析)調用到 SuspendBlockerImpl的acquire()函數中
public void acquire() {
synchronized (this) {
mReferenceCount += 1;
if (mReferenceCount == 1) {
if (DEBUG_SPEW) {
Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
}
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
nativeAcquireSuspendBlocker(mName);
}
}
}
前面已經說過SuspendBlockerImpl維持了一個計數標志mReferenceCount 當為1的時候申請鎖,為0時釋放鎖。nativeAcquireSuspendBlocker()函數通過JNI調用到native層的com_android_server_power_PowerManagerService.cpp文件的nativeAcquireSuspendBlocker函數,而后的詳細流程在前面wakelock已經講的很清楚了 。
至此電源管理核心函數updatePowerStateLocked()基本分析完成。
下圖為概述電源管理核心函數處理流程及作用
總結
Android的電源管理提出wakelock的是一套全新的機制,跟我們C++里使用的智能指針(Smart pointer),借用智能指針的思想來設計電源的使用和分配。Smart Pointer都是引用,申請引用則它的引用計數會自動加1,取消引用則引用計數減1,使用了智能指針的對象,當它的引用計數為0時,則該對象會被回收掉。同樣, 我們的wake_lock也保持使用計數,只不過這種“智能指針”的所使用的資源不再是內存,而是電量。應用程序會通過特定的WakeLock去訪問硬件,然后硬件會根據引用計數是否為0來決定是不是需要關閉這一硬件的供電。
電源管理於Framework層恰似為一個策略控制器,來掌控不同狀態下的電源狀態改變和更新,PowerManager作為一個重要服務在開機啟動時便啟動並注冊到系統內,當上層應用程序需要使用該服務只需調用PowerManager開放接口即可控制系統某些電源狀態的改變,而PowerManagerServcie提供服務端處理邏輯,在交互中做主要電源控制工作,其中與之交互的模塊最頻繁的為Display,Window,Light,和Battery等,其交互之復雜,聯系之緊密實非一言以蔽之。