BatterySaverPolicy.java revision 3aaed2912be642d306fa223edcb58278b0e45795
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.android.server.power; 17 18import android.content.ContentResolver; 19import android.content.Context; 20import android.database.ContentObserver; 21import android.net.Uri; 22import android.os.Handler; 23import android.os.PowerManager.ServiceType; 24import android.os.PowerSaveState; 25import android.provider.Settings; 26import android.provider.Settings.Global; 27import android.text.TextUtils; 28import android.util.ArrayMap; 29import android.util.KeyValueListParser; 30import android.util.Slog; 31 32import com.android.internal.annotations.GuardedBy; 33import com.android.internal.annotations.VisibleForTesting; 34import com.android.internal.R; 35 36import java.io.PrintWriter; 37import java.util.ArrayList; 38import java.util.List; 39 40/** 41 * Class to decide whether to turn on battery saver mode for specific service 42 * 43 * TODO: We should probably make {@link #mFilesForInteractive} and {@link #mFilesForNoninteractive} 44 * less flexible and just take a list of "CPU number - frequency" pairs. Being able to write 45 * anything under /sys/ and /proc/ is too loose. 46 * 47 * Test: atest BatterySaverPolicyTest 48 */ 49public class BatterySaverPolicy extends ContentObserver { 50 private static final String TAG = "BatterySaverPolicy"; 51 52 // Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode. 53 public static final int GPS_MODE_NO_CHANGE = 0; 54 // Value of batterySaverGpsMode such that GPS is disabled when battery saver mode 55 // is enabled and the screen is off. 56 public static final int GPS_MODE_DISABLED_WHEN_SCREEN_OFF = 1; 57 // Secure setting for GPS behavior when battery saver mode is on. 58 public static final String SECURE_KEY_GPS_MODE = "batterySaverGpsMode"; 59 60 private static final String KEY_GPS_MODE = "gps_mode"; 61 private static final String KEY_VIBRATION_DISABLED = "vibration_disabled"; 62 private static final String KEY_ANIMATION_DISABLED = "animation_disabled"; 63 private static final String KEY_SOUNDTRIGGER_DISABLED = "soundtrigger_disabled"; 64 private static final String KEY_FIREWALL_DISABLED = "firewall_disabled"; 65 private static final String KEY_ADJUST_BRIGHTNESS_DISABLED = "adjust_brightness_disabled"; 66 private static final String KEY_DATASAVER_DISABLED = "datasaver_disabled"; 67 private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor"; 68 private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred"; 69 private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred"; 70 private static final String KEY_FORCE_ALL_APPS_STANDBY_JOBS = "force_all_apps_standby_jobs"; 71 private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms"; 72 private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled"; 73 74 private static final String KEY_FILE_FOR_INTERACTIVE_PREFIX = "file-on:"; 75 private static final String KEY_FILE_FOR_NONINTERACTIVE_PREFIX = "file-off:"; 76 77 private static String mSettings; 78 private static String mDeviceSpecificSettings; 79 private static String mDeviceSpecificSettingsSource; // For dump() only. 80 81 /** 82 * {@code true} if vibration is disabled in battery saver mode. 83 * 84 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 85 * @see #KEY_VIBRATION_DISABLED 86 */ 87 private boolean mVibrationDisabled; 88 89 /** 90 * {@code true} if animation is disabled in battery saver mode. 91 * 92 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 93 * @see #KEY_ANIMATION_DISABLED 94 */ 95 private boolean mAnimationDisabled; 96 97 /** 98 * {@code true} if sound trigger is disabled in battery saver mode 99 * in battery saver mode. 100 * 101 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 102 * @see #KEY_SOUNDTRIGGER_DISABLED 103 */ 104 private boolean mSoundTriggerDisabled; 105 106 /** 107 * {@code true} if full backup is deferred in battery saver mode. 108 * 109 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 110 * @see #KEY_FULLBACKUP_DEFERRED 111 */ 112 private boolean mFullBackupDeferred; 113 114 /** 115 * {@code true} if key value backup is deferred in battery saver mode. 116 * 117 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 118 * @see #KEY_KEYVALUE_DEFERRED 119 */ 120 private boolean mKeyValueBackupDeferred; 121 122 /** 123 * {@code true} if network policy firewall is disabled in battery saver mode. 124 * 125 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 126 * @see #KEY_FIREWALL_DISABLED 127 */ 128 private boolean mFireWallDisabled; 129 130 /** 131 * {@code true} if adjust brightness is disabled in battery saver mode. 132 * 133 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 134 * @see #KEY_ADJUST_BRIGHTNESS_DISABLED 135 */ 136 private boolean mAdjustBrightnessDisabled; 137 138 /** 139 * {@code true} if data saver is disabled in battery saver mode. 140 * 141 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 142 * @see #KEY_DATASAVER_DISABLED 143 */ 144 private boolean mDataSaverDisabled; 145 146 /** 147 * This is the flag to decide the gps mode in battery saver mode. 148 * 149 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 150 * @see #KEY_GPS_MODE 151 */ 152 private int mGpsMode; 153 154 /** 155 * This is the flag to decide the how much to adjust the screen brightness. This is 156 * the float value from 0 to 1 where 1 means don't change brightness. 157 * 158 * @see Settings.Global#BATTERY_SAVER_CONSTANTS 159 * @see #KEY_ADJUST_BRIGHTNESS_FACTOR 160 */ 161 private float mAdjustBrightnessFactor; 162 163 /** 164 * Whether to put all apps in the stand-by mode or not for job scheduler. 165 */ 166 private boolean mForceAllAppsStandbyJobs; 167 168 /** 169 * Whether to put all apps in the stand-by mode or not for alarms. 170 */ 171 private boolean mForceAllAppsStandbyAlarms; 172 173 /** 174 * Weather to show non-essential sensors (e.g. edge sensors) or not. 175 */ 176 private boolean mOptionalSensorsDisabled; 177 178 private final Object mLock = new Object(); 179 180 @GuardedBy("mLock") 181 private Context mContext; 182 183 @GuardedBy("mLock") 184 private ContentResolver mContentResolver; 185 186 @GuardedBy("mLock") 187 private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>(); 188 189 /** 190 * List of [Filename -> content] that should be written when battery saver is activated 191 * and the device is interactive. 192 * 193 * We use this to change the max CPU frequencies. 194 */ 195 @GuardedBy("mLock") 196 private ArrayMap<String, String> mFilesForInteractive; 197 198 /** 199 * List of [Filename -> content] that should be written when battery saver is activated 200 * and the device is non-interactive. 201 * 202 * We use this to change the max CPU frequencies. 203 */ 204 @GuardedBy("mLock") 205 private ArrayMap<String, String> mFilesForNoninteractive; 206 207 public interface BatterySaverPolicyListener { 208 void onBatterySaverPolicyChanged(BatterySaverPolicy policy); 209 } 210 211 public BatterySaverPolicy(Handler handler) { 212 super(handler); 213 } 214 215 public void systemReady(Context context) { 216 synchronized (mLock) { 217 mContext = context; 218 mContentResolver = context.getContentResolver(); 219 220 mContentResolver.registerContentObserver(Settings.Global.getUriFor( 221 Settings.Global.BATTERY_SAVER_CONSTANTS), false, this); 222 mContentResolver.registerContentObserver(Settings.Global.getUriFor( 223 Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this); 224 } 225 onChange(true, null); 226 } 227 228 public void addListener(BatterySaverPolicyListener listener) { 229 synchronized (mLock) { 230 mListeners.add(listener); 231 } 232 } 233 234 @VisibleForTesting 235 String getGlobalSetting(String key) { 236 return Settings.Global.getString(mContentResolver, key); 237 } 238 239 @VisibleForTesting 240 int getDeviceSpecificConfigResId() { 241 return R.string.config_batterySaverDeviceSpecificConfig; 242 } 243 244 @Override 245 public void onChange(boolean selfChange, Uri uri) { 246 final BatterySaverPolicyListener[] listeners; 247 synchronized (mLock) { 248 // Load the non-device-specific setting. 249 final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS); 250 251 // Load the device specific setting. 252 // We first check the global setting, and if it's empty or the string "null" is set, 253 // use the default value from config.xml. 254 String deviceSpecificSetting = getGlobalSetting( 255 Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS); 256 mDeviceSpecificSettingsSource = 257 Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS; 258 259 if (TextUtils.isEmpty(deviceSpecificSetting) || "null".equals(deviceSpecificSetting)) { 260 deviceSpecificSetting = 261 mContext.getString(getDeviceSpecificConfigResId()); 262 mDeviceSpecificSettingsSource = "(overlay)"; 263 } 264 265 // Update. 266 updateConstantsLocked(setting, deviceSpecificSetting); 267 268 listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]); 269 } 270 271 // Notify the listeners. 272 for (BatterySaverPolicyListener listener : listeners) { 273 listener.onBatterySaverPolicyChanged(this); 274 } 275 } 276 277 @VisibleForTesting 278 void updateConstantsLocked(final String setting, final String deviceSpecificSetting) { 279 mSettings = setting; 280 mDeviceSpecificSettings = deviceSpecificSetting; 281 282 final KeyValueListParser parser = new KeyValueListParser(','); 283 284 // Non-device-specific parameters. 285 try { 286 parser.setString(setting); 287 } catch (IllegalArgumentException e) { 288 Slog.wtf(TAG, "Bad battery saver constants: " + setting); 289 } 290 291 mVibrationDisabled = parser.getBoolean(KEY_VIBRATION_DISABLED, true); 292 mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, true); 293 mSoundTriggerDisabled = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true); 294 mFullBackupDeferred = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, true); 295 mKeyValueBackupDeferred = parser.getBoolean(KEY_KEYVALUE_DEFERRED, true); 296 mFireWallDisabled = parser.getBoolean(KEY_FIREWALL_DISABLED, false); 297 mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false); 298 mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f); 299 mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true); 300 mForceAllAppsStandbyJobs = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true); 301 mForceAllAppsStandbyAlarms = 302 parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true); 303 mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true); 304 305 // Get default value from Settings.Secure 306 final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE, 307 GPS_MODE_NO_CHANGE); 308 mGpsMode = parser.getInt(KEY_GPS_MODE, defaultGpsMode); 309 310 // Non-device-specific parameters. 311 try { 312 parser.setString(deviceSpecificSetting); 313 } catch (IllegalArgumentException e) { 314 Slog.wtf(TAG, "Bad device specific battery saver constants: " 315 + deviceSpecificSetting); 316 } 317 318 mFilesForInteractive = collectParams(parser, KEY_FILE_FOR_INTERACTIVE_PREFIX); 319 mFilesForNoninteractive = collectParams(parser, KEY_FILE_FOR_NONINTERACTIVE_PREFIX); 320 } 321 322 private static ArrayMap<String, String> collectParams( 323 KeyValueListParser parser, String prefix) { 324 final ArrayMap<String, String> ret = new ArrayMap<>(); 325 326 for (int i = parser.size() - 1; i >= 0; i--) { 327 final String key = parser.keyAt(i); 328 if (!key.startsWith(prefix)) { 329 continue; 330 } 331 final String path = key.substring(prefix.length()); 332 333 if (!(path.startsWith("/sys/") || path.startsWith("/proc/"))) { 334 Slog.wtf(TAG, "Invalid path: " + path); 335 continue; 336 } 337 338 ret.put(path, parser.getString(key, "")); 339 } 340 return ret; 341 } 342 343 /** 344 * Get the {@link PowerSaveState} based on {@paramref type} and {@paramref realMode}. 345 * The result will have {@link PowerSaveState#batterySaverEnabled} and some other 346 * parameters when necessary. 347 * 348 * @param type type of the service, one of {@link ServiceType} 349 * @param realMode whether the battery saver is on by default 350 * @return State data that contains battery saver data 351 */ 352 public PowerSaveState getBatterySaverPolicy(@ServiceType int type, boolean realMode) { 353 synchronized (mLock) { 354 final PowerSaveState.Builder builder = new PowerSaveState.Builder() 355 .setGlobalBatterySaverEnabled(realMode); 356 if (!realMode) { 357 return builder.setBatterySaverEnabled(realMode) 358 .build(); 359 } 360 switch (type) { 361 case ServiceType.GPS: 362 return builder.setBatterySaverEnabled(realMode) 363 .setGpsMode(mGpsMode) 364 .build(); 365 case ServiceType.ANIMATION: 366 return builder.setBatterySaverEnabled(mAnimationDisabled) 367 .build(); 368 case ServiceType.FULL_BACKUP: 369 return builder.setBatterySaverEnabled(mFullBackupDeferred) 370 .build(); 371 case ServiceType.KEYVALUE_BACKUP: 372 return builder.setBatterySaverEnabled(mKeyValueBackupDeferred) 373 .build(); 374 case ServiceType.NETWORK_FIREWALL: 375 return builder.setBatterySaverEnabled(!mFireWallDisabled) 376 .build(); 377 case ServiceType.SCREEN_BRIGHTNESS: 378 return builder.setBatterySaverEnabled(!mAdjustBrightnessDisabled) 379 .setBrightnessFactor(mAdjustBrightnessFactor) 380 .build(); 381 case ServiceType.DATA_SAVER: 382 return builder.setBatterySaverEnabled(!mDataSaverDisabled) 383 .build(); 384 case ServiceType.SOUND: 385 return builder.setBatterySaverEnabled(mSoundTriggerDisabled) 386 .build(); 387 case ServiceType.VIBRATION: 388 return builder.setBatterySaverEnabled(mVibrationDisabled) 389 .build(); 390 case ServiceType.FORCE_ALL_APPS_STANDBY_JOBS: 391 return builder.setBatterySaverEnabled(mForceAllAppsStandbyJobs) 392 .build(); 393 case ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS: 394 return builder.setBatterySaverEnabled(mForceAllAppsStandbyAlarms) 395 .build(); 396 case ServiceType.OPTIONAL_SENSORS: 397 return builder.setBatterySaverEnabled(mOptionalSensorsDisabled) 398 .build(); 399 default: 400 return builder.setBatterySaverEnabled(realMode) 401 .build(); 402 } 403 } 404 } 405 406 public ArrayMap<String, String> getFileValues(boolean interactive) { 407 synchronized (mLock) { 408 return interactive ? mFilesForInteractive : mFilesForNoninteractive; 409 } 410 } 411 412 public void dump(PrintWriter pw) { 413 synchronized (mLock) { 414 pw.println(); 415 pw.println("Battery saver policy"); 416 pw.println(" Settings " + Settings.Global.BATTERY_SAVER_CONSTANTS); 417 pw.println(" value: " + mSettings); 418 pw.println(" Settings " + mDeviceSpecificSettingsSource); 419 pw.println(" value: " + mDeviceSpecificSettings); 420 421 pw.println(); 422 pw.println(" " + KEY_VIBRATION_DISABLED + "=" + mVibrationDisabled); 423 pw.println(" " + KEY_ANIMATION_DISABLED + "=" + mAnimationDisabled); 424 pw.println(" " + KEY_FULLBACKUP_DEFERRED + "=" + mFullBackupDeferred); 425 pw.println(" " + KEY_KEYVALUE_DEFERRED + "=" + mKeyValueBackupDeferred); 426 pw.println(" " + KEY_FIREWALL_DISABLED + "=" + mFireWallDisabled); 427 pw.println(" " + KEY_DATASAVER_DISABLED + "=" + mDataSaverDisabled); 428 pw.println(" " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled); 429 pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor); 430 pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode); 431 pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs); 432 pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms); 433 pw.println(" " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled); 434 pw.println(); 435 436 pw.print(" Interactive File values:\n"); 437 dumpMap(pw, " ", mFilesForInteractive); 438 pw.println(); 439 440 pw.print(" Noninteractive File values:\n"); 441 dumpMap(pw, " ", mFilesForNoninteractive); 442 pw.println(); 443 } 444 } 445 446 private void dumpMap(PrintWriter pw, String prefix, ArrayMap<String, String> map) { 447 if (map == null) { 448 return; 449 } 450 final int size = map.size(); 451 for (int i = 0; i < size; i++) { 452 pw.print(prefix); 453 pw.print(map.keyAt(i)); 454 pw.print(": '"); 455 pw.print(map.valueAt(i)); 456 pw.println("'"); 457 } 458 } 459} 460