【android睡眠喚醒 三】PowerManagerService框架解析
一、電源管理框架
PowerManagerServcie是android系統電源管理的核心服務,它在Framework層建立起一個策略控制方案,向下決策HAL層以及kernel層來控制裝置待機狀態,控制顯示屏,背光燈,距離感測器,光線感測器等硬體裝置的狀態。向上提供給應用程式相應的操作介面,比如聽音樂時持續保持系統喚醒,應用通知來臨喚醒手機螢幕等場景等,PMS也是系統的核心服務,Android的電源管理主要是通過wakelock機制來管理系統的狀態,整個android電源管理,可以分為如下四個層次:
1. 應用介面層(PowerManager.java)
PowerManager中開放給應用一系列介面,應用可以呼叫PM的介面申請wakelock,喚醒系統,或使系統進入睡眠等操作;
2. Framework層(PowerManagerService.java)
應用呼叫PowerManager開放的介面,來對系統進行一些列的操作是在PowerManagerService中完成的,PowerManagerService計算系統中和Power 相關的計算,是整個電源管理的決策系統。同時協調Power如何與系統其 它模組的互動,比如亮屏,暗屏,系統睡眠,喚醒等等;
3. HAL層(Power.c)
該層只有一個power.c檔案,該檔案通過上層傳下來的引數,向/sys/power/wake_lock 或者/sys/power/wake_unlock檔案節點寫資料來與kernel進行通訊,主要功能是申請/釋放鎖,維持螢幕亮滅;
4. 核心層(kernel/Power)
核心層實現電源管理的方案主要包含三個部分:
(1)Kernel/power/:實現了系統電源管理框架機制;
(2)Arch/arm(ormips or powerpc)/mach-XXX/pm.c:實現對特定板的處理器電源管理;
(3)drivers/power:是裝置電源管理的基礎框架,為驅動提供了電源管理介面。
整體框架圖如下(借鑑):
二、電源管理服務PowerManagerService
1. PowerServiceManager啟動流程
PowerManagerService是在SystemServer中建立的,其中在SystemServer在系統啟動的時候會啟動三類服務:引導關鍵服務、核心服務和其他服務,PowerManagerService是作為一個核心服務加入到ServiceManager中,啟動服務的方式如下:
startBootstrapServices(); //啟動引導服務
startCoreServices();//啟動核心服務
startOtherServices();//其他服務
(1)核心服務建立以後,PowerServiceManager的服務隨之建立(SystemServer.java):
mPowerManagerService =
mSystemServiceManager.startService(PowerManagerService.class);
(2)startService方法(SystemServiceManager.java ):
public T startService(Class serviceClass) {
mServices.add(service); //註冊服務到服務列表中去
ervice.onStart();//啟動服務
return service;//返回啟動的服務
}
在startService方法中,利用反射方法構造PowerManagerService的物件,將它新增到本地service變數中,然後呼叫了PowerManagerService的onStart方法。
(3)PowerManagerService構造方法(PowerManagerService.java)
public PowerManagerService(Context context) {
//建立一個HandlerThread,並啟動
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
mHandlerThread.start();
//基於這個HandlerThread建立相關的Handler物件,用於向handlerThread中傳送訊息
mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
synchronized (mLock) {
mWakeLockSuspendBlocker =
createSuspendBlockerLocked("PowerManagerService.WakeLocks");
mDisplaySuspendBlocker =
createSuspendBlockerLocked("PowerManagerService.Display");
mDisplaySuspendBlocker.acquire();
//呼叫native層初始化
nativeInit();
nativeSetAutoSuspend(false);
nativeSetInteractive(true);
nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
}
}
在PowerManagerService的建構函式建立了一個訊息傳送和處理的Handler和兩種WakeLock鎖:
a、PowerManagerService.WakeLocks,主要用於控制CPU的喚醒;
b、PowerManagerService.Display 主要用於控制螢幕的點亮和熄滅;
(4)PowerManagerService的Onstart方法:
在PowerManagerService的Onstart方法中:
public void 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是否空閒。
(4)系統準備工作:
在PowerManagerService建立之後會呼叫systemReady做一些初始化相關的操作,獲取與PowerManager相關的本地服務:
public void systemReady(IAppOpsService appOps) {
synchronized (mLock) {
//第一步:初始化相關的變數
mSystemReady = true;
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();
//獲取螢幕的亮度值,最大亮度,最小亮度,預設亮度
SensorManager sensorManager = new SystemSensorManager
(mContext, mHandler.getLooper());
//獲取感測器管理服務
mBatteryStats = BatteryStatsService.getService();
//初始化電量統計服務
mNotifier = new Notifier(
Looper.getMainLooper(),
mBatteryStats,mAppOps,
createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
mPolicy);
mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
createSuspendBlockerLocked
("PowerManagerService.WirelessChargerDetector"),mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
//settings的監聽器
mLightsManager = getLocalService(LightsManager.class);
//LED指示燈管理服務
mAttentionLight =
mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
// 初始化螢幕顯示服務
mDisplayManagerInternal.initPowerManagement(
mDisplayPowerCallbacks, mHandler, sensorManager);
//第二步:註冊相關的BroadCastReceiver
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
//註冊電池變化的接收器
filter = new IntentFilter();
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
//註冊屏保開始和結束的接收器
filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver
(new UserSwitchedReceiver(), filter, null, mHandler);
//註冊切換使用者的接收器
filter = new IntentFilter();
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
//第三步.註冊設定變化的監聽器
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SCREENSAVER_ENABLED),
false, mSettingsObserver, UserHandle.USER_ALL);
……
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DOUBLE_TAP_TO_WAKE),
false, mSettingsObserver, UserHandle.USER_ALL);
//雙擊喚醒螢幕
// 第四步: 從檔案讀取預設的配置資訊
readConfigurationLocked();
//讀取設定資訊,並更新相關的變數
updateSettingsLocked();
// 第五步
mDirty |= DIRTY_BATTERY_STATE;
//更新電源的相關資訊
updatePowerStateLocked();
}
}
SystemReady所完成的工作如下:
-獲取與PowerManagerServcie相關的系統服務以及本地服務;
-獲取螢幕最大,最小以及預設亮度值;
-建立SensorManager 物件,用於和SensorService互動;
-建立Notifier物件,用於通知系統中電源狀態的改變;
-呼叫DisplayManagerService的initPowerManagement()方法來初始化Power顯示模組。
-註冊SettingsObserver監聽系統設定的變化。
整體框架如下圖(借鑑):
(5)PowerManager相關介面:
PowerManager嚮應用提供了相應的介面,以供應用程式呼叫,來改變系統待機狀態,螢幕狀態,螢幕亮度等,PowerManager是PowerManagerService的代理類,PowerManager向上層應用提供互動的介面,具體的處理工作在PowerManagerService中完成。下面介紹PowerManager中提供的相應介面作用:
a、Wakeup():
強制系統從睡眠狀態喚醒,此介面對應用是不開放的,應用想喚醒系統必須通過設定亮屏標誌;
b、gotoSleep():
強制系統進入到睡眠狀態,此介面也是應用不開放;
c、userActivity():
向PowerManagerService報告影響系統休眠的使用者活動,重計算滅屏時間,背光亮度等,例如觸屏,劃屏,power鍵等使用者活動;
d、Wakelock:
wakelock是PowerManager的一個內部類,提供了相關的介面來操作wakelock鎖,比如newWakeLock()方法來建立wakelock鎖,acquire()和release()方法來申請和釋放鎖;
e、isDeviceIdleMode():
返回裝置當前的狀態,如果處於Idle狀態,則返回true,Idle狀態是在手機長時間沒有被使用以及沒有運動的情況下,手機進入到一種Doze低功耗的模式下,這種狀態下手機可能會關掉網路資料訪問,可以通過監視DEVICE_IDLE_MODE_CHANGED這個廣播資訊,來監控手機狀態的改變。
三、系統喚醒wakeup/睡眠goToSleep
1、PowerManager的wakeup介面,可供應用程式呼叫,來強制喚醒系統,如果該裝置處於睡眠狀態,呼叫該介面會立即喚醒系統,比如按Power鍵,來電,鬧鐘等場景都會呼叫該介面。PowerManager的wakeup介面屬性是@hide的,所以對於上層應用是不可見的,上層應用要喚醒系統大都依靠兩種方式:
(1)在應用啟動Activity時候設定相應的window的flags,通過WMS來喚醒系統;
(2)在應用申請wakelock鎖時附帶ACQUIRE_CAUSES_WAKEUP標誌。
/**
* @hide
*/
public void wakeUp(long time, String reason) {
try {
mService.wakeUp(time, reason, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
在wakeUp中呼叫PowerManagerService的wakeUp方法如下:
public void wakeUp(long eventTime, String reason, String opPackageName) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException
("event time must not be in the future");
}
//檢查android.Manifest.permission.DEVICE_POWER的許可權
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
//呼叫自身的wakeUpInternal介面
wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
在wakeUpInternal中,通過wakeUpNoUpdateLocked來做喚醒系統的相關通知工作,最後呼叫PowerManagerService的核心介面updatePowerStateLocked來更新電源狀態,從而完成亮屏前的圖形繪製工作及亮屏動作。
private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName,
int opUid) {
synchronized (mLock) {
if (mIPOShutdown && reason != PowerManager.WAKE_UP_REASON_SHUTDOWN)
return;
if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {
updatePowerStateLocked();
}
}
}
2、系統睡眠goToSleep
PowerManager的gotoSleep()介面是@hide屬性,因此對於上層應用是不開放的,裝置強制進入睡眠狀態,在處理一些滅屏按鍵事件時,會通過WMS來呼叫PowerManager的gotoSleep介面,一般在系統一段時間沒有被操作時,系統將會自動呼叫gotoSleep函式,讓其進入到睡眠模式;與wakeup喚醒一樣,PowerManager的gotoSleep()在PowerManagerService中處理,PMS中的gotoSleep()首先檢查呼叫者是否擁有android.Manifest.permission.DEVICE_POWER許可權。然後呼叫到goToSleepInternal()中處理:
/**
* @hide Requires signature permission.
*/
public void goToSleep(long time, int reason, int flags) {
try {
//呼叫PMS的goToSleep方法
mService.goToSleep(time, reason, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
public void goToSleep(long eventTime, int reason, int flags) {
//檢測許可權android.Manifest.permission.DEVICE_POWER的許可權
...
goToSleepInternal(eventTime, reason, flags, uid);
}
下面繼續看goToSleepInternal,在goToSleepNoUpdateLocked()中完成傳送了將要休眠的通知,然後修改了Wakefulness,將其置成WAKEFULNESS_DOZING,將mDirty |= DIRTY_WAKEFULNESS置位,更多的實際工作在updatePowerStateLocked()中完成。在updateDreamLocked中完成真正進入睡眠的過程:
private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
synchronized (mLock) {
if (mProximityPositive && reason ==
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
mDirty |= DIRTY_WAKEFULNESS;
mWakefulness = WAKEFULNESS_ASLEEP;
updatePowerStateLocked();
return;
}
if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
updatePowerStateLocked();//PMS核心
}
}
}
三、系統睡眠喚醒策略wakelock
Android裝置的休眠和喚醒主要基於WakeLock機制。WakeLock是一種上鎖機制,只要有程序獲得了WakeLock鎖系統就不會進 入休眠。例如,在下載檔案或播放音樂時,即使休眠時間到了,系統也不能進行休眠。WakeLock可以設定超時,超時後會自動解鎖。
應用使用WakeLock功能前,需要先使用new WakeLock()介面建立一個WakeLock類物件,然後呼叫它的acquire()方法禁止系統休眠,應用完成工作後呼叫release()方法來恢復休眠機制,否則系統將無法休眠,直到耗光所有電量。WakeLock類中實現acquire()和release()方法實際上是呼叫了PowerManagerService的acquireWakeLock()和releaseWakeLock()方法。updatePowerStateLocked為PowerManagerService的核心函式;在執行完申請鎖,釋放鎖,使用者事件,強制喚醒/睡眠等操作都需要呼叫updatePowerStateLocked()來更新電源狀態。
未完待續…
作者:frank_zyp
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。
本文無所謂版權,歡迎轉載。