背景及目的
一般省电模式策略,在电量低于20%弹窗提示用户是否进入省电模式;电量10%弹窗告知用户已强制进入省电模式。为了方便定制,以及控制省电模式下的限制策略,梳理源码。
相关命令
// 检查当前是否省电模式
adb shell settings get global low_power
// 命令修改当前电量
adb shell dumpsys battery set level 80
// 获取省电模式触发器level,0表示不使用
adb shell settings get global low_power_trigger_level
// 设置省电模式触发器level
adb shell settings put global low_power_trigger_level 99
// 获取省电模式限制参数
adb shell settings get global battery_saver_constants
adb shell settings get global battery_saver_device_specific_constants
省电模式开启、关闭、逻辑关系
监听&&判断是否省电模式
- 通过广播ACTION_POWER_SAVE_MODE_CHANGED监听状态变化,PowerManager.isPowerSaveMode()判断当前是否省电模式
/**
* Returns true if the device is currently in power save mode. When in this mode,
* applications should reduce their functionality in order to conserve battery as
* much as possible. You can monitor for changes to this state with
* {@link #ACTION_POWER_SAVE_MODE_CHANGED}.
*
* @return Returns true if currently in low power mode, else false.
*/
public boolean isPowerSaveMode() {
try {
return mService.isPowerSaveMode();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- PowerManagerService.isPowerSaveMode
@Override // Binder call
public boolean isPowerSaveMode() {
final long ident = Binder.clearCallingIdentity();
try {
return mBatterySaverController.isEnabled();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- BatterySaverController.isEnabled(),设置省电模式后会发送广播
/** @return whether battery saver is enabled or not. */
public boolean isEnabled() {
synchronized (mLock) {
return mEnabled;
}
}
/**
* Called by {@link PowerManagerService} to update the battery saver stete.
*/
public void enableBatterySaver(boolean enable, int reason) {
synchronized (mLock) {
if (mEnabled == enable) {
return;
}
mEnabled = enable;
mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
}
}
/**
* Dispatch power save events to the listeners.
*
* This method is always called on the handler thread.
*
* This method is called only in the following cases:
* - When battery saver becomes activated.
* - When battery saver becomes deactivated.
* - When battery saver is on the interactive state changes.
* - When battery saver is on the battery saver policy changes.
*/
void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
......
if (sendBroadcast) {
if (DEBUG) {
Slog.i(TAG, "Sending broadcasts for mode: " + enabled);
}
// Send the broadcasts and notify the listeners. We only do this when the battery saver
// mode changes, but not when only the screen state changes.
Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
.putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, enabled)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
**intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);**
// Send internal version that requires signature permission.
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);
for (LowPowerModeListener listener : listeners) {
final PowerSaveState result =
mBatterySaverPolicy.getBatterySaverPolicy(
listener.getServiceType(), enabled);
listener.onLowPowerModeChanged(result);
}
}
}
自动开关省电模式
- Settings.Global值,当设置LOW_POWER_MODE_TRIGGER_LEVEL非0时,就开启了自动开关省电模式。
/**
* Battery level [1-100] at which low power mode automatically turns on.
* If 0, it will not automatically turn on.
* @hide
*/
public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
- 执行电源状态转换的主要函数PowerManagerService.updatePowerStateLocked()
/**
* Updates the global power state based on dirty bits recorded in mDirty.
*
* This is the main function that performs power state transitions.
* We centralize them here so that we can recompute the power state completely
* each time something important changes, and ensure that we do it the same
* way each time. The point is to gather all of the transition logic here.
*/
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.
// ......
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
/**
* Updates the value of mIsPowered.
* Sets DIRTY_IS_POWERED if a change occurred.
*/
private void updateIsPoweredLocked(int dirty) {
if ((dirty & DIRTY_BATTERY_STATE) != 0) {
final boolean wasPowered = mIsPowered;
final int oldPlugType = mPlugType;
final boolean oldLevelLow = mBatteryLevelLow;
mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
mPlugType = mBatteryManagerInternal.getPlugType();
mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
if (DEBUG_SPEW) {
Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
+ ", mIsPowered=" + mIsPowered
+ ", oldPlugType=" + oldPlugType
+ ", mPlugType=" + mPlugType
+ ", mBatteryLevel=" + mBatteryLevel);
}
if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
mDirty |= DIRTY_IS_POWERED;
// Update wireless dock detection state.
final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
mIsPowered, mPlugType);
// Treat plugging and unplugging the devices as a user activity.
// Users find it disconcerting when they plug or unplug the device
// and it shuts off right away.
// Some devices also wake the device when plugged or unplugged because
// they don't have a charging LED.
// ......
// only play charging sounds if boot is completed so charging sounds don't play
// with potential notification sounds
// ......
}
mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, mBatteryLevelLow);
}
}
- BatterySaverStateMachine.setBatteryStatus()
/**
* {@link com.android.server.power.PowerManagerService} calls it when battery state changes.
*
* Note this may be called before {@link #onBootCompleted} too.
*/
public void setBatteryStatus(boolean newPowered, int newLevel, boolean newBatteryLevelLow) {
if (DEBUG) {
Slog.d(TAG, "setBatteryStatus: powered=" + newPowered + " level=" + newLevel
+ " low=" + newBatteryLevelLow);
}
synchronized (mLock) {
mBatteryStatusSet = true;
final boolean poweredChanged = mIsPowered != newPowered;
final boolean levelChanged = mBatteryLevel != newLevel;
final boolean lowChanged = mIsBatteryLevelLow != newBatteryLevelLow;
if (!(poweredChanged || levelChanged || lowChanged)) {
return;
}
mIsPowered = newPowered;
mBatteryLevel = newLevel;
mIsBatteryLevelLow = newBatteryLevelLow;
doAutoBatterySaverLocked();
}
}
/**
* Decide whether to auto-start / stop battery saver.
*/
private void doAutoBatterySaverLocked() {
if (DEBUG) {
Slog.d(TAG, "doAutoBatterySaverLocked: mBootCompleted=" + mBootCompleted
+ " mSettingsLoaded=" + mSettingsLoaded
+ " mBatteryStatusSet=" + mBatteryStatusSet
+ " mIsBatteryLevelLow=" + mIsBatteryLevelLow
+ " mBatterySaverSnoozing=" + mBatterySaverSnoozing
+ " mIsPowered=" + mIsPowered
+ " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky);
}
if (!(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
return; // Not fully initialized yet.
}
if (!mIsBatteryLevelLow) {
updateSnoozingLocked(false, "Battery not low");
}
if (mIsPowered) {
updateSnoozingLocked(false, "Plugged in");
enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
BatterySaverController.REASON_PLUGGED_IN,
"Plugged in");
} else if (mSettingBatterySaverEnabledSticky) {
// Re-enable BS.
enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true,
BatterySaverController.REASON_STICKY_RESTORE,
"Sticky restore");
} else if (mIsBatteryLevelLow) {
if (!mBatterySaverSnoozing && isAutoBatterySaverConfigured()) {
enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false,
BatterySaverController.REASON_AUTOMATIC_ON,
"Auto ON");
}
} else { // Battery not low
enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
BatterySaverController.REASON_AUTOMATIC_OFF,
"Auto OFF");
}
}
手动开关省电模式
- PowerManager.setPowerSaveMode()设置开启和关闭省电模式
/**
* Set the current power save mode.
*
* @return True if the set was allowed.
*
* @see #isPowerSaveMode()
*
* @hide
*/
public boolean setPowerSaveMode(boolean mode) {
try {
return mService.setPowerSaveMode(mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- PowerManagerService.setPowerSaveMode()
@Override // Binder call
public boolean setPowerSaveMode(boolean enabled) {
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 (DEBUG) {
Slog.d(TAG, "setLowPowerModeInternal " + enabled + " mIsPowered=" + mIsPowered);
}
if (mIsPowered) {
return false;
}
mBatterySaverStateMachine.setBatterySaverEnabledManually(enabled);
return true;
}
}
- BatterySaverStateMachine.setBatterySaverEnabledManually()
/**
* {@link com.android.server.power.PowerManagerService} calls it when
* {@link android.os.PowerManager#setPowerSaveMode} is called.
*
* Note this could? be called before {@link #onBootCompleted} too.
*/
public void setBatterySaverEnabledManually(boolean enabled) {
if (DEBUG) {
Slog.d(TAG, "setBatterySaverEnabledManually: enabled=" + enabled);
}
synchronized (mLock) {
// 调用setPowerSaveMode接口设置,manual手动模式会默认true
enableBatterySaverLocked(/*enable=*/ enabled, /*manual=*/ true,
(enabled ? BatterySaverController.REASON_MANUAL_ON
: BatterySaverController.REASON_MANUAL_OFF),
(enabled ? "Manual ON" : "Manual OFF"));
}
}
/**
* Actually enable / disable battery saver. Write the new state to the global settings
* and propagate it to {@link #mBatterySaverController}.
*/
private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
String strReason) {
if (DEBUG) {
Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual
+ " reason=" + strReason + "(" + intReason + ")");
}
final boolean wasEnabled = mBatterySaverController.isEnabled();
if (wasEnabled == enable) {
if (DEBUG) {
Slog.d(TAG, "Already " + (enable ? "enabled" : "disabled"));
}
return;
}
if (enable && mIsPowered) {
if (DEBUG) Slog.d(TAG, "Can't enable: isPowered");
return;
}
mLastChangedIntReason = intReason;
mLastChangedStrReason = strReason;
if (manual) {
if (enable) {
updateSnoozingLocked(false, "Manual snooze OFF");
} else {
// When battery saver is disabled manually (while battery saver is enabled)
// when the battery level is low, we "snooze" BS -- i.e. disable auto battery saver.
// We resume auto-BS once the battery level is not low, or the device is plugged in.
if (isBatterySaverEnabled() && mIsBatteryLevelLow) {
updateSnoozingLocked(true, "Manual snooze");
}
}
}
mSettingBatterySaverEnabled = enable;
// 设置全局属性 adb shell settings get global low_power
putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);
if (manual) {
mSettingBatterySaverEnabledSticky = enable;
putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
}
// 这里通过类BatterySaverController关联到了isPowerSaveMode
mBatterySaverController.enableBatterySaver(enable, intReason);
if (DEBUG) {
Slog.d(TAG, "Battery saver: Enabled=" + enable
+ " manual=" + manual
+ " reason=" + strReason + "(" + intReason + ")");
}
}
各种开启、关闭省电模式逻辑关系
- 无论哪种方式,逻辑关系处理都在类BatterySaverStateMachine.java,逻辑关系主要处理手动和自动耦合时应该如何关闭开启省电模式,这里还包括重启后的状态恢复。
- 首先看下BatterySaverStateMachine.enableBatterySaverLocked接口,无论手动还是自动,最终都会调用这个接口处理逻辑
/**
* Actually enable / disable battery saver. Write the new state to the global settings
* and propagate it to {@link #mBatterySaverController}.
*/
private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
String strReason) {
if (DEBUG) {
Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual
+ " reason=" + strReason + "(" + intReason + ")");
}
final boolean wasEnabled = mBatterySaverController.isEnabled();
// 如果要设置的值和当前一致,不做处理,这里要留意不区分方式,一律拦截
if (wasEnabled == enable) {
if (DEBUG) {
Slog.d(TAG, "Already " + (enable ? "enabled" : "disabled"));
}
return;
}
// 如果插电状态,不允许设置开启省电模式
if (enable && mIsPowered) {
if (DEBUG) Slog.d(TAG, "Can't enable: isPowered");
return;
}
mLastChangedIntReason = intReason;
mLastChangedStrReason = strReason;
// 调用setPowerSaveMode接口设置,manual手动模式会置为true
if (manual) {
// 更新mBatterySaverSnoozing,标记是否电量低时手动关闭了省电模式
if (enable) {
updateSnoozingLocked(false, "Manual snooze OFF");
} else {
// When battery saver is disabled manually (while battery saver is enabled)
// when the battery level is low, we "snooze" BS -- i.e. disable auto battery saver.
// We resume auto-BS once the battery level is not low, or the device is plugged in.
if (isBatterySaverEnabled() && mIsBatteryLevelLow) {
updateSnoozingLocked(true, "Manual snooze");
}
}
}
mSettingBatterySaverEnabled = enable;
// 设置全局属性 adb shell settings get global low_power
putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);
if (manual) {
// 记录是否手动模式开启省电模式并记录到系统属性重启后恢复
mSettingBatterySaverEnabledSticky = enable;
putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
}
// 这里通过类BatterySaverController关联到了isPowerSaveMode
mBatterySaverController.enableBatterySaver(enable, intReason);
if (DEBUG) {
Slog.d(TAG, "Battery saver: Enabled=" + enable
+ " manual=" + manual
+ " reason=" + strReason + "(" + intReason + ")");
}
}
/**
* Whether BS has been manually disabled while the battery level is low, in which case we
* shouldn't auto re-enable it until the battery level is not low.
*/
@GuardedBy("mLock")
private boolean mBatterySaverSnoozing;
private void updateSnoozingLocked(boolean snoozing, String reason) {
if (mBatterySaverSnoozing == snoozing) {
return;
}
if (DEBUG) Slog.d(TAG, "Snooze: " + (snoozing ? "start" : "stop") + " reason=" + reason);
mBatterySaverSnoozing = snoozing;
}
/**
* Decide whether to auto-start / stop battery saver.
* 自动模式更新会和某些参数有关联
*/
private void doAutoBatterySaverLocked() {
if (DEBUG) {
Slog.d(TAG, "doAutoBatterySaverLocked: mBootCompleted=" + mBootCompleted
+ " mSettingsLoaded=" + mSettingsLoaded
+ " mBatteryStatusSet=" + mBatteryStatusSet
+ " mIsBatteryLevelLow=" + mIsBatteryLevelLow
+ " mBatterySaverSnoozing=" + mBatterySaverSnoozing
+ " mIsPowered=" + mIsPowered
+ " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky);
}
if (!(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
return; // Not fully initialized yet.
}
if (!mIsBatteryLevelLow) {
updateSnoozingLocked(false, "Battery not low");
}
if (mIsPowered) {
updateSnoozingLocked(false, "Plugged in");
// 如果插电会自动退出省电模式,manual为false
enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
BatterySaverController.REASON_PLUGGED_IN,
"Plugged in");
} else if (mSettingBatterySaverEnabledSticky) {
// 如果上一次开启省电模式是手动操作的,恢复状态
// Re-enable BS.
enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true,
BatterySaverController.REASON_STICKY_RESTORE,
"Sticky restore");
} else if (mIsBatteryLevelLow) {
// 这里就是自动开启省电模式的关键代码,如果用户
// 没在低电量时手动关闭过省电模式,同时设置了自动省电模式,开启省电模式
if (!mBatterySaverSnoozing && isAutoBatterySaverConfigured()) {
enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false,
BatterySaverController.REASON_AUTOMATIC_ON,
"Auto ON");
}
} else { // Battery not low
enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
BatterySaverController.REASON_AUTOMATIC_OFF,
"Auto OFF");
}
}
private boolean isAutoBatterySaverConfigured() {
return mSettingBatterySaverTriggerThreshold > 0;
}
- mIsBatteryLevelLow出现多次,是在BatteryManagerInternal.java的实现类BatteryService.getBatteryLevelLow()中获取的。
@Override
public boolean getBatteryLevelLow() {
synchronized (mLock) {
return mBatteryLevelLow;
}
}
private void processValuesLocked(boolean force) {
......
if (!mBatteryLevelLow) {
// Should we now switch in to low battery mode?
// 这里注意只有不插电才会设置电量低
if (mPlugType == BATTERY_PLUGGED_NONE
&& mHealthInfo.batteryStatus !=
BatteryManager.BATTERY_STATUS_UNKNOWN
&& mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
mBatteryLevelLow = true;
}
} else {
// Should we now switch out of low battery mode?
if (mPlugType != BATTERY_PLUGGED_NONE) {
mBatteryLevelLow = false;
} else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
mBatteryLevelLow = false;
} else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) {
// If being forced, the previous state doesn't matter, we will just
// absolutely check to see if we are now above the warning level.
mBatteryLevelLow = false;
}
}
......
}
public BatteryService(Context context) {
super(context);
mContext = context;
mHandler = new Handler(true /*async*/);
mLed = new Led(context, getLocalService(LightsManager.class));
mBatteryStats = BatteryStatsService.getService();
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mCriticalBatteryLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
// 对象创建默认读取的config文件值
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
// 非强制状态下,电量超过低电量一定值,退出省电模式
mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);
......
}
private void updateBatteryWarningLevelLocked() {
final ContentResolver resolver = mContext.getContentResolver();
int defWarnLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
// 读取的自动省电模式的值
mLowBatteryWarningLevel = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
if (mLowBatteryWarningLevel == 0) {
mLowBatteryWarningLevel = defWarnLevel;
}
if (mLowBatteryWarningLevel < mCriticalBatteryLevel) {
mLowBatteryWarningLevel = mCriticalBatteryLevel;
}
mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
processValuesLocked(true);
}
- 用到了多个config值,这里罗列下
<!-- Display low battery warning when battery level dips to this value.
Also, the battery stats are flushed to disk when we hit this level. -->
<integer name="config_criticalBatteryWarningLevel">5</integer>
<!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. -->
<integer name="config_shutdownBatteryTemperature">680</integer>
<!-- Display low battery warning when battery level dips to this value -->
<integer name="config_lowBatteryWarningLevel">15</integer>
<!-- The default suggested battery % at which we enable battery saver automatically. -->
<integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
<!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
plus this -->
<integer name="config_lowBatteryCloseWarningBump">5</integer>
逻辑总结
- 插电状态下,不允许进入省电模式。
- 拔电状态下,手动任意电量进入省电模式,会立即进入省电模式。充电会暂时打断,拔电后会再次进入省电模式,恢复用户的操作终态。
- 拔电状态下,开启自动省电模式,电量低于设置值会自动进入省电模式。自动省电模式状态下,手动进入省电模式会因为重复设置被拦截;手动退出省电模式可以生效,会立即退出省电模式。注意,当重启设备或者充电到自动省电模式边界值以上,再次掉电到边界值以下,还是会自动进入省电模式。
- 拔电状态下,如果用户开了自动省电模式,在高于自动省电模式电量之前手动进入了省电模式,到达自动省电模式电量时,会因为重复设置直接return。直到用户手动退出省电模式,下次电量更新(降低一格)会进入省电模式,如果电量低于自动省电模式电量时手动退出,重启或者上电前,都会高优手动退出的操作,重启或者上电后重置,以自动省电模式为主。
- 手动模式关闭后,下次重启或者上电前优先级最高,重启或者上电后,恢复默认状态。
- 拔电状态下,开启自动省电模式,充电到大于边界值config_lowBatteryCloseWarningBump之后,拔电后才会退出省电模式。源码似乎预留的逻辑,暂未实现,充电到自动省电模式电量值就会退出省电模式。
省电模式限制
代码逻辑
- 省电模式开启和关闭最终都会在BatterySaverController.enableBatterySaver处理
/**
* Called by {@link PowerManagerService} to update the battery saver stete.
*/
public void enableBatterySaver(boolean enable, int reason) {
synchronized (mLock) {
if (mEnabled == enable) {
return;
}
mEnabled = enable;
mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
}
}
private class MyHandler extends Handler {
......
public void postStateChanged(boolean sendBroadcast, int reason) {
obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?
ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget();
}
......
@Override
public void dispatchMessage(Message msg) {
switch (msg.what) {
case MSG_STATE_CHANGED:
handleBatterySaverStateChanged(
msg.arg1 == ARG_SEND_BROADCAST,
msg.arg2);
break;
.......
}
}
}
/**
* Dispatch power save events to the listeners.
*
* This method is always called on the handler thread.
*
* This method is called only in the following cases:
* - When battery saver becomes activated.
* - When battery saver becomes deactivated.
* - When battery saver is on the interactive state changes.
* - When battery saver is on the battery saver policy changes.
*/
void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
......
if (sendBroadcast) {
......
intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
......
for (LowPowerModeListener listener : listeners) {
final PowerSaveState result =
mBatterySaverPolicy.getBatterySaverPolicy(
listener.getServiceType(), enabled);
listener.onLowPowerModeChanged(result);
}
}
}
这里可以看到有一个LowPowerModeListener集合,当省电模式变化的时候会回调onLowPowerModeChanged,并传入通过BatterySaverPolicy.getBatterySaverPolicy获取的PowerSaveState。
- 梳理下LowPowerModeListener集合来源BatterySaverController.java
@GuardedBy("mLock")
private final ArrayList<LowPowerModeListener> mListeners = new ArrayList<>();
/**
* Add a listener.
*/
public void addListener(LowPowerModeListener listener) {
synchronized (mLock) {
mListeners.add(listener);
}
}
- 调用addListener是在PowerManagerService
@Override
public void registerLowPowerModeObserver(LowPowerModeListener listener) {
mBatterySaverController.addListener(listener);
}
- 会有很多service收到LowPowerModeListener回调时处理各自的逻辑,看下VibratorService.java
public void systemReady() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
try {
......
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@Override
public int getServiceType() {
return ServiceType.VIBRATION;
}
@Override
public void onLowPowerModeChanged(PowerSaveState result) {
updateVibrators();
}
});
......
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
private void updateVibrators() {
synchronized (mLock) {
boolean devicesUpdated = updateInputDeviceVibratorsLocked();
boolean lowPowerModeUpdated = updateLowPowerModeLocked();
updateVibrationIntensityLocked();
if (devicesUpdated || lowPowerModeUpdated) {
// If the state changes out from under us then just reset.
doCancelVibrateLocked();
}
}
}
private boolean updateLowPowerModeLocked() {
boolean lowPowerMode = mPowerManagerInternal
.getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
if (lowPowerMode != mLowPowerMode) {
mLowPowerMode = lowPowerMode;
return true;
}
return false;
}
@GuardedBy("mLock")
private void doCancelVibrateLocked() {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
try {
mH.removeCallbacks(mVibrationEndRunnable);
if (mThread != null) {
mThread.cancel();
mThread = null;
}
doVibratorOff();
reportFinishVibrationLocked();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
省电模式会关闭震动。
- onLowPowerModeChanged回调会传PowerSaveState作为参数,是通过BatterySaverPolicy.getBatterySaverPolicy获取的。
/**
* Get the {@link PowerSaveState} based on {@paramref type} and {@paramref realMode}.
* The result will have {@link PowerSaveState#batterySaverEnabled} and some other
* parameters when necessary.
*
* @param type type of the service, one of {@link ServiceType}
* @param realMode whether the battery saver is on by default
* @return State data that contains battery saver data
*/
public PowerSaveState getBatterySaverPolicy(@ServiceType int type, boolean realMode) {
synchronized (mLock) {
final PowerSaveState.Builder builder = new PowerSaveState.Builder()
.setGlobalBatterySaverEnabled(realMode);
if (!realMode) {
return builder.setBatterySaverEnabled(realMode)
.build();
}
switch (type) {
case ServiceType.GPS:
return builder.setBatterySaverEnabled(realMode)
.setGpsMode(mGpsMode)
.build();
case ServiceType.ANIMATION:
return builder.setBatterySaverEnabled(mAnimationDisabled)
.build();
case ServiceType.FULL_BACKUP:
return builder.setBatterySaverEnabled(mFullBackupDeferred)
.build();
case ServiceType.KEYVALUE_BACKUP:
return builder.setBatterySaverEnabled(mKeyValueBackupDeferred)
.build();
case ServiceType.NETWORK_FIREWALL:
return builder.setBatterySaverEnabled(!mFireWallDisabled)
.build();
case ServiceType.SCREEN_BRIGHTNESS:
return builder.setBatterySaverEnabled(!mAdjustBrightnessDisabled)
.setBrightnessFactor(mAdjustBrightnessFactor)
.build();
case ServiceType.DATA_SAVER:
return builder.setBatterySaverEnabled(!mDataSaverDisabled)
.build();
case ServiceType.SOUND:
return builder.setBatterySaverEnabled(mSoundTriggerDisabled)
.build();
case ServiceType.VIBRATION:
return builder.setBatterySaverEnabled(mVibrationDisabledEffective)
.build();
case ServiceType.FORCE_ALL_APPS_STANDBY:
return builder.setBatterySaverEnabled(mForceAllAppsStandby)
.build();
case ServiceType.FORCE_BACKGROUND_CHECK:
return builder.setBatterySaverEnabled(mForceBackgroundCheck)
.build();
case ServiceType.OPTIONAL_SENSORS:
return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
.build();
case ServiceType.AOD:
return builder.setBatterySaverEnabled(mAodDisabled)
.build();
default:
return builder.setBatterySaverEnabled(realMode)
.build();
}
}
}
这个方法明确标明了每个受限ServiceType的值,是通过系统属性设置更新的。
/**
* A short string describing which battery saver is now enabled, which we dump in the eventlog.
*/
@GuardedBy("mLock")
private String mEventLogKeys;
/**
* {@code true} if vibration is disabled in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_VIBRATION_DISABLED
*/
@GuardedBy("mLock")
private boolean mVibrationDisabledConfig;
/**
* Whether vibration should *really* be disabled -- i.e. {@link #mVibrationDisabledConfig}
* is true *and* {@link #mAccessibilityEnabled} is false.
*/
@GuardedBy("mLock")
private boolean mVibrationDisabledEffective;
/**
* {@code true} if animation is disabled in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_ANIMATION_DISABLED
*/
@GuardedBy("mLock")
private boolean mAnimationDisabled;
/**
* {@code true} if sound trigger is disabled in battery saver mode
* in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_SOUNDTRIGGER_DISABLED
*/
@GuardedBy("mLock")
private boolean mSoundTriggerDisabled;
/**
* {@code true} if full backup is deferred in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_FULLBACKUP_DEFERRED
*/
@GuardedBy("mLock")
private boolean mFullBackupDeferred;
/**
* {@code true} if key value backup is deferred in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_KEYVALUE_DEFERRED
*/
@GuardedBy("mLock")
private boolean mKeyValueBackupDeferred;
/**
* {@code true} if network policy firewall is disabled in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_FIREWALL_DISABLED
*/
@GuardedBy("mLock")
private boolean mFireWallDisabled;
/**
* {@code true} if adjust brightness is disabled in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_ADJUST_BRIGHTNESS_DISABLED
*/
@GuardedBy("mLock")
private boolean mAdjustBrightnessDisabled;
/**
* {@code true} if data saver is disabled in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_DATASAVER_DISABLED
*/
@GuardedBy("mLock")
private boolean mDataSaverDisabled;
/**
* {@code true} if launch boost should be disabled on battery saver.
*/
@GuardedBy("mLock")
private boolean mLaunchBoostDisabled;
/**
* This is the flag to decide the gps mode in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_GPS_MODE
*/
@GuardedBy("mLock")
private int mGpsMode;
/**
* This is the flag to decide the how much to adjust the screen brightness. This is
* the float value from 0 to 1 where 1 means don't change brightness.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_ADJUST_BRIGHTNESS_FACTOR
*/
@GuardedBy("mLock")
private float mAdjustBrightnessFactor;
/**
* Whether to put all apps in the stand-by mode.
*/
@GuardedBy("mLock")
private boolean mForceAllAppsStandby;
/**
* Whether to put all apps in the stand-by mode.
*/
@GuardedBy("mLock")
private boolean mForceBackgroundCheck;
/**
* Whether to show non-essential sensors (e.g. edge sensors) or not.
*/
@GuardedBy("mLock")
private boolean mOptionalSensorsDisabled;
/**
* Whether AOD is enabled or not.
*/
@GuardedBy("mLock")
private boolean mAodDisabled;
/**
* Whether BatterySavingStats should send tron events.
*/
@GuardedBy("mLock")
private boolean mSendTronLog;
@GuardedBy("mLock")
@VisibleForTesting
void updateConstantsLocked(final String setting, final String deviceSpecificSetting) {
mSettings = setting;
mDeviceSpecificSettings = deviceSpecificSetting;
if (DEBUG) {
Slog.i(TAG, "mSettings=" + mSettings);
Slog.i(TAG, "mDeviceSpecificSettings=" + mDeviceSpecificSettings);
}
final KeyValueListParser parser = new KeyValueListParser(',');
// Non-device-specific parameters.
try {
parser.setString(setting);
} catch (IllegalArgumentException e) {
Slog.wtf(TAG, "Bad battery saver constants: " + setting);
}
mVibrationDisabledConfig = parser.getBoolean(KEY_VIBRATION_DISABLED, true);
mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, false);
mSoundTriggerDisabled = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true);
mFullBackupDeferred = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
mKeyValueBackupDeferred = parser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
mFireWallDisabled = parser.getBoolean(KEY_FIREWALL_DISABLED, false);
mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, true);
mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
mLaunchBoostDisabled = parser.getBoolean(KEY_LAUNCH_BOOST_DISABLED, true);
mForceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, true);
mForceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK, true);
mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
mAodDisabled = parser.getBoolean(KEY_AOD_DISABLED, true);
mSendTronLog = parser.getBoolean(KEY_SEND_TRON_LOG, false);
......
}
private static final String KEY_GPS_MODE = "gps_mode";
private static final String KEY_VIBRATION_DISABLED = "vibration_disabled";
private static final String KEY_ANIMATION_DISABLED = "animation_disabled";
private static final String KEY_SOUNDTRIGGER_DISABLED = "soundtrigger_disabled";
private static final String KEY_FIREWALL_DISABLED = "firewall_disabled";
private static final String KEY_ADJUST_BRIGHTNESS_DISABLED = "adjust_brightness_disabled";
private static final String KEY_DATASAVER_DISABLED = "datasaver_disabled";
private static final String KEY_LAUNCH_BOOST_DISABLED = "launch_boost_disabled";
private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
private static final String KEY_FORCE_BACKGROUND_CHECK = "force_background_check";
private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
private static final String KEY_AOD_DISABLED = "aod_disabled";
private static final String KEY_SEND_TRON_LOG = "send_tron_log";
private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n";
@Override
public void onChange(boolean selfChange, Uri uri) {
refreshSettings();
}
private void refreshSettings() {
final BatterySaverPolicyListener[] listeners;
synchronized (mLock) {
// Load the non-device-specific setting.
final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS);
// Load the device specific setting.
// We first check the global setting, and if it's empty or the string "null" is set,
// use the default value from config.xml.
String deviceSpecificSetting = getGlobalSetting(
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS);
mDeviceSpecificSettingsSource =
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS;
if (TextUtils.isEmpty(deviceSpecificSetting) || "null".equals(deviceSpecificSetting)) {
deviceSpecificSetting =
mContext.getString(getDeviceSpecificConfigResId());
mDeviceSpecificSettingsSource = "(overlay)";
}
// Update.
updateConstantsLocked(setting, deviceSpecificSetting);
listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]);
}
// Notify the listeners.
mHandler.post(() -> {
for (BatterySaverPolicyListener listener : listeners) {
listener.onBatterySaverPolicyChanged(this);
}
});
}
/**
* Called by {@link PowerManagerService#systemReady}, *with no lock held.*
*/
public void systemReady() {
ConcurrentUtils.wtfIfLockHeld(TAG, mLock);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.BATTERY_SAVER_CONSTANTS), false, this);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this);
......
}
- 省电模式的限制由系统属性配置决定
/**
* Battery Saver specific settings
* This is encoded as a key=value list, separated by commas. Ex:
*
* "vibration_disabled=true,adjust_brightness_factor=0.5"
*
* The following keys are supported:
*
* <pre>
* vibration_disabled (boolean)
* animation_disabled (boolean)
* soundtrigger_disabled (boolean)
* fullbackup_deferred (boolean)
* keyvaluebackup_deferred (boolean)
* firewall_disabled (boolean)
* gps_mode (int)
* adjust_brightness_disabled (boolean)
* adjust_brightness_factor (float)
* </pre>
* @hide
* @see com.android.server.power.BatterySaverPolicy
*/
public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
/**
* Battery Saver device specific settings
* This is encoded as a key=value list, separated by commas.
* See {@link com.android.server.power.BatterySaverPolicy} for the details.
*
* @hide
*/
public static final String BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS =
"battery_saver_device_specific_constants";
到这里基本就明确了限制单个功能的逻辑,不同电量时可能设置了不同的系统属性,最终下发处理各个限制。
调试分析
尝试放开部分限制测试
- 放开网络策略防火墙后问题修复:adb shell settings put global battery_saver_constants firewall_disabled=true
代码逻辑分析
- 限制执行代码在NetworkPolicyManagerService.java
private void initService(CountDownLatch initCompleteSignal) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "systemReady");
final int oldPriority = Process.getThreadPriority(Process.myTid());
try {
// Boost thread's priority during system server init
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
if (!isBandwidthControlEnabled()) {
Slog.w(TAG, "bandwidth controls disabled, unable to enforce policy");
return;
}
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class);
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
updatePowerSaveWhitelistUL();
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@Override
public int getServiceType() {
return ServiceType.NETWORK_FIREWALL;
}
@Override
public void onLowPowerModeChanged(PowerSaveState result) {
final boolean enabled = result.batterySaverEnabled;
if (LOGD) {
Slog.d(TAG, "onLowPowerModeChanged(" + enabled + ")");
}
synchronized (mUidRulesFirstLock) {
if (mRestrictPower != enabled) {
mRestrictPower = enabled;
updateRulesForRestrictPowerUL();
}
}
}
});
......
}
}
.......
} finally {
// Restore the default priority after init is done
Process.setThreadPriority(oldPriority);
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
// TODO: rename / document to make it clear these are global (not app-specific) rules
private void updateRulesForRestrictPowerUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL");
try {
// 刷新设置网络限制
updateRulesForDeviceIdleUL();
updateRulesForPowerSaveUL();
updateRulesForAllAppsUL(TYPE_RESTRICT_POWER);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
- 网络限制支持白名单豁免
NetworkPolicyManagerService.java
void updatePowerSaveWhitelistUL() {
try {
int[] whitelist = mDeviceIdleController.getAppIdWhitelistExceptIdle();
mPowerSaveWhitelistExceptIdleAppIds.clear();
if (whitelist != null) {
for (int uid : whitelist) {
mPowerSaveWhitelistExceptIdleAppIds.put(uid, true);
}
}
whitelist = mDeviceIdleController.getAppIdWhitelist();
mPowerSaveWhitelistAppIds.clear();
if (whitelist != null) {
for (int uid : whitelist) {
mPowerSaveWhitelistAppIds.put(uid, true);
}
}
} catch (RemoteException e) {
}
}
DeviceIdleController.java
@Override public int[] getAppIdWhitelistExceptIdle() {
return getAppIdWhitelistExceptIdleInternal();
}
@Override public int[] getAppIdWhitelist() {
return getAppIdWhitelistInternal();
}
public int[] getAppIdWhitelistExceptIdleInternal() {
synchronized (this) {
return mPowerSaveWhitelistExceptIdleAppIdArray;
}
}
public int[] getAppIdWhitelistInternal() {
synchronized (this) {
return mPowerSaveWhitelistAllAppIdArray;
}
}
public void resetPowerSaveWhitelistExceptIdleInternal() {
synchronized (this) {
if (mPowerSaveWhitelistAppsExceptIdle.removeAll(
mPowerSaveWhitelistUserAppsExceptIdle)) {
reportPowerSaveWhitelistChangedLocked();
mPowerSaveWhitelistExceptIdleAppIdArray = buildAppIdArray(
mPowerSaveWhitelistAppsExceptIdle, mPowerSaveWhitelistUserApps,
mPowerSaveWhitelistExceptIdleAppIds);
mPowerSaveWhitelistUserAppsExceptIdle.clear();
passWhiteListsToForceAppStandbyTrackerLocked();
}
}
}
private void updateWhitelistAppIdsLocked() {
mPowerSaveWhitelistExceptIdleAppIdArray = buildAppIdArray(mPowerSaveWhitelistAppsExceptIdle,
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds);
mPowerSaveWhitelistAllAppIdArray = buildAppIdArray(mPowerSaveWhitelistApps,
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistAllAppIds);
mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null,
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds);
if (mLocalActivityManager != null) {
mLocalActivityManager.setDeviceIdleWhitelist(
mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray);
}
if (mLocalPowerManager != null) {
if (DEBUG) {
Slog.d(TAG, "Setting wakelock whitelist to "
+ Arrays.toString(mPowerSaveWhitelistAllAppIdArray));
}
mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
}
passWhiteListsToForceAppStandbyTrackerLocked();
}
- 这里可以看到和几个集合有关系mPowerSaveWhitelistApps、mPowerSaveWhitelistUserApps、mPowerSaveWhitelistAllAppIds、mPowerSaveWhitelistAppsExceptIdle、mPowerSaveWhitelistExceptIdleAppIds,有几个集合是代码动态设置。
初始配置白名单
DeviceIdleController.java
@Override
public void onStart() {
final PackageManager pm = getContext().getPackageManager();
synchronized (this) {
mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enableAutoPowerModes);
SystemConfig sysConfig = SystemConfig.getInstance();
ArraySet<String> allowPowerExceptIdle = sysConfig.getAllowInPowerSaveExceptIdle();
for (int i=0; i<allowPowerExceptIdle.size(); i++) {
String pkg = allowPowerExceptIdle.valueAt(i);
try {
ApplicationInfo ai = pm.getApplicationInfo(pkg,
PackageManager.MATCH_SYSTEM_ONLY);
int appid = UserHandle.getAppId(ai.uid);
mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
} catch (PackageManager.NameNotFoundException e) {
}
}
ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();
for (int i=0; i<allowPower.size(); i++) {
String pkg = allowPower.valueAt(i);
try {
ApplicationInfo ai = pm.getApplicationInfo(pkg,
PackageManager.MATCH_SYSTEM_ONLY);
int appid = UserHandle.getAppId(ai.uid);
// These apps are on both the whitelist-except-idle as well
// as the full whitelist, so they apply in all cases.
mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
mPowerSaveWhitelistApps.put(ai.packageName, appid);
mPowerSaveWhitelistSystemAppIds.put(appid, true);
} catch (PackageManager.NameNotFoundException e) {
}
}
......
}
SystemConfig.java
public ArraySet<String> getAllowInPowerSaveExceptIdle() {
return mAllowInPowerSaveExceptIdle;
}
public ArraySet<String> getAllowInPowerSave() {
return mAllowInPowerSave;
}
private void readPermissionsFromXml(File permFile, int permissionFlag) {
FileReader permReader = null;
try {
permReader = new FileReader(permFile);
} catch (FileNotFoundException e) {
Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
return;
}
final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(permReader);
int type;
while ((type=parser.next()) != parser.START_TAG
&& type != parser.END_DOCUMENT) {
;
}
if (type != parser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
throw new XmlPullParserException("Unexpected start tag in " + permFile
+ ": found " + parser.getName() + ", expected 'permissions' or 'config'");
}
boolean allowAll = permissionFlag == ALLOW_ALL;
boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;
boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING) != 0;
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;
}
String name = parser.getName();
......
if ("allow-in-power-save-except-idle".equals(name) && allowAll) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowInPowerSaveExceptIdle.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("allow-in-power-save".equals(name) && allowAll) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mAllowInPowerSave.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
}
......
}
data/etc/platform.xml 对于后台进程会限制访问网络,这里可以设置白名单豁免
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
<allow-in-power-save package="com.android.providers.downloads" />
<!-- These are the standard packages that are white-listed to always have internet
access while in data mode, even if they aren't in the foreground. -->
<allow-in-data-usage-save package="com.android.providers.downloads" />
<!-- This is a core platform component that needs to freely run in the background -->
<allow-in-power-save package="com.android.cellbroadcastreceiver" />
<allow-in-power-save package="com.android.shell" />
<!-- Whitelist system providers -->
<allow-in-power-save-except-idle package="com.android.providers.calendar" />
<allow-in-power-save-except-idle package="com.android.providers.contacts" />
代码新增白名单并存储
- 存储
DeviceIdleController.java
public boolean addPowerSaveWhitelistAppInternal(String name) {
synchronized (this) {
try {
ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name,
PackageManager.MATCH_ANY_USER);
if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid)) == null) {
reportPowerSaveWhitelistChangedLocked();
updateWhitelistAppIdsLocked();
writeConfigFileLocked();
}
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}
void writeConfigFileLocked() {
mHandler.removeMessages(MSG_WRITE_CONFIG);
mHandler.sendEmptyMessageDelayed(MSG_WRITE_CONFIG, 5000);
}
void handleWriteConfigFile() {
final ByteArrayOutputStream memStream = new ByteArrayOutputStream();
try {
synchronized (this) {
XmlSerializer out = new FastXmlSerializer();
out.setOutput(memStream, StandardCharsets.UTF_8.name());
writeConfigFileLocked(out);
}
} catch (IOException e) {
}
synchronized (mConfigFile) {
FileOutputStream stream = null;
try {
stream = mConfigFile.startWrite();
memStream.writeTo(stream);
stream.flush();
FileUtils.sync(stream);
stream.close();
mConfigFile.finishWrite(stream);
} catch (IOException e) {
Slog.w(TAG, "Error writing config file", e);
mConfigFile.failWrite(stream);
}
}
}
public DeviceIdleController(Context context) {
super(context);
mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
mAppStateTracker = new AppStateTracker(context, FgThread.get().getLooper());
LocalServices.addService(AppStateTracker.class, mAppStateTracker);
}
private static File getSystemDir() {
return new File(Environment.getDataDirectory(), "system");
}
- 代码设置-framework内部代码设置,IDeviceIdleController是hide类
示例代码:
IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));获取对象
DeviceIdleController.java
private final class BinderService extends IDeviceIdleController.Stub {
@Override public void addPowerSaveWhitelistApp(String name) {
if (DEBUG) {
Slog.i(TAG, "addPowerSaveWhitelistApp(name = " + name + ")");
}
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
null);
long ident = Binder.clearCallingIdentity();
try {
addPowerSaveWhitelistAppInternal(name);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public void onStart() {
......
mBinderService = new BinderService();
publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
publishLocalService(LocalService.class, new LocalService());
}
- 代码设置-软件层
因为是SettingsLib下的类,需要引入out\target\common\obj\JAVA_LIBRARIES\SettingsLib_intermediates/classes.jar,或者mk编译加入SettingsLib模块。
packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
/**
* Handles getting/changing the whitelist for the exceptions to battery saving features.
*/
public class PowerWhitelistBackend {
private static final String TAG = "PowerWhitelistBackend";
private static final String DEVICE_IDLE_SERVICE = "deviceidle";
private static PowerWhitelistBackend sInstance;
private final Context mAppContext;
private final IDeviceIdleController mDeviceIdleService;
private final ArraySet<String> mWhitelistedApps = new ArraySet<>();
private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>();
private final ArraySet<String> mSysWhitelistedAppsExceptIdle = new ArraySet<>();
public PowerWhitelistBackend(Context context) {
this(context, IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(DEVICE_IDLE_SERVICE)));
}
public void addApp(String pkg) {
try {
mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
mWhitelistedApps.add(pkg);
} catch (RemoteException e) {
Log.w(TAG, "Unable to reach IDeviceIdleController", e);
}
}
}
- ShellCommand命令设置
int onShellCommand(Shell shell, String cmd) {
PrintWriter pw = shell.getOutPrintWriter();
......
if ("whitelist".equals(cmd)) {
String arg = shell.getNextArg();
if (arg != null) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
long token = Binder.clearCallingIdentity();
try {
do {
if (arg.length() < 1 || (arg.charAt(0) != '-'
&& arg.charAt(0) != '+' && arg.charAt(0) != '=')) {
pw.println("Package must be prefixed with +, -, or =: " + arg);
return -1;
}
char op = arg.charAt(0);
String pkg = arg.substring(1);
if (op == '+') {
if (addPowerSaveWhitelistAppInternal(pkg)) {
pw.println("Added: " + pkg);
} else {
pw.println("Unknown package: " + pkg);
}
} else if (op == '-') {
if (removePowerSaveWhitelistAppInternal(pkg)) {
pw.println("Removed: " + pkg);
}
} else {
pw.println(getPowerSaveWhitelistAppInternal(pkg));
}
} while ((arg=shell.getNextArg()) != null);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
......
return 0;
}
android原生设置支持白名单豁免
adb pull /data/system/deviceidle.xml
操作步骤:
adb shell am start com.android.settings把后台进程电池优化设置为不优化