1 /* 2 * Copyright (C) 2018 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 */ 16 package com.android.server.power.batterysaver; 17 18 import static com.android.server.power.batterysaver.BatterySaverController.reasonToString; 19 20 import android.annotation.NonNull; 21 import android.annotation.StringRes; 22 import android.app.Notification; 23 import android.app.NotificationChannel; 24 import android.app.NotificationManager; 25 import android.app.PendingIntent; 26 import android.content.ContentResolver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.res.Resources; 30 import android.database.ContentObserver; 31 import android.os.BatterySaverPolicyConfig; 32 import android.os.Handler; 33 import android.os.PowerManager; 34 import android.os.SystemClock; 35 import android.os.UserHandle; 36 import android.provider.Settings; 37 import android.util.IndentingPrintWriter; 38 import android.util.Slog; 39 import android.util.proto.ProtoOutputStream; 40 41 import com.android.internal.R; 42 import com.android.internal.annotations.GuardedBy; 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.internal.os.BackgroundThread; 45 import com.android.server.EventLogTags; 46 import com.android.server.power.BatterySaverStateMachineProto; 47 48 import java.io.PrintWriter; 49 import java.time.Duration; 50 51 /** 52 * Decides when to enable / disable battery saver. 53 * 54 * IMPORTANT: This class shares the power manager lock, which is very low in the lock hierarchy. 55 * Do not call out with the lock held. (Settings provider is okay.) 56 * 57 * Test: atest com.android.server.power.batterysaver.BatterySaverStateMachineTest 58 * 59 * Current state machine. This can be visualized using Graphviz: 60 <pre> 61 62 digraph { 63 STATE_OFF 64 STATE_MANUAL_ON [label="STATE_MANUAL_ON\nTurned on manually by the user"] 65 STATE_AUTOMATIC_ON [label="STATE_AUTOMATIC_ON\nTurned on automatically by the system"] 66 STATE_OFF_AUTOMATIC_SNOOZED [ 67 label="STATE_OFF_AUTOMATIC_SNOOZED\nTurned off manually by the user." 68 + " The system should not turn it back on automatically." 69 ] 70 STATE_PENDING_STICKY_ON [ 71 label="STATE_PENDING_STICKY_ON\n" 72 + " Turned on manually by the user and then plugged in. Will turn back on after unplug." 73 ] 74 75 STATE_OFF -> STATE_MANUAL_ON [label="manual"] 76 STATE_OFF -> STATE_AUTOMATIC_ON [label="Auto on AND charge <= auto threshold"] 77 78 STATE_MANUAL_ON -> STATE_OFF [label="manual\nOR\nPlugged & sticky disabled"] 79 STATE_MANUAL_ON -> STATE_PENDING_STICKY_ON [label="Plugged & sticky enabled"] 80 81 STATE_PENDING_STICKY_ON -> STATE_MANUAL_ON [label="Unplugged & sticky enabled"] 82 STATE_PENDING_STICKY_ON -> STATE_OFF [ 83 label="Sticky disabled\nOR\nSticky auto off enabled AND charge >= sticky auto off threshold" 84 ] 85 86 STATE_AUTOMATIC_ON -> STATE_OFF [label="Plugged"] 87 STATE_AUTOMATIC_ON -> STATE_OFF_AUTOMATIC_SNOOZED [label="Manual"] 88 89 STATE_OFF_AUTOMATIC_SNOOZED -> STATE_OFF [label="Plug\nOR\nCharge > auto threshold"] 90 STATE_OFF_AUTOMATIC_SNOOZED -> STATE_MANUAL_ON [label="manual"] 91 92 </pre> 93 } 94 */ 95 public class BatterySaverStateMachine { 96 private static final String TAG = "BatterySaverStateMachine"; 97 private static final String DYNAMIC_MODE_NOTIF_CHANNEL_ID = "dynamic_mode_notification"; 98 private static final String BATTERY_SAVER_NOTIF_CHANNEL_ID = "battery_saver_channel"; 99 private static final int DYNAMIC_MODE_NOTIFICATION_ID = 1992; 100 private static final int STICKY_AUTO_DISABLED_NOTIFICATION_ID = 1993; 101 private final Object mLock; 102 103 private static final boolean DEBUG = BatterySaverPolicy.DEBUG; 104 105 private static final long ADAPTIVE_CHANGE_TIMEOUT_MS = 24 * 60 * 60 * 1000L; 106 107 /** Turn off adaptive battery saver if the device has charged above this level. */ 108 private static final int ADAPTIVE_AUTO_DISABLE_BATTERY_LEVEL = 80; 109 110 private static final long STICKY_DISABLED_NOTIFY_TIMEOUT_MS = Duration.ofHours(12).toMillis(); 111 112 private static final int STATE_OFF = BatterySaverStateMachineProto.STATE_OFF; 113 114 /** Turned on manually by the user. */ 115 private static final int STATE_MANUAL_ON = BatterySaverStateMachineProto.STATE_MANUAL_ON; 116 117 /** Turned on automatically by the system. */ 118 private static final int STATE_AUTOMATIC_ON = BatterySaverStateMachineProto.STATE_AUTOMATIC_ON; 119 120 /** Turned off manually by the user. The system should not turn it back on automatically. */ 121 private static final int STATE_OFF_AUTOMATIC_SNOOZED = 122 BatterySaverStateMachineProto.STATE_OFF_AUTOMATIC_SNOOZED; 123 124 /** Turned on manually by the user and then plugged in. Will turn back on after unplug. */ 125 private static final int STATE_PENDING_STICKY_ON = 126 BatterySaverStateMachineProto.STATE_PENDING_STICKY_ON; 127 128 private final Context mContext; 129 private final BatterySaverController mBatterySaverController; 130 131 /** Whether the system has booted. */ 132 @GuardedBy("mLock") 133 private boolean mBootCompleted; 134 135 /** Whether global settings have been loaded already. */ 136 @GuardedBy("mLock") 137 private boolean mSettingsLoaded; 138 139 /** Whether the first battery status has arrived. */ 140 @GuardedBy("mLock") 141 private boolean mBatteryStatusSet; 142 143 @GuardedBy("mLock") 144 private int mState; 145 146 /** Whether the device is connected to any power source. */ 147 @GuardedBy("mLock") 148 private boolean mIsPowered; 149 150 /** Current battery level in %, 0-100. (Currently only used in dumpsys.) */ 151 @GuardedBy("mLock") 152 private int mBatteryLevel; 153 154 /** Whether the battery level is considered to be "low" or not. */ 155 @GuardedBy("mLock") 156 private boolean mIsBatteryLevelLow; 157 158 /** Previously known value of Settings.Global.LOW_POWER_MODE. */ 159 @GuardedBy("mLock") 160 private boolean mSettingBatterySaverEnabled; 161 162 /** Previously known value of Settings.Global.LOW_POWER_MODE_STICKY. */ 163 @GuardedBy("mLock") 164 private boolean mSettingBatterySaverEnabledSticky; 165 166 /** Config flag to track if battery saver's sticky behaviour is disabled. */ 167 private final boolean mBatterySaverStickyBehaviourDisabled; 168 169 /** 170 * Whether or not to end sticky battery saver upon reaching a level specified by 171 * {@link #mSettingBatterySaverStickyAutoDisableThreshold}. 172 */ 173 @GuardedBy("mLock") 174 private boolean mSettingBatterySaverStickyAutoDisableEnabled; 175 176 /** 177 * The battery level at which to end sticky battery saver. Only useful if 178 * {@link #mSettingBatterySaverStickyAutoDisableEnabled} is {@code true}. 179 */ 180 @GuardedBy("mLock") 181 private int mSettingBatterySaverStickyAutoDisableThreshold; 182 183 /** 184 * Config flag to track default disable threshold for Dynamic Power Savings enabled battery 185 * saver. 186 */ 187 @GuardedBy("mLock") 188 private final int mDynamicPowerSavingsDefaultDisableThreshold; 189 190 /** 191 * Previously known value of Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL. 192 * (Currently only used in dumpsys.) 193 */ 194 @GuardedBy("mLock") 195 private int mSettingBatterySaverTriggerThreshold; 196 197 /** Previously known value of Settings.Global.AUTOMATIC_POWER_SAVE_MODE. */ 198 @GuardedBy("mLock") 199 private int mSettingAutomaticBatterySaver; 200 201 /** 202 * When to disable battery saver again if it was enabled due to an external suggestion. 203 * Corresponds to Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD. 204 */ 205 @GuardedBy("mLock") 206 private int mDynamicPowerSavingsDisableThreshold; 207 208 /** 209 * Whether we've received a suggestion that battery saver should be on from an external app. 210 * Updates when Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED changes. 211 */ 212 @GuardedBy("mLock") 213 private boolean mDynamicPowerSavingsEnableBatterySaver; 214 215 /** 216 * Last reason passed to {@link #enableBatterySaverLocked}. 217 */ 218 @GuardedBy("mLock") 219 private int mLastChangedIntReason; 220 221 /** 222 * Last reason passed to {@link #enableBatterySaverLocked}. 223 */ 224 @GuardedBy("mLock") 225 private String mLastChangedStrReason; 226 227 /** 228 * The last time adaptive battery saver was changed by an external service, using elapsed 229 * realtime as the timebase. 230 */ 231 @GuardedBy("mLock") 232 private long mLastAdaptiveBatterySaverChangedExternallyElapsed; 233 234 private final ContentObserver mSettingsObserver = new ContentObserver(null) { 235 @Override 236 public void onChange(boolean selfChange) { 237 synchronized (mLock) { 238 refreshSettingsLocked(); 239 } 240 } 241 }; 242 BatterySaverStateMachine(Object lock, Context context, BatterySaverController batterySaverController)243 public BatterySaverStateMachine(Object lock, 244 Context context, BatterySaverController batterySaverController) { 245 mLock = lock; 246 mContext = context; 247 mBatterySaverController = batterySaverController; 248 mState = STATE_OFF; 249 250 mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean( 251 com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled); 252 mDynamicPowerSavingsDefaultDisableThreshold = mContext.getResources().getInteger( 253 com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold); 254 } 255 256 /** @return true if the automatic percentage based mode should be used */ isAutomaticModeActiveLocked()257 private boolean isAutomaticModeActiveLocked() { 258 return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE 259 && mSettingBatterySaverTriggerThreshold > 0; 260 } 261 262 /** 263 * The returned value won't necessarily make sense if {@link #isAutomaticModeActiveLocked()} 264 * returns {@code false}. 265 * 266 * @return true if the battery level is below automatic's threshold. 267 */ isInAutomaticLowZoneLocked()268 private boolean isInAutomaticLowZoneLocked() { 269 return mIsBatteryLevelLow; 270 } 271 272 /** @return true if the dynamic mode should be used */ isDynamicModeActiveLocked()273 private boolean isDynamicModeActiveLocked() { 274 return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC 275 && mDynamicPowerSavingsEnableBatterySaver; 276 } 277 278 /** 279 * The returned value won't necessarily make sense if {@link #isDynamicModeActiveLocked()} 280 * returns {@code false}. 281 * 282 * @return true if the battery level is below dynamic's threshold. 283 */ isInDynamicLowZoneLocked()284 private boolean isInDynamicLowZoneLocked() { 285 return mBatteryLevel <= mDynamicPowerSavingsDisableThreshold; 286 } 287 288 /** 289 * {@link com.android.server.power.PowerManagerService} calls it when the system is booted. 290 */ onBootCompleted()291 public void onBootCompleted() { 292 if (DEBUG) { 293 Slog.d(TAG, "onBootCompleted"); 294 } 295 // Just booted. We don't want LOW_POWER_MODE to be persisted, so just always clear it. 296 putGlobalSetting(Settings.Global.LOW_POWER_MODE, 0); 297 298 // This is called with the power manager lock held. Don't do anything that may call to 299 // upper services. (e.g. don't call into AM directly) 300 // So use a BG thread. 301 runOnBgThread(() -> { 302 303 final ContentResolver cr = mContext.getContentResolver(); 304 cr.registerContentObserver(Settings.Global.getUriFor( 305 Settings.Global.LOW_POWER_MODE), 306 false, mSettingsObserver, UserHandle.USER_SYSTEM); 307 cr.registerContentObserver(Settings.Global.getUriFor( 308 Settings.Global.LOW_POWER_MODE_STICKY), 309 false, mSettingsObserver, UserHandle.USER_SYSTEM); 310 cr.registerContentObserver(Settings.Global.getUriFor( 311 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), 312 false, mSettingsObserver, UserHandle.USER_SYSTEM); 313 cr.registerContentObserver(Settings.Global.getUriFor( 314 Settings.Global.AUTOMATIC_POWER_SAVE_MODE), 315 false, mSettingsObserver, UserHandle.USER_SYSTEM); 316 cr.registerContentObserver(Settings.Global.getUriFor( 317 Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED), 318 false, mSettingsObserver, UserHandle.USER_SYSTEM); 319 cr.registerContentObserver(Settings.Global.getUriFor( 320 Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD), 321 false, mSettingsObserver, UserHandle.USER_SYSTEM); 322 cr.registerContentObserver(Settings.Global.getUriFor( 323 Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED), 324 false, mSettingsObserver, UserHandle.USER_SYSTEM); 325 cr.registerContentObserver(Settings.Global.getUriFor( 326 Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL), 327 false, mSettingsObserver, UserHandle.USER_SYSTEM); 328 329 330 synchronized (mLock) { 331 final boolean lowPowerModeEnabledSticky = getGlobalSetting( 332 Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0; 333 334 if (lowPowerModeEnabledSticky) { 335 mState = STATE_PENDING_STICKY_ON; 336 } 337 338 mBootCompleted = true; 339 340 refreshSettingsLocked(); 341 342 doAutoBatterySaverLocked(); 343 } 344 }); 345 } 346 347 /** 348 * Run a {@link Runnable} on a background handler. 349 */ 350 @VisibleForTesting runOnBgThread(Runnable r)351 void runOnBgThread(Runnable r) { 352 BackgroundThread.getHandler().post(r); 353 } 354 355 /** 356 * Run a {@link Runnable} on a background handler, but lazily. If the same {@link Runnable} is 357 * already registered, it'll be first removed before being re-posted. 358 */ 359 @VisibleForTesting runOnBgThreadLazy(Runnable r, int delayMillis)360 void runOnBgThreadLazy(Runnable r, int delayMillis) { 361 final Handler h = BackgroundThread.getHandler(); 362 h.removeCallbacks(r); 363 h.postDelayed(r, delayMillis); 364 } 365 366 @GuardedBy("mLock") refreshSettingsLocked()367 private void refreshSettingsLocked() { 368 final boolean lowPowerModeEnabled = getGlobalSetting( 369 Settings.Global.LOW_POWER_MODE, 0) != 0; 370 final boolean lowPowerModeEnabledSticky = getGlobalSetting( 371 Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0; 372 final boolean dynamicPowerSavingsBatterySaver = getGlobalSetting( 373 Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0) != 0; 374 final int lowPowerModeTriggerLevel = getGlobalSetting( 375 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); 376 final int automaticBatterySaverMode = getGlobalSetting( 377 Settings.Global.AUTOMATIC_POWER_SAVE_MODE, 378 PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE); 379 final int dynamicPowerSavingsDisableThreshold = getGlobalSetting( 380 Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 381 mDynamicPowerSavingsDefaultDisableThreshold); 382 final boolean isStickyAutoDisableEnabled = getGlobalSetting( 383 Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1) != 0; 384 final int stickyAutoDisableThreshold = getGlobalSetting( 385 Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90); 386 387 setSettingsLocked(lowPowerModeEnabled, lowPowerModeEnabledSticky, 388 lowPowerModeTriggerLevel, 389 isStickyAutoDisableEnabled, stickyAutoDisableThreshold, 390 automaticBatterySaverMode, 391 dynamicPowerSavingsBatterySaver, dynamicPowerSavingsDisableThreshold); 392 } 393 394 /** 395 * {@link com.android.server.power.PowerManagerService} calls it when relevant global settings 396 * have changed. 397 * 398 * Note this will be called before {@link #onBootCompleted} too. 399 */ 400 @GuardedBy("mLock") 401 @VisibleForTesting setSettingsLocked(boolean batterySaverEnabled, boolean batterySaverEnabledSticky, int batterySaverTriggerThreshold, boolean isStickyAutoDisableEnabled, int stickyAutoDisableThreshold, int automaticBatterySaver, boolean dynamicPowerSavingsBatterySaver, int dynamicPowerSavingsDisableThreshold)402 void setSettingsLocked(boolean batterySaverEnabled, boolean batterySaverEnabledSticky, 403 int batterySaverTriggerThreshold, 404 boolean isStickyAutoDisableEnabled, int stickyAutoDisableThreshold, 405 int automaticBatterySaver, 406 boolean dynamicPowerSavingsBatterySaver, int dynamicPowerSavingsDisableThreshold) { 407 if (DEBUG) { 408 Slog.d(TAG, "setSettings: enabled=" + batterySaverEnabled 409 + " sticky=" + batterySaverEnabledSticky 410 + " threshold=" + batterySaverTriggerThreshold 411 + " stickyAutoDisableEnabled=" + isStickyAutoDisableEnabled 412 + " stickyAutoDisableThreshold=" + stickyAutoDisableThreshold 413 + " automaticBatterySaver=" + automaticBatterySaver 414 + " dynamicPowerSavingsBatterySaver=" + dynamicPowerSavingsBatterySaver 415 + " dynamicPowerSavingsDisableThreshold=" 416 + dynamicPowerSavingsDisableThreshold); 417 } 418 419 mSettingsLoaded = true; 420 421 // Set sensible limits. 422 stickyAutoDisableThreshold = Math.max(stickyAutoDisableThreshold, 423 batterySaverTriggerThreshold); 424 425 final boolean enabledChanged = mSettingBatterySaverEnabled != batterySaverEnabled; 426 final boolean stickyChanged = 427 mSettingBatterySaverEnabledSticky != batterySaverEnabledSticky; 428 final boolean thresholdChanged 429 = mSettingBatterySaverTriggerThreshold != batterySaverTriggerThreshold; 430 final boolean stickyAutoDisableEnabledChanged = 431 mSettingBatterySaverStickyAutoDisableEnabled != isStickyAutoDisableEnabled; 432 final boolean stickyAutoDisableThresholdChanged = 433 mSettingBatterySaverStickyAutoDisableThreshold != stickyAutoDisableThreshold; 434 final boolean automaticModeChanged = mSettingAutomaticBatterySaver != automaticBatterySaver; 435 final boolean dynamicPowerSavingsThresholdChanged = 436 mDynamicPowerSavingsDisableThreshold != dynamicPowerSavingsDisableThreshold; 437 final boolean dynamicPowerSavingsBatterySaverChanged = 438 mDynamicPowerSavingsEnableBatterySaver != dynamicPowerSavingsBatterySaver; 439 440 if (!(enabledChanged || stickyChanged || thresholdChanged || automaticModeChanged 441 || stickyAutoDisableEnabledChanged || stickyAutoDisableThresholdChanged 442 || dynamicPowerSavingsThresholdChanged || dynamicPowerSavingsBatterySaverChanged)) { 443 return; 444 } 445 446 mSettingBatterySaverEnabled = batterySaverEnabled; 447 mSettingBatterySaverEnabledSticky = batterySaverEnabledSticky; 448 mSettingBatterySaverTriggerThreshold = batterySaverTriggerThreshold; 449 mSettingBatterySaverStickyAutoDisableEnabled = isStickyAutoDisableEnabled; 450 mSettingBatterySaverStickyAutoDisableThreshold = stickyAutoDisableThreshold; 451 mSettingAutomaticBatterySaver = automaticBatterySaver; 452 mDynamicPowerSavingsDisableThreshold = dynamicPowerSavingsDisableThreshold; 453 mDynamicPowerSavingsEnableBatterySaver = dynamicPowerSavingsBatterySaver; 454 455 if (thresholdChanged) { 456 // To avoid spamming the event log, we throttle logging here. 457 runOnBgThreadLazy(mThresholdChangeLogger, 2000); 458 } 459 460 if (!mSettingBatterySaverStickyAutoDisableEnabled) { 461 hideStickyDisabledNotification(); 462 } 463 464 if (enabledChanged) { 465 final String reason = batterySaverEnabled 466 ? "Global.low_power changed to 1" : "Global.low_power changed to 0"; 467 enableBatterySaverLocked(/*enable=*/ batterySaverEnabled, /*manual=*/ true, 468 BatterySaverController.REASON_SETTING_CHANGED, reason); 469 } else { 470 doAutoBatterySaverLocked(); 471 } 472 } 473 474 private final Runnable mThresholdChangeLogger = () -> { 475 EventLogTags.writeBatterySaverSetting(mSettingBatterySaverTriggerThreshold); 476 }; 477 478 /** 479 * {@link com.android.server.power.PowerManagerService} calls it when battery state changes. 480 * 481 * Note this may be called before {@link #onBootCompleted} too. 482 */ setBatteryStatus(boolean newPowered, int newLevel, boolean newBatteryLevelLow)483 public void setBatteryStatus(boolean newPowered, int newLevel, boolean newBatteryLevelLow) { 484 if (DEBUG) { 485 Slog.d(TAG, "setBatteryStatus: powered=" + newPowered + " level=" + newLevel 486 + " low=" + newBatteryLevelLow); 487 } 488 synchronized (mLock) { 489 mBatteryStatusSet = true; 490 491 final boolean poweredChanged = mIsPowered != newPowered; 492 final boolean levelChanged = mBatteryLevel != newLevel; 493 final boolean lowChanged = mIsBatteryLevelLow != newBatteryLevelLow; 494 495 if (!(poweredChanged || levelChanged || lowChanged)) { 496 return; 497 } 498 499 mIsPowered = newPowered; 500 mBatteryLevel = newLevel; 501 mIsBatteryLevelLow = newBatteryLevelLow; 502 503 doAutoBatterySaverLocked(); 504 } 505 } 506 507 /** 508 * Change the full battery saver policy. 509 */ getFullBatterySaverPolicy()510 public BatterySaverPolicyConfig getFullBatterySaverPolicy() { 511 if (DEBUG) { 512 Slog.d(TAG, "getFullBatterySaverPolicy"); 513 } 514 515 synchronized (mLock) { 516 return mBatterySaverController.getPolicyLocked(BatterySaverPolicy.POLICY_LEVEL_FULL); 517 } 518 } 519 520 /** 521 * Change the full battery saver policy. 522 */ setFullBatterySaverPolicy(BatterySaverPolicyConfig config)523 public boolean setFullBatterySaverPolicy(BatterySaverPolicyConfig config) { 524 if (DEBUG) { 525 Slog.d(TAG, "setFullBatterySaverPolicy: config=" + config); 526 } 527 528 synchronized (mLock) { 529 return mBatterySaverController.setFullPolicyLocked(config, 530 BatterySaverController.REASON_FULL_POWER_SAVINGS_CHANGED); 531 } 532 } 533 534 /** 535 * Enable or disable the current adaptive battery saver policy. This may not change what's in 536 * effect if full battery saver is also enabled. 537 */ setAdaptiveBatterySaverEnabled(boolean enabled)538 public boolean setAdaptiveBatterySaverEnabled(boolean enabled) { 539 if (DEBUG) { 540 Slog.d(TAG, "setAdaptiveBatterySaverEnabled: enabled=" + enabled); 541 } 542 synchronized (mLock) { 543 mLastAdaptiveBatterySaverChangedExternallyElapsed = SystemClock.elapsedRealtime(); 544 return mBatterySaverController.setAdaptivePolicyEnabledLocked( 545 enabled, BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED); 546 } 547 } 548 549 /** 550 * Change the adaptive battery saver policy. 551 */ setAdaptiveBatterySaverPolicy(BatterySaverPolicyConfig config)552 public boolean setAdaptiveBatterySaverPolicy(BatterySaverPolicyConfig config) { 553 if (DEBUG) { 554 Slog.d(TAG, "setAdaptiveBatterySaverPolicy: config=" + config); 555 } 556 557 synchronized (mLock) { 558 mLastAdaptiveBatterySaverChangedExternallyElapsed = SystemClock.elapsedRealtime(); 559 return mBatterySaverController.setAdaptivePolicyLocked(config, 560 BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED); 561 } 562 } 563 564 /** 565 * Decide whether to auto-start / stop battery saver. 566 */ 567 @GuardedBy("mLock") doAutoBatterySaverLocked()568 private void doAutoBatterySaverLocked() { 569 if (DEBUG) { 570 Slog.d(TAG, "doAutoBatterySaverLocked: mBootCompleted=" + mBootCompleted 571 + " mSettingsLoaded=" + mSettingsLoaded 572 + " mBatteryStatusSet=" + mBatteryStatusSet 573 + " mState=" + mState 574 + " mIsBatteryLevelLow=" + mIsBatteryLevelLow 575 + " mIsPowered=" + mIsPowered 576 + " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver 577 + " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky 578 + " mSettingBatterySaverStickyAutoDisableEnabled=" 579 + mSettingBatterySaverStickyAutoDisableEnabled); 580 } 581 if (!(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) { 582 return; // Not fully initialized yet. 583 } 584 585 updateStateLocked(false, false); 586 587 // Adaptive control. 588 if (SystemClock.elapsedRealtime() - mLastAdaptiveBatterySaverChangedExternallyElapsed 589 > ADAPTIVE_CHANGE_TIMEOUT_MS) { 590 mBatterySaverController.setAdaptivePolicyEnabledLocked( 591 false, BatterySaverController.REASON_TIMEOUT); 592 mBatterySaverController.resetAdaptivePolicyLocked( 593 BatterySaverController.REASON_TIMEOUT); 594 } else if (mIsPowered && mBatteryLevel >= ADAPTIVE_AUTO_DISABLE_BATTERY_LEVEL) { 595 mBatterySaverController.setAdaptivePolicyEnabledLocked(false, 596 BatterySaverController.REASON_PLUGGED_IN); 597 } 598 } 599 600 /** 601 * Update the state machine based on the current settings and battery/charge status. 602 * 603 * @param manual Whether the change was made by the user. 604 * @param enable Whether the user wants to turn battery saver on or off. Is only used if {@param 605 * manual} is true. 606 */ 607 @GuardedBy("mLock") updateStateLocked(boolean manual, boolean enable)608 private void updateStateLocked(boolean manual, boolean enable) { 609 if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) { 610 return; // Not fully initialized yet. 611 } 612 613 switch (mState) { 614 case STATE_OFF: { 615 if (!mIsPowered) { 616 if (manual) { 617 if (!enable) { 618 Slog.e(TAG, "Tried to disable BS when it's already OFF"); 619 return; 620 } 621 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, 622 BatterySaverController.REASON_MANUAL_ON); 623 hideStickyDisabledNotification(); 624 mState = STATE_MANUAL_ON; 625 } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) { 626 enableBatterySaverLocked(/*enable*/ true, /*manual*/ false, 627 BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON); 628 hideStickyDisabledNotification(); 629 mState = STATE_AUTOMATIC_ON; 630 } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) { 631 enableBatterySaverLocked(/*enable*/ true, /*manual*/ false, 632 BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON); 633 hideStickyDisabledNotification(); 634 mState = STATE_AUTOMATIC_ON; 635 } 636 } 637 break; 638 } 639 640 case STATE_MANUAL_ON: { 641 if (manual) { 642 if (enable) { 643 Slog.e(TAG, "Tried to enable BS when it's already MANUAL_ON"); 644 return; 645 } 646 enableBatterySaverLocked(/*enable*/ false, /*manual*/ true, 647 BatterySaverController.REASON_MANUAL_OFF); 648 mState = STATE_OFF; 649 } else if (mIsPowered) { 650 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, 651 BatterySaverController.REASON_PLUGGED_IN); 652 if (mSettingBatterySaverEnabledSticky 653 && !mBatterySaverStickyBehaviourDisabled) { 654 mState = STATE_PENDING_STICKY_ON; 655 } else { 656 mState = STATE_OFF; 657 } 658 } 659 break; 660 } 661 662 case STATE_AUTOMATIC_ON: { 663 if (mIsPowered) { 664 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, 665 BatterySaverController.REASON_PLUGGED_IN); 666 mState = STATE_OFF; 667 } else if (manual) { 668 if (enable) { 669 Slog.e(TAG, "Tried to enable BS when it's already AUTO_ON"); 670 return; 671 } 672 enableBatterySaverLocked(/*enable*/ false, /*manual*/ true, 673 BatterySaverController.REASON_MANUAL_OFF); 674 // When battery saver is disabled manually (while battery saver is enabled) 675 // when the battery level is low, we "snooze" BS -- i.e. disable auto battery 676 // saver. 677 // We resume auto-BS once the battery level is not low, or the device is 678 // plugged in. 679 mState = STATE_OFF_AUTOMATIC_SNOOZED; 680 } else if (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked()) { 681 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, 682 BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF); 683 mState = STATE_OFF; 684 } else if (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked()) { 685 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, 686 BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF); 687 mState = STATE_OFF; 688 } else if (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked()) { 689 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, 690 BatterySaverController.REASON_SETTING_CHANGED); 691 mState = STATE_OFF; 692 } 693 break; 694 } 695 696 case STATE_OFF_AUTOMATIC_SNOOZED: { 697 if (manual) { 698 if (!enable) { 699 Slog.e(TAG, "Tried to disable BS when it's already AUTO_SNOOZED"); 700 return; 701 } 702 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, 703 BatterySaverController.REASON_MANUAL_ON); 704 mState = STATE_MANUAL_ON; 705 } else if (mIsPowered // Plugging in resets snooze. 706 || (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked()) 707 || (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked()) 708 || (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked())) { 709 mState = STATE_OFF; 710 } 711 break; 712 } 713 714 case STATE_PENDING_STICKY_ON: { 715 if (manual) { 716 // This shouldn't be possible. We'll only be in this state when the device is 717 // plugged in, so the user shouldn't be able to manually change state. 718 Slog.e(TAG, "Tried to manually change BS state from PENDING_STICKY_ON"); 719 return; 720 } 721 final boolean shouldTurnOffSticky = mSettingBatterySaverStickyAutoDisableEnabled 722 && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold; 723 final boolean isStickyDisabled = 724 mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky; 725 if (isStickyDisabled || shouldTurnOffSticky) { 726 mState = STATE_OFF; 727 setStickyActive(false); 728 triggerStickyDisabledNotification(); 729 } else if (!mIsPowered) { 730 // Re-enable BS. 731 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, 732 BatterySaverController.REASON_STICKY_RESTORE); 733 mState = STATE_MANUAL_ON; 734 } 735 break; 736 } 737 738 default: 739 Slog.wtf(TAG, "Unknown state: " + mState); 740 break; 741 } 742 } 743 744 @VisibleForTesting getState()745 int getState() { 746 synchronized (mLock) { 747 return mState; 748 } 749 } 750 751 /** 752 * {@link com.android.server.power.PowerManagerService} calls it when 753 * {@link android.os.PowerManager#setPowerSaveModeEnabled} is called. 754 * 755 * Note this could? be called before {@link #onBootCompleted} too. 756 */ setBatterySaverEnabledManually(boolean enabled)757 public void setBatterySaverEnabledManually(boolean enabled) { 758 if (DEBUG) { 759 Slog.d(TAG, "setBatterySaverEnabledManually: enabled=" + enabled); 760 } 761 synchronized (mLock) { 762 updateStateLocked(true, enabled); 763 // TODO: maybe turn off adaptive if it's on and advertiseIsEnabled is true and 764 // enabled is false 765 } 766 } 767 768 @GuardedBy("mLock") enableBatterySaverLocked(boolean enable, boolean manual, int intReason)769 private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason) { 770 enableBatterySaverLocked(enable, manual, intReason, reasonToString(intReason)); 771 } 772 773 /** 774 * Actually enable / disable battery saver. Write the new state to the global settings 775 * and propagate it to {@link #mBatterySaverController}. 776 */ 777 @GuardedBy("mLock") enableBatterySaverLocked(boolean enable, boolean manual, int intReason, String strReason)778 private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason, 779 String strReason) { 780 if (DEBUG) { 781 Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual 782 + " reason=" + strReason + "(" + intReason + ")"); 783 } 784 final boolean wasEnabled = mBatterySaverController.isFullEnabled(); 785 786 if (wasEnabled == enable) { 787 if (DEBUG) { 788 Slog.d(TAG, "Already " + (enable ? "enabled" : "disabled")); 789 } 790 return; 791 } 792 if (enable && mIsPowered) { 793 if (DEBUG) Slog.d(TAG, "Can't enable: isPowered"); 794 return; 795 } 796 mLastChangedIntReason = intReason; 797 mLastChangedStrReason = strReason; 798 799 mSettingBatterySaverEnabled = enable; 800 putGlobalSetting(Settings.Global.LOW_POWER_MODE, enable ? 1 : 0); 801 802 if (manual) { 803 setStickyActive(!mBatterySaverStickyBehaviourDisabled && enable); 804 } 805 mBatterySaverController.enableBatterySaver(enable, intReason); 806 807 // Handle triggering the notification to show/hide when appropriate 808 if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON 809 || intReason == BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON) { 810 triggerDynamicModeNotification(); 811 } else if (!enable) { 812 hideDynamicModeNotification(); 813 } 814 815 if (DEBUG) { 816 Slog.d(TAG, "Battery saver: Enabled=" + enable 817 + " manual=" + manual 818 + " reason=" + strReason + "(" + intReason + ")"); 819 } 820 } 821 822 @VisibleForTesting triggerDynamicModeNotification()823 void triggerDynamicModeNotification() { 824 // The current lock is the PowerManager lock, which sits very low in the service lock 825 // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock. 826 runOnBgThread(() -> { 827 NotificationManager manager = mContext.getSystemService(NotificationManager.class); 828 ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID, 829 R.string.dynamic_mode_notification_channel_name); 830 831 manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID, 832 buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID, 833 R.string.dynamic_mode_notification_title, 834 R.string.dynamic_mode_notification_summary, 835 Settings.ACTION_BATTERY_SAVER_SETTINGS, 0L), 836 UserHandle.ALL); 837 }); 838 } 839 840 @VisibleForTesting triggerStickyDisabledNotification()841 void triggerStickyDisabledNotification() { 842 // The current lock is the PowerManager lock, which sits very low in the service lock 843 // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock. 844 runOnBgThread(() -> { 845 NotificationManager manager = mContext.getSystemService(NotificationManager.class); 846 ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID, 847 R.string.battery_saver_notification_channel_name); 848 849 manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID, 850 buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID, 851 R.string.battery_saver_off_notification_title, 852 R.string.battery_saver_charged_notification_summary, 853 Settings.ACTION_BATTERY_SAVER_SETTINGS, 854 STICKY_DISABLED_NOTIFY_TIMEOUT_MS), 855 UserHandle.ALL); 856 }); 857 } 858 ensureNotificationChannelExists(NotificationManager manager, @NonNull String channelId, @StringRes int nameId)859 private void ensureNotificationChannelExists(NotificationManager manager, 860 @NonNull String channelId, @StringRes int nameId) { 861 NotificationChannel channel = new NotificationChannel( 862 channelId, mContext.getText(nameId), NotificationManager.IMPORTANCE_DEFAULT); 863 channel.setSound(null, null); 864 channel.setBlockable(true); 865 manager.createNotificationChannel(channel); 866 } 867 buildNotification(@onNull String channelId, @StringRes int titleId, @StringRes int summaryId, @NonNull String intentAction, long timeoutMs)868 private Notification buildNotification(@NonNull String channelId, @StringRes int titleId, 869 @StringRes int summaryId, @NonNull String intentAction, long timeoutMs) { 870 Resources res = mContext.getResources(); 871 Intent intent = new Intent(intentAction); 872 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 873 PendingIntent batterySaverIntent = PendingIntent.getActivity( 874 mContext, 0 /* requestCode */, intent, 875 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); 876 final String title = res.getString(titleId); 877 final String summary = res.getString(summaryId); 878 879 return new Notification.Builder(mContext, channelId) 880 .setSmallIcon(R.drawable.ic_battery) 881 .setContentTitle(title) 882 .setContentText(summary) 883 .setContentIntent(batterySaverIntent) 884 .setStyle(new Notification.BigTextStyle().bigText(summary)) 885 .setOnlyAlertOnce(true) 886 .setAutoCancel(true) 887 .setTimeoutAfter(timeoutMs) 888 .build(); 889 } 890 hideDynamicModeNotification()891 private void hideDynamicModeNotification() { 892 hideNotification(DYNAMIC_MODE_NOTIFICATION_ID); 893 } 894 hideStickyDisabledNotification()895 private void hideStickyDisabledNotification() { 896 hideNotification(STICKY_AUTO_DISABLED_NOTIFICATION_ID); 897 } 898 hideNotification(int notificationId)899 private void hideNotification(int notificationId) { 900 // The current lock is the PowerManager lock, which sits very low in the service lock 901 // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock. 902 runOnBgThread(() -> { 903 NotificationManager manager = mContext.getSystemService(NotificationManager.class); 904 manager.cancelAsUser(TAG, notificationId, UserHandle.ALL); 905 }); 906 } 907 setStickyActive(boolean active)908 private void setStickyActive(boolean active) { 909 mSettingBatterySaverEnabledSticky = active; 910 putGlobalSetting(Settings.Global.LOW_POWER_MODE_STICKY, 911 mSettingBatterySaverEnabledSticky ? 1 : 0); 912 } 913 914 @VisibleForTesting putGlobalSetting(String key, int value)915 protected void putGlobalSetting(String key, int value) { 916 Settings.Global.putInt(mContext.getContentResolver(), key, value); 917 } 918 919 @VisibleForTesting getGlobalSetting(String key, int defValue)920 protected int getGlobalSetting(String key, int defValue) { 921 return Settings.Global.getInt(mContext.getContentResolver(), key, defValue); 922 } 923 dump(PrintWriter pw)924 public void dump(PrintWriter pw) { 925 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 926 927 ipw.println(); 928 ipw.println("Battery saver state machine:"); 929 ipw.increaseIndent(); 930 synchronized (mLock) { 931 ipw.print("Enabled="); 932 ipw.println(mBatterySaverController.isEnabled()); 933 ipw.increaseIndent(); 934 ipw.print("full="); 935 ipw.println(mBatterySaverController.isFullEnabled()); 936 ipw.print("adaptive="); 937 ipw.print(mBatterySaverController.isAdaptiveEnabled()); 938 if (mBatterySaverController.isAdaptiveEnabled()) { 939 ipw.print(" (advertise="); 940 ipw.print( 941 mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled()); 942 ipw.print(")"); 943 } 944 ipw.decreaseIndent(); 945 ipw.println(); 946 ipw.print("mState="); 947 ipw.println(mState); 948 949 ipw.print("mLastChangedIntReason="); 950 ipw.println(mLastChangedIntReason); 951 ipw.print("mLastChangedStrReason="); 952 ipw.println(mLastChangedStrReason); 953 954 ipw.print("mBootCompleted="); 955 ipw.println(mBootCompleted); 956 ipw.print("mSettingsLoaded="); 957 ipw.println(mSettingsLoaded); 958 ipw.print("mBatteryStatusSet="); 959 ipw.println(mBatteryStatusSet); 960 961 ipw.print("mIsPowered="); 962 ipw.println(mIsPowered); 963 ipw.print("mBatteryLevel="); 964 ipw.println(mBatteryLevel); 965 ipw.print("mIsBatteryLevelLow="); 966 ipw.println(mIsBatteryLevelLow); 967 968 ipw.print("mSettingAutomaticBatterySaver="); 969 ipw.println(mSettingAutomaticBatterySaver); 970 ipw.print("mSettingBatterySaverEnabled="); 971 ipw.println(mSettingBatterySaverEnabled); 972 ipw.print("mSettingBatterySaverEnabledSticky="); 973 ipw.println(mSettingBatterySaverEnabledSticky); 974 ipw.print("mSettingBatterySaverStickyAutoDisableEnabled="); 975 ipw.println(mSettingBatterySaverStickyAutoDisableEnabled); 976 ipw.print("mSettingBatterySaverStickyAutoDisableThreshold="); 977 ipw.println(mSettingBatterySaverStickyAutoDisableThreshold); 978 ipw.print("mSettingBatterySaverTriggerThreshold="); 979 ipw.println(mSettingBatterySaverTriggerThreshold); 980 ipw.print("mBatterySaverStickyBehaviourDisabled="); 981 ipw.println(mBatterySaverStickyBehaviourDisabled); 982 983 ipw.print("mDynamicPowerSavingsDefaultDisableThreshold="); 984 ipw.println(mDynamicPowerSavingsDefaultDisableThreshold); 985 ipw.print("mDynamicPowerSavingsDisableThreshold="); 986 ipw.println(mDynamicPowerSavingsDisableThreshold); 987 ipw.print("mDynamicPowerSavingsEnableBatterySaver="); 988 ipw.println(mDynamicPowerSavingsEnableBatterySaver); 989 990 ipw.print("mLastAdaptiveBatterySaverChangedExternallyElapsed="); 991 ipw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed); 992 } 993 ipw.decreaseIndent(); 994 } 995 dumpProto(ProtoOutputStream proto, long tag)996 public void dumpProto(ProtoOutputStream proto, long tag) { 997 synchronized (mLock) { 998 final long token = proto.start(tag); 999 1000 proto.write(BatterySaverStateMachineProto.ENABLED, 1001 mBatterySaverController.isEnabled()); 1002 proto.write(BatterySaverStateMachineProto.STATE, mState); 1003 proto.write(BatterySaverStateMachineProto.IS_FULL_ENABLED, 1004 mBatterySaverController.isFullEnabled()); 1005 proto.write(BatterySaverStateMachineProto.IS_ADAPTIVE_ENABLED, 1006 mBatterySaverController.isAdaptiveEnabled()); 1007 proto.write(BatterySaverStateMachineProto.SHOULD_ADVERTISE_IS_ENABLED, 1008 mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled()); 1009 1010 proto.write(BatterySaverStateMachineProto.BOOT_COMPLETED, mBootCompleted); 1011 proto.write(BatterySaverStateMachineProto.SETTINGS_LOADED, mSettingsLoaded); 1012 proto.write(BatterySaverStateMachineProto.BATTERY_STATUS_SET, mBatteryStatusSet); 1013 1014 1015 proto.write(BatterySaverStateMachineProto.IS_POWERED, mIsPowered); 1016 proto.write(BatterySaverStateMachineProto.BATTERY_LEVEL, mBatteryLevel); 1017 proto.write(BatterySaverStateMachineProto.IS_BATTERY_LEVEL_LOW, mIsBatteryLevelLow); 1018 1019 proto.write(BatterySaverStateMachineProto.SETTING_AUTOMATIC_TRIGGER, 1020 mSettingAutomaticBatterySaver); 1021 proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_ENABLED, 1022 mSettingBatterySaverEnabled); 1023 proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_ENABLED_STICKY, 1024 mSettingBatterySaverEnabledSticky); 1025 proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_TRIGGER_THRESHOLD, 1026 mSettingBatterySaverTriggerThreshold); 1027 proto.write( 1028 BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_STICKY_AUTO_DISABLE_ENABLED, 1029 mSettingBatterySaverStickyAutoDisableEnabled); 1030 proto.write( 1031 BatterySaverStateMachineProto 1032 .SETTING_BATTERY_SAVER_STICKY_AUTO_DISABLE_THRESHOLD, 1033 mSettingBatterySaverStickyAutoDisableThreshold); 1034 1035 proto.write( 1036 BatterySaverStateMachineProto.DEFAULT_DYNAMIC_DISABLE_THRESHOLD, 1037 mDynamicPowerSavingsDefaultDisableThreshold); 1038 proto.write( 1039 BatterySaverStateMachineProto.DYNAMIC_DISABLE_THRESHOLD, 1040 mDynamicPowerSavingsDisableThreshold); 1041 proto.write( 1042 BatterySaverStateMachineProto.DYNAMIC_BATTERY_SAVER_ENABLED, 1043 mDynamicPowerSavingsEnableBatterySaver); 1044 1045 proto.write( 1046 BatterySaverStateMachineProto 1047 .LAST_ADAPTIVE_BATTERY_SAVER_CHANGED_EXTERNALLY_ELAPSED, 1048 mLastAdaptiveBatterySaverChangedExternallyElapsed); 1049 1050 proto.end(token); 1051 } 1052 } 1053 } 1054