首頁 > 軟體

初識Android PowerManagerService省電模式

2022-08-24 14:01:02

前言

最近遇到一些關於省電模式、電量消耗、Doze模式,等等相關問題。於是,我決定對它們進行徹底分析,那就先從省電模式開啟。

功能介紹

可以在 Settings->Battery->Battery Saver 介面進行省電模式的操作,如下圖:

介面中有三個開關,它們的意思如下:

  • Use Battery Saver : 開啟/關閉省電模式。
  • Set a Schedule : 設定一個電量百分比閾值,當電量低於這個閾值的時候,就會觸發省電模式。設定閾值的介面如下圖

  • Turn off when charging : 這個開關的意思不能按表面意思理解,它真正的意思是,省電模式被開啟的情況下,拔掉充電器或者重啟,如果電量百分比大於90%,那麼不會再次開啟省電模式。

由於省電模式的功能涉及的程式碼比較多,本文只分析省電模式環境的初始化,涉及的檔案路徑如下

  • frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
  • frameworks/base/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
  • frameworks/base/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
  • frameworks/base/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java

環境

省電模式屬於 PowerManagerService 的一部分功能,下面列出與省電模式環境相關的程式碼:

PowerManagerService(Context context, Injector injector) {
    super(context);
    // ...

    // Class to decide whether to turn on battery saver mode for specific services.
    mBatterySaverPolicy =
            mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);

    // Responsible for battery saver mode transition logic.
    mBatterySaverController = mInjector.createBatterySaverController(mLock, mContext,
            mBatterySaverPolicy, mBatterySavingStats);

    //  Decides when to enable / disable battery saver.
    mBatterySaverStateMachine = mInjector.createBatterySaverStateMachine(mLock, mContext,
            mBatterySaverController);
   
    // ...
}

與省電模式相關的類有三個,BatterySaverPolicyBatterySaverControllerBatterySaverStateMachine

介紹下三個類的作用:

  • BatterySaverPolicy : 省電模式的策略。
  • BatterySaverController : 控制省電模式的開啟/關閉,並根據省電模式策略,控制某些功能,例如控制 CPU 頻率。
  • BatterySaverStateMachine : 一個狀態機,管理著省電模式相關的狀態,並通過 BatterySaverController 控制省電模式的開啟/關閉。

這裡建立三個類物件的方式,就是呼叫建構函式,下面大致看下這幾個建構函式

public BatterySaverPolicy(Object lock, Context context, BatterySavingStats batterySavingStats) {
    super(BackgroundThread.getHandler());
    mLock = lock;
    mHandler = BackgroundThread.getHandler();
    mContext = context;
    mContentResolver = context.getContentResolver();
    mBatterySavingStats = batterySavingStats;
}
public BatterySaverController(Object lock, Context context, Looper looper,
        BatterySaverPolicy policy, BatterySavingStats batterySavingStats) {
    mLock = lock;
    mContext = context;
    mHandler = new MyHandler(looper);
    mBatterySaverPolicy = policy;
    // 注意,監聽了 policy change
    mBatterySaverPolicy.addListener(this);
    
    // FileUpdater 負責向底層檔案節點寫值,例如向CPU頻率的節點寫值,控制CPU頻率
    mFileUpdater = new FileUpdater(context);
    
    mBatterySavingStats = batterySavingStats;

    // 重新整理獲取省電模式的快取
    PowerManager.invalidatePowerSaveModeCaches();
}
public BatterySaverStateMachine(Object lock,
        Context context, BatterySaverController batterySaverController) {
    mLock = lock;
    mContext = context;
    mBatterySaverController = batterySaverController;
    mState = STATE_OFF;
    // Whether or not battery saver should be "sticky" when manually enabled.
    // false
    // 注意了,值為 false,表明這個 battery saver sticky 功能是開啟的
    mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean(
            com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled);
            
    //  Config flag to track default disable threshold for Dynamic power savings enabled battery saver.
    // 80
    mDynamicPowerSavingsDefaultDisableThreshold = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold);
}

BatterySaverController 的建構函式,只需要注意一點,那就是它監聽了省電模式策略的改變,因為這個策略會影響省電模式。

BatterySaverStateMachine 的建構函式,需要注意 mBatterySaverStickyBehaviourDisabled,它表示是否支援 battery saver sticky 功能。注意了,它的值 false,但是表示支援這個功能,而不是不支援。mDynamicPowerSavingsDefaultDisableThreshold 是與動態省電模式相關,讀者可自行分析。

繼續看 PowerManagerService 對省電模式環境的初始化程式碼

public void systemReady(IAppOpsService appOps) {
    synchronized (mLock) {
        // ...
        mBatterySaverController.systemReady();
        mBatterySaverPolicy.systemReady();
        
        // ...
    }   
}

首先看看 BatterySaverController#systemRead()

public void systemReady() {
    // 監聽這些東西,來控制 battery saver
    final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
    filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
    filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
    mContext.registerReceiver(mReceiver, filter);

    // 如果 Runtime 重啟,那麼讀取 /data/system/battery-saver/default-values.xml 儲存的資料,否則刪除這個檔案。
    mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class)
            .isRuntimeRestarted());
    // 這裡的處理邏輯為空
    mHandler.postSystemReady();
}

BatterySaverController 會監聽螢幕亮滅以及 Doze 模式的廣播。

不過只有螢幕亮滅,會實際地影響省電模式,因為螢幕的亮滅會影響互動模式,而省電模式策略會根據手機是否處於互動模式而設定不同的策略。目前,在策略中,只有 CPU 頻率受影響。

再來看看 BatterySaverPolicy#systemReady()

public void systemReady() {
    ConcurrentUtils.wtfIfLockHeld(TAG, mLock);

    // 下面兩個欄位,目前沒有值,但是會控制省電模式策略,進而影響省電模式
    mContentResolver.registerContentObserver(Settings.Global.getUriFor(
            Settings.Global.BATTERY_SAVER_CONSTANTS), false, this);
    mContentResolver.registerContentObserver(Settings.Global.getUriFor(
            Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this);

    // 無障礙相關
    final AccessibilityManager acm = mContext.getSystemService(AccessibilityManager.class);
    acm.addAccessibilityStateChangeListener(enabled -> mAccessibilityEnabled.update(enabled));
    mAccessibilityEnabled.initialize(acm.isEnabled());

    // 汽車相關
    UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
    uiModeManager.addOnProjectionStateChangedListener(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE,
            mContext.getMainExecutor(), mOnProjectionStateChangedListener);
    mAutomotiveProjectionActive.initialize(
            uiModeManager.getActiveProjectionTypes() != UiModeManager.PROJECTION_TYPE_NONE);

    // 監聽 SettingProvider 的 config 表中,關於 battery_saver 名稱空間下的所有欄位值。
    // 目前,這些欄位都為空,但是這些欄位會影響省電模式策略,進而控制省電模式
    DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BATTERY_SAVER,
            mContext.getMainExecutor(), this);
    mLastDeviceConfigProperties =
            DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BATTERY_SAVER);
    onChange(true, null);
}

BatterySaverPolicy 本身有一個預設的策略,但是可以通過 SettingsProvider 中的一些欄位來控制策略,從而控制省電模式影響的功能。但是目前,這些欄位的值都為空,因此並不影響分析,後面或許我另寫一篇文章,分析如何控制省電模式策略。

繼續看 PowerManagerService 對省電模式環境的初始化程式碼

public void onBootPhase(int phase) {
    synchronized (mLock) {
        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
            incrementBootCount();

        } else if (phase == PHASE_BOOT_COMPLETED) {
            // ...

            mBatterySaverStateMachine.onBootCompleted();
            
            // ...
        }
    }
}

BatterySaverStateMachine.onBootCompleted() 程式碼如下:

public void onBootCompleted() {
    putGlobalSetting(Settings.Global.LOW_POWER_MODE, 0);
    runOnBgThread(() -> {

        // 監聽資料庫欄位
        final ContentResolver cr = mContext.getContentResolver();
        // Settings.Global.LOW_POWER_MODE 代表省電模式是否開啟
        cr.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.LOW_POWER_MODE),
                false, mSettingsObserver, UserHandle.USER_SYSTEM);
        // Settings.Global.LOW_POWER_MODE_STICKY 代表 battery saver sticky 功能是否開啟
        cr.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.LOW_POWER_MODE_STICKY),
                false, mSettingsObserver, UserHandle.USER_SYSTEM);
        // Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL 代表觸發省電模式的電量百分比
        cr.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
                false, mSettingsObserver, UserHandle.USER_SYSTEM);
        // Settings.Global.AUTOMATIC_POWER_SAVE_MODE 代表自動省電模式的型別
        // 型別有三個,根據電量百分比觸發,動態省電模式模式,或者none
        cr.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.AUTOMATIC_POWER_SAVE_MODE),
                false, mSettingsObserver, UserHandle.USER_SYSTEM);
        // Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED 代表是否開啟動態省電模式
        cr.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED),
                false, mSettingsObserver, UserHandle.USER_SYSTEM);
        // Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD 代表動態省電模式關閉的閾值
        cr.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD),
                false, mSettingsObserver, UserHandle.USER_SYSTEM);
        // Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED 表示是否開啟了 battery saver sticky auto disable
        // 對應於 Settings->Battery->Battery Saver->Turn off when charging
        cr.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED),
                false, mSettingsObserver, UserHandle.USER_SYSTEM);
        // Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL 表示  battery saver sticky auto disable 的閾值
        cr.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL),
                false, mSettingsObserver, UserHandle.USER_SYSTEM);


        synchronized (mLock) {
            /**
             * If 1, battery saver ({@link #LOW_POWER_MODE}) will be re-activated after the device
             * is unplugged from a charger or rebooted.
             */
            // battery saver sticky 功能是否被開啟
            final boolean lowPowerModeEnabledSticky = getGlobalSetting(
                    Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0;
            if (lowPowerModeEnabledSticky) {
                mState = STATE_PENDING_STICKY_ON;
            }

            mBootCompleted = true;

            // 讀取資料庫欄位,並根據情況,開啟/關閉省電模式
            refreshSettingsLocked();

            // 自動開啟/關閉省電模式
            doAutoBatterySaverLocked();
        }
    });
}

這裡主要是監聽並讀取與省電模式相關的資料庫欄位,由於現在是分析初始化環境,因此這裡不分析觸發省電模式的程式碼。這些欄位值是什麼意思,請注意看註釋。

至此,省電模式環境的初始化程式碼已經分析完畢,你可能對程式碼中提到的一些概念非常迷惑,我剛開啟接觸的時候也是一樣。為了後面程式碼分析的順利進行,現在預備幾個知識

  • 當省電模式開啟後,只要插入了充電器,系統會自動關閉省電模式。
  • 當處於充電模式,系統是禁止開啟省電模式。
  • Battery Saver Sticky : 在省電模式開啟的情況下,拔掉充電器,或者系統重啟,這個 Battery Saver Sticky 模式就會起效,它會再次開啟省電模式。
  • Battery Saver Sticky Auto Disable : 從字面理解意思,就是自動關閉 Battery Saver Sticky 功能。當電量百分比大於某個閾值,預設是90%,在省電模式開啟的情況狂下,拔掉充電器或者重啟,那麼不會再次開啟省電模式。

結束

到此這篇關於初識Android  PowerManagerService省電模式的文章就介紹到這了,更多相關Andorra PowerManagerService內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com