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