1. 程式人生 > >【android睡眠喚醒 三】PowerManagerService框架解析

【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();
            }
        }
    }

wakeUp流程

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
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。
本文無所謂版權,歡迎轉載。