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