Android PowerManagerService 打开省电模式

Android PowerManagerService 打开省电模式

目录

概要

打开省电模式

BatterySaverStateMachine状态管理

BatterySaverController切换省电模式

BattterySaverPolicy控制省电策略

处理省电模式状态改变

battery saver sticky 模式

结束

概要

初识Android PowerManagerService省电模式对省电模式进行了一个初步认识,介绍了一些概念,以及对省电模式环境初始化的代码进行了简单分析。读者需要仔细阅读第一篇文章,再来看这一篇文章。

打开省电模式,有三种方式:

手动模式,也就是用户手动打开省电模式。

自动模式,用户设置一个电量百分比阈值,当电量低于这个阈值,自动触发省电模式。

动态模式,这种模式其实就是自动模式。根据文档,这个模式是提供给应用,根据情况自动调整触发省电模式的阈值。

本文只关注如下内容:

省电模式的打开过程。

什么是 battery saver sticky 模式。

只要掌握了上面2点内容,自动模式、动态模式,都可以自行分析。

打开省电模式

现在以手动打开省电模式为例,分析省电模式的打开过程。

从初识Android PowerManagerService省电模式可知,在 Settings->Battery->Battery Saver 界面,可以手动打开省电模式,调用代码如下

最终会调用 PowerManagerService 对应的方法:

public boolean setPowerSaveModeEnabled(boolean enabled) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER) != PackageManager.PERMISSION_GRANTED) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); } final long ident = Binder.clearCallingIdentity(); try { return setLowPowerModeInternal(enabled); } finally { Binder.restoreCallingIdentity(ident); } } private boolean setLowPowerModeInternal(boolean enabled) { synchronized (mLock) { // 充电状态下,不允许打开/关闭省电模式 if (mIsPowered) { return false; } mBatterySaverStateMachine.setBatterySaverEnabledManually(enabled); return true; } }

在 AOSP 的设计中,省电模式和充电状态是冲突的。如果设备处于省电模式状态,此时插入充电器,那么一定会关闭省电模式。如果设备处于充电状态,那么是不允许打开省电模式的。

说实话,我不是很认同这种设计。我认为省电模式是用户的强烈个人意愿,只能由用户自己决定打开或者关闭。

BatterySaverStateMachine状态管理

从上面代码可知,打开省电模式时,通过 BatterySaverStateMachine#setBatterySaverEnabledManually() 方法,把指令传给状态机

public void setBatterySaverEnabledManually(boolean enabled) { synchronized (mLock) { updateStateLocked(true, enabled); } }

状态机通过 updateStateLocked() 更新内部状态,然后根据状态执行相应的操作。 注意,这里的第一个参数表示是否是用户手动打开省电模式,值为 true,第二个参数表示是否打开省电模式,根据我们分析的例子,这里的值为 true。

private void updateStateLocked(boolean manual, boolean enable) { if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) { return; // Not fully initialized yet. } switch (mState) { case STATE_OFF: { if (!mIsPowered) { // 非充电模式,才允许操作省电模式 if (manual) { // 手动操作 if (!enable) { return; } // 用户手动打开省电模式 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_MANUAL_ON); hideStickyDisabledNotification(); // 状态切换为 STATE_MANUAL_ON mState = STATE_MANUAL_ON; } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) { // ... 自动模式 } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) { // ... 动态模式 } } break; } // ... } }

状态机里的默认状态是 STATE_OFF,表示省电模式默认关闭。

通过 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_MANUAL_ON); 打开省电模式,然后把状态切换为 STATE_MANUAL_ON。对于每一次状态切换,我们都要注意,因此这会影响下一次状态切换。

private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason) { enableBatterySaverLocked(enable, manual, intReason, reasonToString(intReason)); } private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason, String strReason) { final boolean wasEnabled = mBatterySaverController.isFullEnabled(); // 已经处于省电模式状态 if (wasEnabled == enable) { return; } // 充电中,是不允许打开省电模式的 if (enable && mIsPowered) { return; } mLastChangedIntReason = intReason; mLastChangedStrReason = strReason; mSettingBatterySaverEnabled = enable; // 1. 保存省电模式的状态 putGlobalSetting(Settings.Global.LOW_POWER_MODE, enable ? 1 : 0); // 2. 打开 battery saver sticky 模式 if (manual) { // 用户手动操作省电模式 // mBatterySaverStickyBehaviourDisabled 默认为 false,表示支持 battery saver sticky 模式 setStickyActive(!mBatterySaverStickyBehaviourDisabled && enable); } // 3. 通过 BatterySaverController 打开省电模式 mBatterySaverController.enableBatterySaver(enable, intReason); // 动态省电模式相关 if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) { triggerDynamicModeNotification(); } else if (!enable) { hideDynamicModeNotification(); } }

在打开省电模式之前,首先把数据库 Settings.Global.LOW_POWER_MODE 字段的值保存为 1。

low power 应该翻译为低功耗,俗称省电模式,而 low battery 才应该翻译为低电量,不要混淆了。 源码中 BatteryManagerService#getBatteryLevelLow() 表示电量是否低于自动省电模式的电量百分比,这个函数的命名非常差劲,一度让我误以为是低电量(电量低于15%),其实它表示是否触发了自动省电模式。

第二步,我们要注意了,这里涉及了 battery saver sticky 功能。根据判断条件可知,只有在用户手动操作省电模式的情况下,才会触发 battery saver sticky 功能,来看下 setStickyActive()

private void setStickyActive(boolean active) { // 表示 battery saver sticky 模式已经打开 mSettingBatterySaverEnabledSticky = active; // Settings.Global.LOW_POWER_MODE_STICKY 代表 battery saver sticky功能的状态 putGlobalSetting(Settings.Global.LOW_POWER_MODE_STICKY, mSettingBatterySaverEnabledSticky ? 1 : 0); }

很简单,就是保存状态,表示 battery saver sticky 功能已经打开。

第三步,把打开省电模式的实际操作,交给了省电模式控制器 BatterySaverController

BatterySaverController切换省电模式

现在来看下 BatterySaverController#enableBatterySaver() 如何打开省电模式

public void enableBatterySaver(boolean enable, int reason) { synchronized (mLock) { if (getFullEnabledLocked() == enable) { return; } // 1. 保存省电模式的状态 setFullEnabledLocked(enable); // 2. 更新省电模式策略 if (updatePolicyLevelLocked()) { // 3. 处理省电模式状态的改变 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); } } } private boolean getFullEnabledLocked() { return mFullEnabledRaw; } private void setFullEnabledLocked(boolean value) { if (mFullEnabledRaw == value) { return; } // 刷新省电模式的缓存,客户端可以通过 PowerManager 获取省电模式状态 PowerManager.invalidatePowerSaveModeCaches(); mFullEnabledRaw = value; }

首先使用 mFullEnabledRaw 保存省电模式状态。

然后,更新省电模式的策略。省电模式会影响很多模块的功能,例如,最直观的就是影响屏幕亮度。因此打开省电模式,必须得有一个策略,这些策略影响某些模块的功能。

最后,处理省电模式状态的改变。其中包括切换省电模式,通知省电模式的监听者。

mFullEnabledRaw 表示 full battery saver,其实就是用户用到的省电模式。其实还有一种省电模式 adaptive battery saver,这种省电模式,是通过命令行设置的,应该是与测试相关,执行 adb shell power set-adaptive-power-saver-enabled true 来开启,具体可以参考 PowerManagerShellCommand 类。

BattterySaverPolicy控制省电策略

现在来看下 BatterySaverController#updatePolicyLevelLocked() 如何更新省电模式策略

private boolean updatePolicyLevelLocked() { if (getFullEnabledLocked()) { // 设置省电模式 policy level return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_FULL); } else if (getAdaptiveEnabledLocked()) { return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_ADAPTIVE); } else { return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_OFF); } }

原来是给 BatterySaverPolicy 设置了 policy level,值为 BatterySaverPolicy.POLICY_LEVEL_FULL

boolean setPolicyLevel(@PolicyLevel int level) { synchronized (mLock) { if (mPolicyLevel == level) { return false; } if (mPolicyLevel == POLICY_LEVEL_FULL) { mFullPolicy = mDefaultFullPolicy; } switch (level) { case POLICY_LEVEL_FULL: case POLICY_LEVEL_ADAPTIVE: case POLICY_LEVEL_OFF: // 1. 保存 level mPolicyLevel = level; break; default: Slog.wtf(TAG, "setPolicyLevel invalid level given: " + level); return false; } // 2. 根据 level,更新有效的 policy updatePolicyDependenciesLocked(); return true; } }

BatterSaverPolicy 保存了 policy level,并且调用 updatePolicyDependenciesLocked() 来更新有效的 battery saver policy

private void updatePolicyDependenciesLocked() { // 1. 根据 policy level, 获取对应的 policy final Policy rawPolicy = getCurrentRawPolicyLocked(); // 刷新省电模式缓存 invalidatePowerSaveModeCaches(); // 车载 final int locationMode; if (mAutomotiveProjectionActive.get() && rawPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE && rawPolicy.locationMode != PowerManager.LOCATION_MODE_FOREGROUND_ONLY) { // If car projection is enabled, ensure that navigation works. locationMode = PowerManager.LOCATION_MODE_FOREGROUND_ONLY; } else { locationMode = rawPolicy.locationMode; } // 2. 根据获取的策略,来更新有效的策略 // mEffectivePolicyRaw 表示实际生效的 policy // mEffectivePolicyRaw 的数据,基本上都是从 rawPolicy 中复制过来的 // 只有几项是需要调整的,例如 车载 或者 无障碍,这两个特殊的情况,在使用时注意下即可 mEffectivePolicyRaw = new Policy( rawPolicy.adjustBrightnessFactor, rawPolicy.advertiseIsEnabled, rawPolicy.cpuFrequenciesForInteractive, rawPolicy.cpuFrequenciesForNoninteractive, rawPolicy.deferFullBackup, rawPolicy.deferKeyValueBackup, rawPolicy.disableAnimation, rawPolicy.disableAod, rawPolicy.disableLaunchBoost, rawPolicy.disableOptionalSensors, // Don't disable vibration when accessibility is on. rawPolicy.disableVibration && !mAccessibilityEnabled.get(), rawPolicy.enableAdjustBrightness, rawPolicy.enableDataSaver, rawPolicy.enableFirewall, // Don't force night mode when car projection is enabled. rawPolicy.enableNightMode && !mAutomotiveProjectionActive.get(), rawPolicy.enableQuickDoze, rawPolicy.forceAllAppsStandby, rawPolicy.forceBackgroundCheck, locationMode, rawPolicy.soundTriggerMode ); // ... } // 默认省电模式策略 private Policy mFullPolicy = DEFAULT_FULL_POLICY; private Policy getCurrentRawPolicyLocked() { switch (mPolicyLevel) { case POLICY_LEVEL_FULL: return mFullPolicy; case POLICY_LEVEL_ADAPTIVE: return mAdaptivePolicy; case POLICY_LEVEL_OFF: default: return OFF_POLICY; } }

getCurrentRawPolicyLocked() 会获取默认的省电模式策略 DEFAULT_FULL_POLICY,然后根据一些情况调整省电模式策略,最后形成有效的省电模式策略 mEffectivePolicyRaw

现在让我们看看这个默认的省电策略 DEFAULT_FULL_POLICY 到底是何方神圣

private static final Policy DEFAULT_FULL_POLICY = new Policy( 0.5f, /* adjustBrightnessFactor */ true, /* advertiseIsEnabled */ new CpuFrequencies(), /* cpuFrequenciesForInteractive */ new CpuFrequencies(), /* cpuFrequenciesForNoninteractive */ true, /* deferFullBackup */ true, /* deferKeyValueBackup */ false, /* disableAnimation */ true, /* disableAod */ true, /* disableLaunchBoost */ true, /* disableOptionalSensors */ true, /* disableVibration */ false, /* enableAdjustBrightness */ false, /* enableDataSaver */ true, /* enableFirewall */ true, /* enableNightMode */ true, /* enableQuickDoze */ true, /* forceAllAppsStandby */ true, /* forceBackgroundCheck */ PowerManager.LOCATION_MODE_FOREGROUND_ONLY, /* locationMode */ PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY /* soundTriggerMode */ );

Policy 就是一个数据封装类,看下它构造函数的参数,我们就能大致猜测出省电模式影响哪些模块的功能。

这里注意下第三个和第四个参数,它表示省电模式下,需要限制频率的 CPU 的编号以及限制的频率值,这里默认是空,后面会用到。

处理省电模式状态改变

现在让我们回到打开省电模式的代码

public void enableBatterySaver(boolean enable, int reason) { synchronized (mLock) { if (getFullEnabledLocked() == enable) { return; } // 1. 保存省电模式的状态 setFullEnabledLocked(enable); // 2. 更新省电模式策略 if (updatePolicyLevelLocked()) { // 3. 处理省电模式状态的改变 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); } } }

前两步已经分析完毕,现在来看看第三步,它最终会调用 BatterySaverController#handleBatterySaverStateChanged() 来处理省电模式状态改变

void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) { final LowPowerModeListener[] listeners; final boolean enabled; // 获取设备是否处于交互状态 // 一般来说,如果屏幕熄灭,设备处于非交互状态,屏幕电量,设备处于交互状态 final boolean isInteractive = getPowerManager().isInteractive(); final ArrayMap<String, String> fileValues; synchronized (mLock) { // 获取省电模式的状态 enabled = getFullEnabledLocked() || getAdaptiveEnabledLocked(); // 保存前一个 full battery saver状态 mFullPreviouslyEnabled = getFullEnabledLocked(); // 保存前一个adaptive battery saver状态 mAdaptivePreviouslyEnabled = getAdaptiveEnabledLocked(); listeners = mListeners.toArray(new LowPowerModeListener[0]); mIsInteractive = isInteractive; if (enabled) { // 1. 打开省电模式情况下,获取频率受限的CPU的编号以及受限的值 fileValues = mBatterySaverPolicy.getFileValues(isInteractive); } else { fileValues = null; } } // 2. 通过 PowerManagerService 向底层设置省电模式 final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class); if (pmi != null) { pmi.setPowerMode(Mode.LOW_POWER, isEnabled()); } // 用 BatterySavingStats 记录数据 updateBatterySavingStats(); // 3. 根据策略,限制或恢复CPU频率 if (ArrayUtils.isEmpty(fileValues)) { // CPU 策略为空,表示需要恢复 CPU 之前的频率 mFileUpdater.restoreDefault(); } else { // CPU 频率策略不为空,表示需要限制 CPU 频率 mFileUpdater.writeFiles(fileValues); } if (sendBroadcast) { // 4. 发送广播 Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); // 可以在 frameworks-res 的配置文件中配置一个应用的包名 // 这个应用可以在manifest.xml中注册广播接收器,接收省电模式状态改变 if (getPowerSaveModeChangedListenerPackage().isPresent()) { intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED) .setPackage(getPowerSaveModeChangedListenerPackage().get()) .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } // 发送一个内部版本的广播,但是接收者需要权限 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.DEVICE_POWER); // 5. 通知监听者 for (LowPowerModeListener listener : listeners) { final PowerSaveState result = mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType()); listener.onLowPowerModeChanged(result); } } }

第一步和第三步,是在省电模式下限制 CPU 频率的。根据前面分析可知,目前默认策略是没有配置CPU频率的,因此这两步不分析了。我将在后面的文章中,分析如何控制省电模式策略,到时候再来分析这里的代码逻辑。

第二步,通过 PowerManagerService 向底层设置省电模式,底层称之为低功耗模式(low power mode)。

第四步,发送省电模式状态改变的广播。

第五步,通知监听者。谁会是监听者呢?当然是那些受省电模式影响的模块。

让我们看下返回给监听者的数据到底是什么?看下mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType())

public PowerSaveState getBatterySaverPolicy(@ServiceType int type) { synchronized (mLock) { final Policy currPolicy = getCurrentPolicyLocked(); final PowerSaveState.Builder builder = new PowerSaveState.Builder() .setGlobalBatterySaverEnabled(currPolicy.advertiseIsEnabled); switch (type) { case ServiceType.LOCATION: boolean isEnabled = currPolicy.advertiseIsEnabled || currPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE; return builder.setBatterySaverEnabled(isEnabled) .setLocationMode(currPolicy.locationMode) .build(); case ServiceType.ANIMATION: return builder.setBatterySaverEnabled(currPolicy.disableAnimation) .build(); // ... case ServiceType.VIBRATION: return builder.setBatterySaverEnabled(currPolicy.disableVibration) .build(); case ServiceType.FORCE_ALL_APPS_STANDBY: return builder.setBatterySaverEnabled(currPolicy.forceAllAppsStandby) .build(); case ServiceType.FORCE_BACKGROUND_CHECK: return builder.setBatterySaverEnabled(currPolicy.forceBackgroundCheck) .build(); // ... default: return builder.setBatterySaverEnabled(currPolicy.advertiseIsEnabled) .build(); } } }

原来,根据监听者的类型,返回一个 PowerSaveState 对象,这个对象中只包含了监听者关心的数据。

从这里,我们应该有所领悟,如果我们自己开发了一个功能模块

如果受省电模式策略影响,必须注册一个监听器,获取省电模式下策略,然后调整模块的功能。

如果这个模块是个耗电大户,那么必须监听省电模式,在省电模式下执行相应的操作。

现在很多项目都关注电量消耗问题,省电模式到底能让手机待机多长时间,也是一个考核的指标。

battery saver sticky 模式

根据前面的分析,只有在用户手动操作省电模式的时候,才会相应的打开或者关闭 battery saver sticky 模式。

我先总结下什么是 battery saver sticky 模式? 当手机已经处于省电模式,插入电源,系统会默认关闭省电模式,如果此时拔掉电源或者手机重启,当 battery saver sticky 功能已经打开的情况下,系统会重新打开省电模式。

现在让我们从代码角度分析,继续使用上面的例子分析,假如现在已经打开了省电模式,此时插入了电源,来看下状态机的切换动作 BatterySaverStateMachine#updateStateLocked()

private void updateStateLocked(boolean manual, boolean enable) { if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) { return; // Not fully initialized yet. } switch (mState) { case STATE_OFF: { if (!mIsPowered) { // 充电状态下,不允许打开省电模式 if (manual) { // 手动模式 if (!enable) { Slog.e(TAG, "Tried to disable BS when it's already OFF"); return; } enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_MANUAL_ON); hideStickyDisabledNotification(); // 1. 用户打开省电模式,状态切换为 STATE_MANUAL_ON mState = STATE_MANUAL_ON; } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) { // 自动模式 ... } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) { // 动态模式 ... } } break; } case STATE_MANUAL_ON: { if (manual) { // ... } else if (mIsPowered) { // 2. 插入电源 // 关闭省电模式 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, BatterySaverController.REASON_PLUGGED_IN); // 手动打开省电模式时,mSettingBatterySaverEnabledSticky 设置为 true // mBatterySaverStickyBehaviourDisabled 默认为 false,表示支持这个 feature if (mSettingBatterySaverEnabledSticky && !mBatterySaverStickyBehaviourDisabled) { // 插入电源,状态切换为 STATE_PENDING_STICKY_ON mState = STATE_PENDING_STICKY_ON; } else { mState = STATE_OFF; } } break; } // ... case STATE_PENDING_STICKY_ON: { // 3. battery saver sticky 模式操作 if (manual) { return; } // mSettingBatterySaverStickyAutoDisableEnabled 对应 Battery Saver界面下的 Turn off when charging 开关 // mSettingBatterySaverStickyAutoDisableThreshold 默认值为 90 final boolean shouldTurnOffSticky = mSettingBatterySaverStickyAutoDisableEnabled && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold; // 手动打开省电模式,再插入电源,此时 isStickyDisabled 值为 false final boolean isStickyDisabled = mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky; if (isStickyDisabled || shouldTurnOffSticky) { // 3.2 如果Turn off when charging 开关被打开,并且电量大于90%,那么不会重新打开省电模式 mState = STATE_OFF; setStickyActive(false); triggerStickyDisabledNotification(); } else if (!mIsPowered) { // Re-enable BS. // 3.1 断开电源,重新打开省电模式 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_STICKY_RESTORE); mState = STATE_MANUAL_ON; } break; } // ... } }

首先看下第一步,它打开了省电模式,并且状态切换为 STATE_MANUAL_ON

如果此时,插入电源,那么会进入第二步, 关闭省电模式, 并把状态切换为 STATE_PENDING_STICKY_ON

如果关闭了设置中 Battery Saver 界面的 Turn off when Charging 开关,此时拔掉电源,那么进入 3.1 步,又会再次打开省电模式,这就是 sticky 的含义。

如果打开了设置中 Battery Saver 界面的 Turn off when Charging 开关,那么进入 3.2 步,不会再次打开省电模式。

设置中 Battery Saver 界面的 Turn off when Charging 开关就是 battery saver sticky auto disable 功能。

结束

通读了整个省电模式的代码,给我的感觉是很多功能都非常鸡肋,限于偏于原因,我只分析了核心的代码,那就是如何切换省电模式。剩下的其他功能,留给读者自行分析。

到此这篇关于Android PowerManagerService 打开省电模式的文章就介绍到这了,更多相关Android PowerManagerService 内容请搜索易知道(ezd.cc)以前的文章或继续浏览下面的相关文章希望大家以后多多支持易知道(ezd.cc)!

推荐阅读