Android 8.1省电模式分析

1. 功能概述

Google在Android L上新增了省电模式功能开关,这个功能是在Setting -> Battery (–> more (androidO以前的路径)) –> Battery saver,这个功能主要是为了在相同电量下能够更长时间的使用手机,简称:“省电模式”。

打开之后手机将处于省电模式,省电模式下电池使用量将大大降低,一些不必要的耗电操作将禁止。属于一个很实用的功能。

2. 实现原理分析

下面我们来介绍一下Battery saver的实现原理与其做了什么电源管理类型的优化。

2.1 Battery saver界面显示与功能

å¾2.1 Battery Saverçé¢

图2.1 Battery Saver界面

从图2.1我们可以知道Battery saver有一个switch开关,还有一个选项(是否自动打开battery saver)

1、先来看一下Battery saver的switch开关,当我们打开Battery saver的时候

备注:打开省电模式,还有一种方式就是通过SystemUI 下拉Toolbox点击开启

//packages/apps/Settings/src/com/android/settings/fuelgauge/BatterySaverSettings.javaprivate void trySetPowerSaveMode(boolean mode) {if (!mPowerManager.setPowerSaveMode(mode)) {…}

2、先来看一下Battery saver的switch开关,当我们打开Battery saver的时候 

//frameworks/base/core/java/android/provider/Settings.javapublic static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";//省电模式的触发门槛值//packages/apps/Settings/src/com/android/settings/fuelgauge/BatterySaverSettings.java 
// battery_saver_trigger_values是省电模式触发门槛值的可选值,’0’代表关闭,’5’代表5%电量的时候自动进入省电模式,’15’代表15%电量的时候自动进入省电模式
mTriggerPref = new SettingPref(SettingPref.TYPE_GLOBAL, KEY_TURN_ON_AUTOMATICALLY,Global.LOW_POWER_MODE_TRIGGER_LEVEL,0, /*default*/getResources().getIntArray(R.array.battery_saver_trigger_values))

0515

2.2 进入Power Save Mode的条件

1、当应用调用了PMS的setPowerSaveMode进行省电模式,会进入PowerManagerService.java,里面主要包括权限检查和进一步设置

//PowerManagerService.java
public boolean setPowerSaveMode(boolean mode) {//这个操作需要调用者用有DEVICE_POWER权限mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);//......//进行省电模式内部设置return setLowPowerModeInternal(mode);
}

2、省电模式内部设置setLowPowerModeInternal,里面包含省电模式标志位LOW_POWER_MODE的设置,省电模式mLowPowerModeSetting的设置

private boolean setLowPowerModeInternal(boolean mode) {……//此时会设置省电模式标志位LOW_POWER_MODE设置Settings.Global.putInt(mContext.getContentResolver(),Settings.Global.LOW_POWER_MODE, mode ? 1 : 0);mLowPowerModeSetting = mode;//省电模式设置……updateLowPowerModeLocked();//省电模式return true;
}

3、更新省电模式相关设置updateLowPowerModeLocked,该函数包括了大部分省电模式的逻辑处理。

1) 如果在充电中或者”非低电而且没有开完机”情况下是不允许进入省电模式

2) 自动打开省电模式的条件是:不在充电状态 && 设置了自动进入省电模式 && 用户没有主动在低电下关闭低电模式 && 低电状态

3) 只要手动设置省电模式mLowPowerModeSetting,或者自动进入省电模式,那么省电模式lowPowerModeEnabled将会给打开

4) 进行Power HAL的设置,android默认的default Power HAL是power.default.so,里面的内容默认都是置空,也就是没有任何操作。

5) 进行省电模式模式切换前会发生一个ACTION_POWER_SAVE_MODE_CHANGING的广播,然后回调所有监听了省电模式的监听器,最后切换成功后会发生ACTION_POWER_SAVE_MODE_CHANGED的广播。

6) 会将mLowPowerModeListeners低电模式的监听onLowPowerModeChanged全部回调一遍

void updateLowPowerModeLocked() {
//如果在充电中是不允许进入省电模式,其中在androidO上新增在非低电量
//(当电量低于15%属于低电模式,config_lowBatteryWarningLevel可以配置)
//而且还没开完机的时候也是不允许进入该模式的if ((mIsPowered || !mBatteryLevelLow && !mBootCompleted) && mLowPowerModeSetting) { Settings.Global.putInt(mContext.getContentResolver(),Settings.Global.LOW_POWER_MODE, 0);mLowPowerModeSetting = false;}
// autoLowPowerModeEnabled代表是否需要自动打开省电模式,条件是不在充电状态 && 设置了自动进入省电模式 && 用户没有主动在低电下关闭低电模式 && 低电状态final boolean autoLowPowerModeEnabled = !mIsPowered && mAutoLowPowerModeConfigured&& !mAutoLowPowerModeSnoozing && mBatteryLevelLow;//只要手动设置省电模式mLowPowerModeSetting,或者自动进入省电模式,那么省电模式lowPowerModeEnabled将会给打开final boolean lowPowerModeEnabled = mLowPowerModeSetting || autoLowPowerModeEnabled;if (mLowPowerModeEnabled != lowPowerModeEnabled) {//省电模式进行了改变mLowPowerModeEnabled = lowPowerModeEnabled;//设置当前是否处于省电模式//进行Power HAL的设置,android默认的default Power HAL是power.default.so里面是做空的powerHintInternal(POWER_HINT_LOW_POWER, lowPowerModeEnabled ? 1 : 0);//在androidO上是在开机完成后在进行设置postAfterBootCompleted( new Runnable() {@Overridepublic void run() {Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING).putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, mLowPowerModeEnabled).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);//发送ACTION_POWER_SAVE_MODE_CHANGING省电模式正在切换的广播mContext.sendBroadcast(intent);……for (int i=0; i

接下来讲的是对系统实际的影响

2.3 Power Save Mode对系统的实际影响

2.3.1 屏幕亮度将会变成原来的50%

我们在设置了LOW_POWER_MODE省电模式的时候,PowerManagerService本身会对其进行监听,监听器改变后会进行显示设备状态更新handleSettingsChangedLocked ---> updateDisplayPowerStateLocked

    private final class UserSwitchedReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {synchronized (mLock) {handleSettingsChangedLocked();}}}private final class SettingsObserver extends ContentObserver {public SettingsObserver(Handler handler) {super(handler);}@Overridepublic void onChange(boolean selfChange, Uri uri) {synchronized (mLock) {handleSettingsChangedLocked();}}}private void handleSettingsChangedLocked() {updateSettingsLocked();updatePowerStateLocked();}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 {// Phase 0: Basic state updates.updateIsPoweredLocked(mDirty);updateStayOnLocked(mDirty);updateScreenBrightnessBoostLocked(mDirty);// Phase 1: Update wakefulness.// Loop because the wake lock and user activity computations are influenced// by changes in wakefulness.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;}}// Phase 2: Update display power state.boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);// Phase 3: Update dream state (depends on display ready signal).updateDreamLocked(dirtyPhase2, displayBecameReady);// Phase 4: Send notifications, if needed.finishWakefulnessChangeIfNeededLocked();// Phase 5: Update suspend blocker.// Because we might release the last suspend blocker here, we need to make sure// we finished everything else first!updateSuspendBlockerLocked();} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}}private boolean updateDisplayPowerStateLocked(int dirty) {……//传入省电模式updatePowerRequestFromBatterySaverPolicy(mDisplayPowerRequest); ……mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,mRequestWaitForNegativeProximity);//更新显示设备的状态请求……}
   void updatePowerRequestFromBatterySaverPolicy(DisplayPowerRequest displayPowerRequest) {PowerSaveState state = mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS, mLowPowerModeEnabled);//设置低电模式和屏幕亮度降低的比例,一般都是降低50%displayPowerRequest.lowPowerMode = state.batterySaverEnabled;displayPowerRequest.screenLowPowerBrightnessFactor = state.brightnessFactor;}显示设备状态更新的控制器DisplayPowerController.javaprivate void updatePowerState() {……// 只要打开省电模式,屏幕亮度减半// 当然啦,需要大于最小亮度值if (mPowerRequest.lowPowerMode) {if (brightness > mScreenBrightnessRangeMinimum) {// brightnessFactor就是刚才设置的降低亮度的比例,默认是50%final int lowPowerBrightness = (int) (brightness * brightnessFactor);brightness = Math.max(lowPowerBrightness, mScreenBrightnessRangeMinimum);//默认最小亮度值mScreenBrightnessRangeMinimum一般都是1brightness = Math.max(brightness / 2, mScreenBrightnessRangeMinimum);}……}

2.3.2 systemui会对此操作进行一下界面上的更新

å¾2.2 æå¼çµæºä¿æ¤åsystemuiçé¢

图2.2 打开省电模式后systemui界面

Ps:上述是androidO以前的界面,androidO的状态栏是红色的。

该橙色/红色是一种status bar的警告Warning的标识(androidO使用的是error标识是红色),代表正处于省电模式(在发送ACTION_POWER_SAVE_MODE_CHANGING的时候就会通知status状态栏更新)

//StatusBar.java
private void checkBarMode(int mode, int windowState, BarTransitions transitions) {final boolean powerSave = mBatteryController.isPowerSave();//check whether you are in power mode……if (powerSave && getBarState() == StatusBarState.SHADE) {mode = MODE_WARNING;//change mode to power mode,when in status bar normol state}transitions.transitionTo(mode, anim);
}//BarTransitions.java
public void draw(Canvas canvas) {int targetGradientAlpha = 0, targetColor = 0;if (mMode == MODE_WARNING) {targetColor = mWarning;//绘制bar的时候,将warning颜色赋值给status bar显示mWarning = Utils.getColorAttr(context, android.R.attr.colorError); ./res/values/themes.xml
@color/red./res/res/values/colors.xml
//androidO使用的是红色,RGB只有R有颜色
#ffff0000//f4 51 1e切换成十进制的话244 81 30,androidO以前使用的颜色
#fff4511e

图2.3 battery_saver_mode_color的颜色值 
图2.3 battery_saver_mode_color的颜色值

红色就没必要查看了,只有红色分量肯定是红色。

2.3.3 modem进入低电模式

DeviceStateMonitor.java通过接收ACTION_POWER_SAVE_MODE_CHANGED的广播将modem设置成低电模式。(AndroidO新增,说明google也意识到了功耗属于android的通病,在modem这一块也进行优化)

    private void updateDeviceState(int eventType, boolean state) {//..….case EVENT_POWER_SAVE_MODE_CHANGED:if (mIsPowerSaveOn == state) return;mIsPowerSaveOn = state;//设置设备进入低电模式sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn);break;//..….private void sendDeviceState(int type, boolean state) {// mPhone一般都是telcom通信相关,mCi是RIL对象,modem相关设置mPhone.mCi.sendDeviceState(type, state, null);}

2.3.4 语音互动功能将关闭

SoundTriggerHelper.java会接收POWER_SAVE_MODE_CHANGED的广播,语音互动voiceinteraction功能将关闭

    //启动语音识别private int startRecognitionLocked(ModelData modelData, boolean notify) {//……if (!isRecognitionAllowed()) {//判断是否运行识别//……MetricsLogger.count(mContext, "sth_start_recognition_not_allowed", 1);return STATUS_OK;}//……private boolean isRecognitionAllowed() {//在打电话、该服务禁止掉或者省电模式中不允许语言识别功能return !mCallActive && !mServiceDisabled && !mIsPowerSaveMode;}

2.3.5 地理位置信息进行限制访问

GnssLocationProvider.java会接收POWER_SAVE_MODE_CHANGED的广播,限制gps使用,灭屏后会关闭gps

    private void updateLowPowerMode() {// 如果设备是深度睡眠,不再允许使用GPSboolean disableGps = mPowerManager.isDeviceIdleMode();final PowerSaveState result =mPowerManager.getPowerSaveState(ServiceType.GPS);switch (result.gpsMode) {//如果有相关GPS设置,一般都有case BatterySaverPolicy.GPS_MODE_DISABLED_WHEN_SCREEN_OFF:// 默认情况是低电模式在灭屏情况不允许GPSdisableGps |= result.batterySaverEnabled && !mPowerManager.isInteractive();break;}//……} }

2.3.6 震动将取消

震动将取消(原来如果有震动的话)的操作是在VibratorService.java 
当处于省电模式下,除了来电显示的震动(有条件的震动),其它都将关闭震动

//启动震动的函数private void startVibrationLocked(final Vibration vib) {//判断是否允许震动,关于低电模式是在这里判断if (!isAllowedToVibrate(vib)) {//……//在铃声静音(且设置了来电震动)或者不在震动模式(且没有设置来电震动时)的情况下不允许通知类型的铃声震动if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE &&!shouldVibrateForRingtone()) {//……//当然,如果你没有权限,什么都不行if (mode != AppOpsManager.MODE_ALLOWED) {//……}//是否允许震动的函数private boolean isAllowedToVibrate(Vibration vib) {//如果是非低电模式下是允许震动的if (!mLowPowerMode) {//onLowPowerModeChanged时会进行设置return true;}//低电模式对于铃声震动不受限制if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {return true;}//是否允许在低电模式下震动,一般这个值默认是falseif (!mAllowPriorityVibrationsInLowPowerMode) {return false;}//如果允许在低电模式下震动,仅支持alarm,助手、网络电话进行震动if (vib.mUsageHint == AudioAttributes.USAGE_ALARM ||vib.mUsageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {return true;}//其它默认是不允许震动的return false;}

2.3.7 对于网络数据链接会进行限制接入

NetworkPolicyManagerService.java会进行相应的限制接入处理,专门设置了一套FIREWALL_CHAIN_POWERSAVE低电模式的防火墙体系.

1、更新白名单范围(允许访问的网络的范围),范围之外的将停止socket网络链接

    // 更新防火墙规则,低电模式下enabled==true,chain==FIREWALL_CHAIN_POWERSAVE此模式下防火墙属于白名单模式,除了白名单以外的都不运行访问网络private void updateRulesForWhitelistedPowerSaveUL(boolean enabled, int chain,SparseIntArray rules) {if (enabled) {// 更新临时白名单、白名单、除了idleapp之外的白名单都将允许网络访问for (int ui = users.size() - 1; ui >= 0; ui--) {UserInfo user = users.get(ui);updateRulesForWhitelistedAppIds(uidRules, mPowerSaveTempWhitelistAppIds, user.id);updateRulesForWhitelistedAppIds(uidRules, mPowerSaveWhitelistAppIds, user.id);if (chain == FIREWALL_CHAIN_POWERSAVE) {updateRulesForWhitelistedAppIds(uidRules,mPowerSaveWhitelistExceptIdleAppIds, user.id);}}// 如果是进程优先级是前台服务以上的允许网络访问(前台服务一般是PERCEPTIBLE用户可感知的进程优先级以上的服务)for (int i = mUidState.size() - 1; i >= 0; i--) {if (isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.valueAt(i))) {uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW);}}setUidFirewallRulesAsync(chain, uidRules, CHAIN_TOGGLE_ENABLE);} else {setUidFirewallRulesAsync(chain, null, CHAIN_TOGGLE_DISABLE);}}

2、更新所有应用的访问规则,除了前台进程和在白名单内的进程,都将不允许访问网络

    // 更新应用在低电模式下所有app的访问规则private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) {//当uid是media或者drm类型的不需要,或者之前已经授权INTERNET网络访问的app,不许用更新if (!isUidValidForBlacklistRules(uid)) {if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);return RULE_NONE;}//是否处于空闲态final boolean isIdle = !paroled && isUidIdle(uid);//如果是低电模式会将进入受限模式final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;//是否前台进程,和上面isProcStateAllowedWhileIdleOrPowerSaveMode类似final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);//是否处在电源白名单之内final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode);final int oldRule = oldUidRules & MASK_ALL_NETWORKS;int newRule = RULE_NONE;//......if (isForeground) {if (restrictMode) {//如果是前台进程,就算是受限模式下也会允许访问网络newRule = RULE_ALLOW_ALL;}} else if (restrictMode) {//其它进程,非白名单将设置成拒绝访问RULE_REJECT_ALLnewRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;}//......}

2.3.8 WindowManagerService的动画将关闭

WindowManagerService.java会将动画关闭(onLowPowerModeChanged),但是此处由于一些第三方应用如果没有考虑省电模式关闭动画的话,可能会出现一些问题(设计时需要考虑动画在低电模式关闭后的效果)。

public void onLowPowerModeChanged(PowerSaveState result) {synchronized (mWindowMap) {final boolean enabled = result.batterySaverEnabled;//mAllowAnimationsInLowPowerMode代表是否在允许在低电模式下继续使用动画(一般是false,就是不允许),如果在低电模式下会把WMS的动画都关闭if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {mAnimationsDisabled = enabled;//是否关闭动画mAnimationsDisableddispatchNewAnimatorScaleLocked(null);//将动画设置更新到系统中去}}
}

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部