blob: ff9129956a3b4ffe8bc1c1393478657d909ca3f7 [file] [log] [blame]
Makoto Onukia3cd7b92018-03-19 14:47:05 -07001/*
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 */
16package com.android.server.power.batterysaver;
17
Kweku Adamsf57f99e2019-03-07 15:38:21 -080018import static com.android.server.power.batterysaver.BatterySaverController.reasonToString;
19
Kweku Adams476ca772019-03-06 16:10:25 -080020import android.annotation.NonNull;
21import android.annotation.StringRes;
Salvador Martinez3a3f8222018-12-14 15:29:30 -080022import android.app.Notification;
23import android.app.NotificationChannel;
24import android.app.NotificationManager;
25import android.app.PendingIntent;
Makoto Onukia3cd7b92018-03-19 14:47:05 -070026import android.content.ContentResolver;
27import android.content.Context;
Salvador Martinez3a3f8222018-12-14 15:29:30 -080028import android.content.Intent;
29import android.content.res.Resources;
Makoto Onukia3cd7b92018-03-19 14:47:05 -070030import android.database.ContentObserver;
Kweku Adams9f488e22019-01-14 16:25:08 -080031import android.os.BatterySaverPolicyConfig;
Makoto Onuki0f8617a2018-04-30 10:26:21 -070032import android.os.Handler;
Salvador Martinez812ea752018-10-19 13:03:20 -070033import android.os.PowerManager;
Kweku Adams9f488e22019-01-14 16:25:08 -080034import android.os.SystemClock;
Makoto Onukia3cd7b92018-03-19 14:47:05 -070035import android.os.UserHandle;
36import android.provider.Settings;
Makoto Onukia3cd7b92018-03-19 14:47:05 -070037import android.util.Slog;
38import android.util.proto.ProtoOutputStream;
39
Salvador Martinez3a3f8222018-12-14 15:29:30 -080040import com.android.internal.R;
Makoto Onukia3cd7b92018-03-19 14:47:05 -070041import com.android.internal.annotations.GuardedBy;
42import com.android.internal.annotations.VisibleForTesting;
Makoto Onuki6e5eb1d2018-03-30 16:15:35 -070043import com.android.internal.os.BackgroundThread;
Makoto Onuki0f8617a2018-04-30 10:26:21 -070044import com.android.server.EventLogTags;
Makoto Onukia3cd7b92018-03-19 14:47:05 -070045import com.android.server.power.BatterySaverStateMachineProto;
46
47import java.io.PrintWriter;
Kweku Adams3c95ee12019-03-21 13:06:53 -070048import java.text.NumberFormat;
Makoto Onukia3cd7b92018-03-19 14:47:05 -070049
50/**
51 * Decides when to enable / disable battery saver.
52 *
Makoto Onukibd7a6252018-05-10 13:41:39 -070053 * 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.)
Makoto Onukia3cd7b92018-03-19 14:47:05 -070055 *
Kweku Adams9f488e22019-01-14 16:25:08 -080056 * Test: atest com.android.server.power.batterysaver.BatterySaverStateMachineTest
Kweku Adamsf57f99e2019-03-07 15:38:21 -080057 *
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 }
Makoto Onukia3cd7b92018-03-19 14:47:05 -070093 */
94public class BatterySaverStateMachine {
95 private static final String TAG = "BatterySaverStateMachine";
Salvador Martinez3a3f8222018-12-14 15:29:30 -080096 private static final String DYNAMIC_MODE_NOTIF_CHANNEL_ID = "dynamic_mode_notification";
Kweku Adams476ca772019-03-06 16:10:25 -080097 private static final String BATTERY_SAVER_NOTIF_CHANNEL_ID = "battery_saver_channel";
Salvador Martinez3a3f8222018-12-14 15:29:30 -080098 private static final int DYNAMIC_MODE_NOTIFICATION_ID = 1992;
Kweku Adams476ca772019-03-06 16:10:25 -080099 private static final int STICKY_AUTO_DISABLED_NOTIFICATION_ID = 1993;
Makoto Onukibd7a6252018-05-10 13:41:39 -0700100 private final Object mLock;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700101
102 private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
103
Kweku Adams9f488e22019-01-14 16:25:08 -0800104 private static final long ADAPTIVE_CHANGE_TIMEOUT_MS = 24 * 60 * 60 * 1000L;
105
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800106 /** 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
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700125 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
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800140 @GuardedBy("mLock")
141 private int mState;
142
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700143 /** 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
Michael Kwane3225022018-06-26 18:03:38 -0700151 /** Whether the battery level is considered to be "low" or not. */
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700152 @GuardedBy("mLock")
153 private boolean mIsBatteryLevelLow;
154
Kweku Adamsb243a992019-01-18 11:18:16 -0800155 /** Previously known value of Settings.Global.LOW_POWER_MODE. */
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700156 @GuardedBy("mLock")
157 private boolean mSettingBatterySaverEnabled;
158
Kweku Adamsb243a992019-01-18 11:18:16 -0800159 /** Previously known value of Settings.Global.LOW_POWER_MODE_STICKY. */
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700160 @GuardedBy("mLock")
161 private boolean mSettingBatterySaverEnabledSticky;
162
Michael Kwane3225022018-06-26 18:03:38 -0700163 /** Config flag to track if battery saver's sticky behaviour is disabled. */
164 private final boolean mBatterySaverStickyBehaviourDisabled;
165
Kweku Adamsb243a992019-01-18 11:18:16 -0800166 /**
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
Salvador Martinez04b98332018-10-02 11:28:39 -0700180 /** Config flag to track default disable threshold for Dynamic Power Savings enabled battery
181 * saver. */
182 @GuardedBy("mLock")
183 private final int mDynamicPowerSavingsDefaultDisableThreshold;
184
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700185 /**
Kweku Adamsb243a992019-01-18 11:18:16 -0800186 * Previously known value of Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL.
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700187 * (Currently only used in dumpsys.)
188 */
189 @GuardedBy("mLock")
190 private int mSettingBatterySaverTriggerThreshold;
191
Salvador Martinezb85a9f82019-03-20 16:21:27 -0700192 /** Previously known value of Settings.Global.AUTOMATIC_POWER_SAVE_MODE. */
Salvador Martinez04b98332018-10-02 11:28:39 -0700193 @GuardedBy("mLock")
194 private int mSettingAutomaticBatterySaver;
195
196 /** When to disable battery saver again if it was enabled due to an external suggestion.
Kweku Adamsb243a992019-01-18 11:18:16 -0800197 * Corresponds to Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD.
Salvador Martinez04b98332018-10-02 11:28:39 -0700198 */
199 @GuardedBy("mLock")
200 private int mDynamicPowerSavingsDisableThreshold;
201
202 /**
203 * Whether we've received a suggestion that battery saver should be on from an external app.
Kweku Adamsb243a992019-01-18 11:18:16 -0800204 * Updates when Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED changes.
Salvador Martinez04b98332018-10-02 11:28:39 -0700205 */
206 @GuardedBy("mLock")
207 private boolean mDynamicPowerSavingsBatterySaver;
208
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700209 /**
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700210 * Last reason passed to {@link #enableBatterySaverLocked}.
211 */
212 @GuardedBy("mLock")
213 private int mLastChangedIntReason;
214
215 /**
216 * Last reason passed to {@link #enableBatterySaverLocked}.
217 */
218 @GuardedBy("mLock")
219 private String mLastChangedStrReason;
220
Kweku Adams9f488e22019-01-14 16:25:08 -0800221 /**
222 * The last time adaptive battery saver was changed by an external service, using elapsed
223 * realtime as the timebase.
224 */
225 @GuardedBy("mLock")
226 private long mLastAdaptiveBatterySaverChangedExternallyElapsed;
227
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700228 private final ContentObserver mSettingsObserver = new ContentObserver(null) {
229 @Override
230 public void onChange(boolean selfChange) {
231 synchronized (mLock) {
232 refreshSettingsLocked();
233 }
234 }
235 };
236
Makoto Onukibd7a6252018-05-10 13:41:39 -0700237 public BatterySaverStateMachine(Object lock,
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700238 Context context, BatterySaverController batterySaverController) {
Makoto Onukibd7a6252018-05-10 13:41:39 -0700239 mLock = lock;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700240 mContext = context;
241 mBatterySaverController = batterySaverController;
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800242 mState = STATE_OFF;
Michael Kwane3225022018-06-26 18:03:38 -0700243
244 mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean(
245 com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled);
Salvador Martinez04b98332018-10-02 11:28:39 -0700246 mDynamicPowerSavingsDefaultDisableThreshold = mContext.getResources().getInteger(
247 com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700248 }
249
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800250 /** @return true if the automatic percentage based mode should be used */
251 private boolean isAutomaticModeActiveLocked() {
Salvador Martinezb85a9f82019-03-20 16:21:27 -0700252 return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800253 && mSettingBatterySaverTriggerThreshold > 0;
254 }
255
256 /**
257 * The returned value won't necessarily make sense if {@link #isAutomaticModeActiveLocked()}
258 * returns {@code false}.
259 *
260 * @return true if the battery level is below automatic's threshold.
261 */
262 private boolean isInAutomaticLowZoneLocked() {
263 return mIsBatteryLevelLow;
264 }
265
266 /** @return true if the dynamic mode should be used */
267 private boolean isDynamicModeActiveLocked() {
Salvador Martinezb85a9f82019-03-20 16:21:27 -0700268 return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800269 && mDynamicPowerSavingsBatterySaver;
270 }
271
272 /**
273 * The returned value won't necessarily make sense if {@link #isDynamicModeActiveLocked()}
274 * returns {@code false}.
275 *
276 * @return true if the battery level is below dynamic's threshold.
277 */
278 private boolean isInDynamicLowZoneLocked() {
279 return mBatteryLevel <= mDynamicPowerSavingsDisableThreshold;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700280 }
281
282 /**
283 * {@link com.android.server.power.PowerManagerService} calls it when the system is booted.
284 */
285 public void onBootCompleted() {
286 if (DEBUG) {
287 Slog.d(TAG, "onBootCompleted");
288 }
Makoto Onuki1d146e42018-06-04 13:54:25 -0700289 // Just booted. We don't want LOW_POWER_MODE to be persisted, so just always clear it.
Kweku Adamsb243a992019-01-18 11:18:16 -0800290 putGlobalSetting(Settings.Global.LOW_POWER_MODE, 0);
Makoto Onuki1d146e42018-06-04 13:54:25 -0700291
292 // This is called with the power manager lock held. Don't do anything that may call to
293 // upper services. (e.g. don't call into AM directly)
294 // So use a BG thread.
Makoto Onuki6e5eb1d2018-03-30 16:15:35 -0700295 runOnBgThread(() -> {
Makoto Onuki1d146e42018-06-04 13:54:25 -0700296
Makoto Onukibd7a6252018-05-10 13:41:39 -0700297 final ContentResolver cr = mContext.getContentResolver();
298 cr.registerContentObserver(Settings.Global.getUriFor(
299 Settings.Global.LOW_POWER_MODE),
300 false, mSettingsObserver, UserHandle.USER_SYSTEM);
301 cr.registerContentObserver(Settings.Global.getUriFor(
302 Settings.Global.LOW_POWER_MODE_STICKY),
303 false, mSettingsObserver, UserHandle.USER_SYSTEM);
304 cr.registerContentObserver(Settings.Global.getUriFor(
305 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
306 false, mSettingsObserver, UserHandle.USER_SYSTEM);
Salvador Martinez04b98332018-10-02 11:28:39 -0700307 cr.registerContentObserver(Settings.Global.getUriFor(
Salvador Martinezb85a9f82019-03-20 16:21:27 -0700308 Settings.Global.AUTOMATIC_POWER_SAVE_MODE),
Salvador Martinez04b98332018-10-02 11:28:39 -0700309 false, mSettingsObserver, UserHandle.USER_SYSTEM);
310 cr.registerContentObserver(Settings.Global.getUriFor(
Kweku Adamsb243a992019-01-18 11:18:16 -0800311 Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED),
Salvador Martinez04b98332018-10-02 11:28:39 -0700312 false, mSettingsObserver, UserHandle.USER_SYSTEM);
313 cr.registerContentObserver(Settings.Global.getUriFor(
Kweku Adamsb243a992019-01-18 11:18:16 -0800314 Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD),
315 false, mSettingsObserver, UserHandle.USER_SYSTEM);
316 cr.registerContentObserver(Settings.Global.getUriFor(
317 Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED),
318 false, mSettingsObserver, UserHandle.USER_SYSTEM);
319 cr.registerContentObserver(Settings.Global.getUriFor(
320 Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL),
Salvador Martinez04b98332018-10-02 11:28:39 -0700321 false, mSettingsObserver, UserHandle.USER_SYSTEM);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700322
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800323
Makoto Onukibd7a6252018-05-10 13:41:39 -0700324 synchronized (mLock) {
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800325 final boolean lowPowerModeEnabledSticky = getGlobalSetting(
326 Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0;
327
328 if (lowPowerModeEnabledSticky) {
329 mState = STATE_PENDING_STICKY_ON;
330 }
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700331
Makoto Onuki6e5eb1d2018-03-30 16:15:35 -0700332 mBootCompleted = true;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700333
Makoto Onuki6e5eb1d2018-03-30 16:15:35 -0700334 refreshSettingsLocked();
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700335
Makoto Onuki6e5eb1d2018-03-30 16:15:35 -0700336 doAutoBatterySaverLocked();
337 }
338 });
339 }
340
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700341 /**
342 * Run a {@link Runnable} on a background handler.
343 */
Makoto Onuki6e5eb1d2018-03-30 16:15:35 -0700344 @VisibleForTesting
345 void runOnBgThread(Runnable r) {
346 BackgroundThread.getHandler().post(r);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700347 }
348
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700349 /**
Kweku Adams7fb72a42019-01-08 16:08:49 -0800350 * Run a {@link Runnable} on a background handler, but lazily. If the same {@link Runnable} is
351 * already registered, it'll be first removed before being re-posted.
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700352 */
353 @VisibleForTesting
354 void runOnBgThreadLazy(Runnable r, int delayMillis) {
355 final Handler h = BackgroundThread.getHandler();
356 h.removeCallbacks(r);
357 h.postDelayed(r, delayMillis);
358 }
359
Andreas Gampeedef60e2018-07-20 12:55:37 -0700360 @GuardedBy("mLock")
Kweku Adamsb243a992019-01-18 11:18:16 -0800361 private void refreshSettingsLocked() {
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700362 final boolean lowPowerModeEnabled = getGlobalSetting(
363 Settings.Global.LOW_POWER_MODE, 0) != 0;
364 final boolean lowPowerModeEnabledSticky = getGlobalSetting(
365 Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0;
Salvador Martinez04b98332018-10-02 11:28:39 -0700366 final boolean dynamicPowerSavingsBatterySaver = getGlobalSetting(
Kweku Adamsb243a992019-01-18 11:18:16 -0800367 Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0) != 0;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700368 final int lowPowerModeTriggerLevel = getGlobalSetting(
369 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
Kweku Adamsb243a992019-01-18 11:18:16 -0800370 final int automaticBatterySaverMode = getGlobalSetting(
Salvador Martinezb85a9f82019-03-20 16:21:27 -0700371 Settings.Global.AUTOMATIC_POWER_SAVE_MODE,
372 PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
Salvador Martinez04b98332018-10-02 11:28:39 -0700373 final int dynamicPowerSavingsDisableThreshold = getGlobalSetting(
Kweku Adamsb243a992019-01-18 11:18:16 -0800374 Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Salvador Martinez04b98332018-10-02 11:28:39 -0700375 mDynamicPowerSavingsDefaultDisableThreshold);
Kweku Adamsb243a992019-01-18 11:18:16 -0800376 final boolean isStickyAutoDisableEnabled = getGlobalSetting(
Kweku Adams6065cbb2019-02-25 18:33:22 -0800377 Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1) != 0;
Kweku Adamsb243a992019-01-18 11:18:16 -0800378 final int stickyAutoDisableThreshold = getGlobalSetting(
379 Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700380
381 setSettingsLocked(lowPowerModeEnabled, lowPowerModeEnabledSticky,
Kweku Adamsb243a992019-01-18 11:18:16 -0800382 lowPowerModeTriggerLevel,
383 isStickyAutoDisableEnabled, stickyAutoDisableThreshold,
384 automaticBatterySaverMode,
385 dynamicPowerSavingsBatterySaver, dynamicPowerSavingsDisableThreshold);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700386 }
387
388 /**
389 * {@link com.android.server.power.PowerManagerService} calls it when relevant global settings
390 * have changed.
391 *
392 * Note this will be called before {@link #onBootCompleted} too.
393 */
Andreas Gampeedef60e2018-07-20 12:55:37 -0700394 @GuardedBy("mLock")
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700395 @VisibleForTesting
396 void setSettingsLocked(boolean batterySaverEnabled, boolean batterySaverEnabledSticky,
Kweku Adamsb243a992019-01-18 11:18:16 -0800397 int batterySaverTriggerThreshold,
398 boolean isStickyAutoDisableEnabled, int stickyAutoDisableThreshold,
399 int automaticBatterySaver,
Salvador Martinez04b98332018-10-02 11:28:39 -0700400 boolean dynamicPowerSavingsBatterySaver, int dynamicPowerSavingsDisableThreshold) {
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700401 if (DEBUG) {
402 Slog.d(TAG, "setSettings: enabled=" + batterySaverEnabled
403 + " sticky=" + batterySaverEnabledSticky
Salvador Martinez04b98332018-10-02 11:28:39 -0700404 + " threshold=" + batterySaverTriggerThreshold
Kweku Adamsb243a992019-01-18 11:18:16 -0800405 + " stickyAutoDisableEnabled=" + isStickyAutoDisableEnabled
406 + " stickyAutoDisableThreshold=" + stickyAutoDisableThreshold
Salvador Martinez04b98332018-10-02 11:28:39 -0700407 + " automaticBatterySaver=" + automaticBatterySaver
408 + " dynamicPowerSavingsBatterySaver=" + dynamicPowerSavingsBatterySaver
409 + " dynamicPowerSavingsDisableThreshold="
410 + dynamicPowerSavingsDisableThreshold);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700411 }
412
413 mSettingsLoaded = true;
414
Kweku Adamsb243a992019-01-18 11:18:16 -0800415 // Set sensible limits.
416 stickyAutoDisableThreshold = Math.max(stickyAutoDisableThreshold,
417 batterySaverTriggerThreshold);
418
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700419 final boolean enabledChanged = mSettingBatterySaverEnabled != batterySaverEnabled;
420 final boolean stickyChanged =
421 mSettingBatterySaverEnabledSticky != batterySaverEnabledSticky;
422 final boolean thresholdChanged
423 = mSettingBatterySaverTriggerThreshold != batterySaverTriggerThreshold;
Kweku Adamsb243a992019-01-18 11:18:16 -0800424 final boolean stickyAutoDisableEnabledChanged =
425 mSettingBatterySaverStickyAutoDisableEnabled != isStickyAutoDisableEnabled;
426 final boolean stickyAutoDisableThresholdChanged =
427 mSettingBatterySaverStickyAutoDisableThreshold != stickyAutoDisableThreshold;
Salvador Martinez04b98332018-10-02 11:28:39 -0700428 final boolean automaticModeChanged = mSettingAutomaticBatterySaver != automaticBatterySaver;
429 final boolean dynamicPowerSavingsThresholdChanged =
430 mDynamicPowerSavingsDisableThreshold != dynamicPowerSavingsDisableThreshold;
431 final boolean dynamicPowerSavingsBatterySaverChanged =
432 mDynamicPowerSavingsBatterySaver != dynamicPowerSavingsBatterySaver;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700433
Salvador Martinez04b98332018-10-02 11:28:39 -0700434 if (!(enabledChanged || stickyChanged || thresholdChanged || automaticModeChanged
Kweku Adamsb243a992019-01-18 11:18:16 -0800435 || stickyAutoDisableEnabledChanged || stickyAutoDisableThresholdChanged
Salvador Martinez04b98332018-10-02 11:28:39 -0700436 || dynamicPowerSavingsThresholdChanged || dynamicPowerSavingsBatterySaverChanged)) {
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700437 return;
438 }
439
440 mSettingBatterySaverEnabled = batterySaverEnabled;
441 mSettingBatterySaverEnabledSticky = batterySaverEnabledSticky;
442 mSettingBatterySaverTriggerThreshold = batterySaverTriggerThreshold;
Kweku Adamsb243a992019-01-18 11:18:16 -0800443 mSettingBatterySaverStickyAutoDisableEnabled = isStickyAutoDisableEnabled;
444 mSettingBatterySaverStickyAutoDisableThreshold = stickyAutoDisableThreshold;
Salvador Martinez04b98332018-10-02 11:28:39 -0700445 mSettingAutomaticBatterySaver = automaticBatterySaver;
446 mDynamicPowerSavingsDisableThreshold = dynamicPowerSavingsDisableThreshold;
447 mDynamicPowerSavingsBatterySaver = dynamicPowerSavingsBatterySaver;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700448
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700449 if (thresholdChanged) {
450 // To avoid spamming the event log, we throttle logging here.
451 runOnBgThreadLazy(mThresholdChangeLogger, 2000);
452 }
453
Kweku Adams476ca772019-03-06 16:10:25 -0800454 if (!mSettingBatterySaverStickyAutoDisableEnabled) {
455 hideStickyDisabledNotification();
456 }
457
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700458 if (enabledChanged) {
459 final String reason = batterySaverEnabled
460 ? "Global.low_power changed to 1" : "Global.low_power changed to 0";
461 enableBatterySaverLocked(/*enable=*/ batterySaverEnabled, /*manual=*/ true,
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700462 BatterySaverController.REASON_SETTING_CHANGED, reason);
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800463 } else {
464 doAutoBatterySaverLocked();
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700465 }
466 }
467
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700468 private final Runnable mThresholdChangeLogger = () -> {
469 EventLogTags.writeBatterySaverSetting(mSettingBatterySaverTriggerThreshold);
470 };
471
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700472 /**
473 * {@link com.android.server.power.PowerManagerService} calls it when battery state changes.
474 *
475 * Note this may be called before {@link #onBootCompleted} too.
476 */
477 public void setBatteryStatus(boolean newPowered, int newLevel, boolean newBatteryLevelLow) {
478 if (DEBUG) {
479 Slog.d(TAG, "setBatteryStatus: powered=" + newPowered + " level=" + newLevel
480 + " low=" + newBatteryLevelLow);
481 }
482 synchronized (mLock) {
483 mBatteryStatusSet = true;
484
485 final boolean poweredChanged = mIsPowered != newPowered;
486 final boolean levelChanged = mBatteryLevel != newLevel;
487 final boolean lowChanged = mIsBatteryLevelLow != newBatteryLevelLow;
488
489 if (!(poweredChanged || levelChanged || lowChanged)) {
490 return;
491 }
492
493 mIsPowered = newPowered;
494 mBatteryLevel = newLevel;
495 mIsBatteryLevelLow = newBatteryLevelLow;
496
497 doAutoBatterySaverLocked();
498 }
499 }
500
Kweku Adams9f488e22019-01-14 16:25:08 -0800501 /**
502 * Enable or disable the current adaptive battery saver policy. This may not change what's in
503 * effect if full battery saver is also enabled.
504 */
505 public boolean setAdaptiveBatterySaverEnabled(boolean enabled) {
506 if (DEBUG) {
507 Slog.d(TAG, "setAdaptiveBatterySaverEnabled: enabled=" + enabled);
508 }
509 synchronized (mLock) {
510 mLastAdaptiveBatterySaverChangedExternallyElapsed = SystemClock.elapsedRealtime();
511 return mBatterySaverController.setAdaptivePolicyEnabledLocked(
512 enabled, BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED);
513 }
514 }
515
516 /**
517 * Change the adaptive battery saver policy.
518 */
519 public boolean setAdaptiveBatterySaverPolicy(BatterySaverPolicyConfig config) {
520 if (DEBUG) {
521 Slog.d(TAG, "setAdaptiveBatterySaverPolicy: config=" + config);
522 }
523
524 synchronized (mLock) {
525 mLastAdaptiveBatterySaverChangedExternallyElapsed = SystemClock.elapsedRealtime();
526 return mBatterySaverController.setAdaptivePolicyLocked(config,
527 BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED);
528 }
529 }
530
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700531 /**
532 * Decide whether to auto-start / stop battery saver.
533 */
Andreas Gampeedef60e2018-07-20 12:55:37 -0700534 @GuardedBy("mLock")
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700535 private void doAutoBatterySaverLocked() {
536 if (DEBUG) {
537 Slog.d(TAG, "doAutoBatterySaverLocked: mBootCompleted=" + mBootCompleted
538 + " mSettingsLoaded=" + mSettingsLoaded
539 + " mBatteryStatusSet=" + mBatteryStatusSet
Kweku Adams5bcc89f2019-05-15 11:23:19 -0700540 + " mState=" + mState
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700541 + " mIsBatteryLevelLow=" + mIsBatteryLevelLow
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700542 + " mIsPowered=" + mIsPowered
Salvador Martinez04b98332018-10-02 11:28:39 -0700543 + " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver
Kweku Adamsb243a992019-01-18 11:18:16 -0800544 + " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky
545 + " mSettingBatterySaverStickyAutoDisableEnabled="
546 + mSettingBatterySaverStickyAutoDisableEnabled);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700547 }
548 if (!(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
549 return; // Not fully initialized yet.
550 }
Salvador Martinez812ea752018-10-19 13:03:20 -0700551
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800552 updateStateLocked(false, false);
Kweku Adams9f488e22019-01-14 16:25:08 -0800553
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800554 // Adaptive control.
Kweku Adams9f488e22019-01-14 16:25:08 -0800555 if (SystemClock.elapsedRealtime() - mLastAdaptiveBatterySaverChangedExternallyElapsed
556 > ADAPTIVE_CHANGE_TIMEOUT_MS) {
557 mBatterySaverController.setAdaptivePolicyEnabledLocked(
558 false, BatterySaverController.REASON_TIMEOUT);
559 mBatterySaverController.resetAdaptivePolicyLocked(
560 BatterySaverController.REASON_TIMEOUT);
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800561 } else if (mIsPowered && mBatteryLevel >= ADAPTIVE_AUTO_DISABLE_BATTERY_LEVEL) {
562 mBatterySaverController.setAdaptivePolicyEnabledLocked(false,
563 BatterySaverController.REASON_PLUGGED_IN);
564 }
565 }
566
567 /**
568 * Update the state machine based on the current settings and battery/charge status.
569 *
570 * @param manual Whether the change was made by the user.
571 * @param enable Whether the user wants to turn battery saver on or off. Is only used if {@param
572 * manual} is true.
573 */
574 @GuardedBy("mLock")
575 private void updateStateLocked(boolean manual, boolean enable) {
576 if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
577 return; // Not fully initialized yet.
Kweku Adams9f488e22019-01-14 16:25:08 -0800578 }
579
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800580 switch (mState) {
581 case STATE_OFF: {
582 if (!mIsPowered) {
583 if (manual) {
584 if (!enable) {
585 Slog.e(TAG, "Tried to disable BS when it's already OFF");
586 return;
587 }
588 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
589 BatterySaverController.REASON_MANUAL_ON);
Kweku Adams476ca772019-03-06 16:10:25 -0800590 hideStickyDisabledNotification();
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800591 mState = STATE_MANUAL_ON;
592 } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) {
593 enableBatterySaverLocked(/*enable*/ true, /*manual*/ false,
594 BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON);
Kweku Adams476ca772019-03-06 16:10:25 -0800595 hideStickyDisabledNotification();
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800596 mState = STATE_AUTOMATIC_ON;
597 } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) {
598 enableBatterySaverLocked(/*enable*/ true, /*manual*/ false,
599 BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON);
Kweku Adams476ca772019-03-06 16:10:25 -0800600 hideStickyDisabledNotification();
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800601 mState = STATE_AUTOMATIC_ON;
602 }
603 }
604 break;
Kweku Adams9f488e22019-01-14 16:25:08 -0800605 }
606
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800607 case STATE_MANUAL_ON: {
608 if (manual) {
609 if (enable) {
610 Slog.e(TAG, "Tried to enable BS when it's already MANUAL_ON");
611 return;
612 }
613 enableBatterySaverLocked(/*enable*/ false, /*manual*/ true,
614 BatterySaverController.REASON_MANUAL_OFF);
615 mState = STATE_OFF;
616 } else if (mIsPowered) {
617 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
618 BatterySaverController.REASON_PLUGGED_IN);
619 if (mSettingBatterySaverEnabledSticky
620 && !mBatterySaverStickyBehaviourDisabled) {
621 mState = STATE_PENDING_STICKY_ON;
622 } else {
623 mState = STATE_OFF;
624 }
625 }
626 break;
Kweku Adamsb243a992019-01-18 11:18:16 -0800627 }
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700628
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800629 case STATE_AUTOMATIC_ON: {
630 if (mIsPowered) {
631 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
632 BatterySaverController.REASON_PLUGGED_IN);
633 mState = STATE_OFF;
634 } else if (manual) {
635 if (enable) {
636 Slog.e(TAG, "Tried to enable BS when it's already AUTO_ON");
637 return;
638 }
639 enableBatterySaverLocked(/*enable*/ false, /*manual*/ true,
640 BatterySaverController.REASON_MANUAL_OFF);
641 // When battery saver is disabled manually (while battery saver is enabled)
642 // when the battery level is low, we "snooze" BS -- i.e. disable auto battery
643 // saver.
644 // We resume auto-BS once the battery level is not low, or the device is
645 // plugged in.
646 mState = STATE_OFF_AUTOMATIC_SNOOZED;
647 } else if (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked()) {
648 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
649 BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF);
650 mState = STATE_OFF;
651 } else if (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked()) {
652 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
653 BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF);
654 mState = STATE_OFF;
655 } else if (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked()) {
656 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
657 BatterySaverController.REASON_SETTING_CHANGED);
658 mState = STATE_OFF;
659 }
660 break;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700661 }
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800662
663 case STATE_OFF_AUTOMATIC_SNOOZED: {
664 if (manual) {
665 if (!enable) {
666 Slog.e(TAG, "Tried to disable BS when it's already AUTO_SNOOZED");
667 return;
668 }
669 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
670 BatterySaverController.REASON_MANUAL_ON);
671 mState = STATE_MANUAL_ON;
672 } else if (mIsPowered // Plugging in resets snooze.
673 || (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked())
674 || (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked())
675 || (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked())) {
676 mState = STATE_OFF;
677 }
678 break;
Salvador Martinez04b98332018-10-02 11:28:39 -0700679 }
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800680
681 case STATE_PENDING_STICKY_ON: {
682 if (manual) {
683 // This shouldn't be possible. We'll only be in this state when the device is
684 // plugged in, so the user shouldn't be able to manually change state.
685 Slog.e(TAG, "Tried to manually change BS state from PENDING_STICKY_ON");
686 return;
687 }
688 final boolean shouldTurnOffSticky = mSettingBatterySaverStickyAutoDisableEnabled
689 && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold;
690 final boolean isStickyDisabled =
691 mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky;
692 if (isStickyDisabled || shouldTurnOffSticky) {
Kweku Adams5bcc89f2019-05-15 11:23:19 -0700693 mState = STATE_OFF;
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800694 setStickyActive(false);
Kweku Adams476ca772019-03-06 16:10:25 -0800695 triggerStickyDisabledNotification();
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800696 } else if (!mIsPowered) {
697 // Re-enable BS.
698 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
699 BatterySaverController.REASON_STICKY_RESTORE);
700 mState = STATE_MANUAL_ON;
701 }
702 break;
703 }
704
705 default:
706 Slog.wtf(TAG, "Unknown state: " + mState);
707 break;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700708 }
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800709 }
710
711 @VisibleForTesting
712 int getState() {
713 synchronized (mLock) {
714 return mState;
715 }
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700716 }
717
Kweku Adams9f488e22019-01-14 16:25:08 -0800718 /**
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700719 * {@link com.android.server.power.PowerManagerService} calls it when
Salvador Martinezc8c4c5d2019-03-11 11:11:37 -0700720 * {@link android.os.PowerManager#setPowerSaveModeEnabled} is called.
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700721 *
722 * Note this could? be called before {@link #onBootCompleted} too.
723 */
724 public void setBatterySaverEnabledManually(boolean enabled) {
725 if (DEBUG) {
726 Slog.d(TAG, "setBatterySaverEnabledManually: enabled=" + enabled);
727 }
728 synchronized (mLock) {
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800729 updateStateLocked(true, enabled);
730 // TODO: maybe turn off adaptive if it's on and advertiseIsEnabled is true and
731 // enabled is false
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700732 }
733 }
734
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800735 @GuardedBy("mLock")
736 private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason) {
737 enableBatterySaverLocked(enable, manual, intReason, reasonToString(intReason));
738 }
739
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700740 /**
741 * Actually enable / disable battery saver. Write the new state to the global settings
742 * and propagate it to {@link #mBatterySaverController}.
743 */
Andreas Gampeedef60e2018-07-20 12:55:37 -0700744 @GuardedBy("mLock")
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700745 private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
746 String strReason) {
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700747 if (DEBUG) {
748 Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700749 + " reason=" + strReason + "(" + intReason + ")");
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700750 }
Kweku Adams9f488e22019-01-14 16:25:08 -0800751 final boolean wasEnabled = mBatterySaverController.isFullEnabled();
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700752
753 if (wasEnabled == enable) {
754 if (DEBUG) {
755 Slog.d(TAG, "Already " + (enable ? "enabled" : "disabled"));
756 }
757 return;
758 }
759 if (enable && mIsPowered) {
760 if (DEBUG) Slog.d(TAG, "Can't enable: isPowered");
761 return;
762 }
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700763 mLastChangedIntReason = intReason;
764 mLastChangedStrReason = strReason;
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700765
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700766 mSettingBatterySaverEnabled = enable;
Kweku Adamsb243a992019-01-18 11:18:16 -0800767 putGlobalSetting(Settings.Global.LOW_POWER_MODE, enable ? 1 : 0);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700768
769 if (manual) {
Kweku Adamsb243a992019-01-18 11:18:16 -0800770 setStickyActive(!mBatterySaverStickyBehaviourDisabled && enable);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700771 }
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700772 mBatterySaverController.enableBatterySaver(enable, intReason);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700773
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800774 // Handle triggering the notification to show/hide when appropriate
775 if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
776 runOnBgThread(this::triggerDynamicModeNotification);
777 } else if (!enable) {
778 runOnBgThread(this::hideDynamicModeNotification);
779 }
780
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700781 if (DEBUG) {
782 Slog.d(TAG, "Battery saver: Enabled=" + enable
783 + " manual=" + manual
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700784 + " reason=" + strReason + "(" + intReason + ")");
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700785 }
786 }
787
Kweku Adamsb243a992019-01-18 11:18:16 -0800788 @VisibleForTesting
789 void triggerDynamicModeNotification() {
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800790 NotificationManager manager = mContext.getSystemService(NotificationManager.class);
Kweku Adams476ca772019-03-06 16:10:25 -0800791 ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID,
792 R.string.dynamic_mode_notification_channel_name);
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800793
Kweku Adams400e0442019-05-23 18:41:23 -0700794 manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID,
Kweku Adams476ca772019-03-06 16:10:25 -0800795 buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
Kweku Adams3c95ee12019-03-21 13:06:53 -0700796 mContext.getResources().getString(R.string.dynamic_mode_notification_title),
Kweku Adams476ca772019-03-06 16:10:25 -0800797 R.string.dynamic_mode_notification_summary,
Kweku Adams400e0442019-05-23 18:41:23 -0700798 Intent.ACTION_POWER_USAGE_SUMMARY),
799 UserHandle.ALL);
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800800 }
801
Kweku Adams5bcc89f2019-05-15 11:23:19 -0700802 @VisibleForTesting
803 void triggerStickyDisabledNotification() {
Kweku Adams476ca772019-03-06 16:10:25 -0800804 NotificationManager manager = mContext.getSystemService(NotificationManager.class);
805 ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
806 R.string.battery_saver_notification_channel_name);
807
Kweku Adams3c95ee12019-03-21 13:06:53 -0700808 final String percentage = NumberFormat.getPercentInstance()
809 .format((double) mBatteryLevel / 100.0);
Kweku Adams400e0442019-05-23 18:41:23 -0700810 manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID,
Kweku Adams476ca772019-03-06 16:10:25 -0800811 buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID,
Kweku Adams3c95ee12019-03-21 13:06:53 -0700812 mContext.getResources().getString(
813 R.string.battery_saver_charged_notification_title, percentage),
814 R.string.battery_saver_off_notification_summary,
Kweku Adams400e0442019-05-23 18:41:23 -0700815 Settings.ACTION_BATTERY_SAVER_SETTINGS),
816 UserHandle.ALL);
Kweku Adams476ca772019-03-06 16:10:25 -0800817 }
818
819 private void ensureNotificationChannelExists(NotificationManager manager,
820 @NonNull String channelId, @StringRes int nameId) {
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800821 NotificationChannel channel = new NotificationChannel(
Kweku Adams476ca772019-03-06 16:10:25 -0800822 channelId, mContext.getText(nameId), NotificationManager.IMPORTANCE_DEFAULT);
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800823 channel.setSound(null, null);
Kweku Adams400e0442019-05-23 18:41:23 -0700824 channel.setBlockableSystem(true);
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800825 manager.createNotificationChannel(channel);
826 }
827
Kweku Adams3c95ee12019-03-21 13:06:53 -0700828 private Notification buildNotification(@NonNull String channelId, @NonNull String title,
Kweku Adams476ca772019-03-06 16:10:25 -0800829 @StringRes int summaryId, @NonNull String intentAction) {
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800830 Resources res = mContext.getResources();
Kweku Adams476ca772019-03-06 16:10:25 -0800831 Intent intent = new Intent(intentAction);
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800832 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
833 PendingIntent batterySaverIntent = PendingIntent.getActivity(
834 mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Kweku Adams476ca772019-03-06 16:10:25 -0800835 final String summary = res.getString(summaryId);
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800836
Kweku Adams476ca772019-03-06 16:10:25 -0800837 return new Notification.Builder(mContext, channelId)
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800838 .setSmallIcon(R.drawable.ic_battery)
Kweku Adams3c95ee12019-03-21 13:06:53 -0700839 .setContentTitle(title)
Kweku Adams476ca772019-03-06 16:10:25 -0800840 .setContentText(summary)
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800841 .setContentIntent(batterySaverIntent)
Kweku Adams476ca772019-03-06 16:10:25 -0800842 .setStyle(new Notification.BigTextStyle().bigText(summary))
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800843 .setOnlyAlertOnce(true)
Kweku Adams3c95ee12019-03-21 13:06:53 -0700844 .setAutoCancel(true)
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800845 .build();
846 }
847
848 private void hideDynamicModeNotification() {
Kweku Adams476ca772019-03-06 16:10:25 -0800849 hideNotification(DYNAMIC_MODE_NOTIFICATION_ID);
850 }
851
852 private void hideStickyDisabledNotification() {
853 hideNotification(STICKY_AUTO_DISABLED_NOTIFICATION_ID);
854 }
855
856 private void hideNotification(int notificationId) {
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800857 NotificationManager manager = mContext.getSystemService(NotificationManager.class);
Kweku Adams476ca772019-03-06 16:10:25 -0800858 manager.cancel(notificationId);
Salvador Martinez3a3f8222018-12-14 15:29:30 -0800859 }
860
Kweku Adamsb243a992019-01-18 11:18:16 -0800861 private void setStickyActive(boolean active) {
862 mSettingBatterySaverEnabledSticky = active;
863 putGlobalSetting(Settings.Global.LOW_POWER_MODE_STICKY,
864 mSettingBatterySaverEnabledSticky ? 1 : 0);
865 }
866
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700867 @VisibleForTesting
868 protected void putGlobalSetting(String key, int value) {
Kweku Adamsb243a992019-01-18 11:18:16 -0800869 Settings.Global.putInt(mContext.getContentResolver(), key, value);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700870 }
871
872 @VisibleForTesting
873 protected int getGlobalSetting(String key, int defValue) {
Kweku Adamsb243a992019-01-18 11:18:16 -0800874 return Settings.Global.getInt(mContext.getContentResolver(), key, defValue);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700875 }
876
877 public void dump(PrintWriter pw) {
878 synchronized (mLock) {
879 pw.println();
880 pw.println("Battery saver state machine:");
881
882 pw.print(" Enabled=");
883 pw.println(mBatterySaverController.isEnabled());
Kweku Adams9f488e22019-01-14 16:25:08 -0800884 pw.print(" full=");
885 pw.println(mBatterySaverController.isFullEnabled());
886 pw.print(" adaptive=");
887 pw.print(mBatterySaverController.isAdaptiveEnabled());
888 if (mBatterySaverController.isAdaptiveEnabled()) {
889 pw.print(" (advertise=");
890 pw.print(
891 mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled());
892 pw.print(")");
893 }
894 pw.println();
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800895 pw.print(" mState=");
896 pw.println(mState);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700897
Makoto Onuki0f8617a2018-04-30 10:26:21 -0700898 pw.print(" mLastChangedIntReason=");
899 pw.println(mLastChangedIntReason);
900 pw.print(" mLastChangedStrReason=");
901 pw.println(mLastChangedStrReason);
902
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700903 pw.print(" mBootCompleted=");
904 pw.println(mBootCompleted);
905 pw.print(" mSettingsLoaded=");
906 pw.println(mSettingsLoaded);
907 pw.print(" mBatteryStatusSet=");
908 pw.println(mBatteryStatusSet);
909
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700910 pw.print(" mIsPowered=");
911 pw.println(mIsPowered);
912 pw.print(" mBatteryLevel=");
913 pw.println(mBatteryLevel);
914 pw.print(" mIsBatteryLevelLow=");
915 pw.println(mIsBatteryLevelLow);
916
917 pw.print(" mSettingBatterySaverEnabled=");
918 pw.println(mSettingBatterySaverEnabled);
919 pw.print(" mSettingBatterySaverEnabledSticky=");
920 pw.println(mSettingBatterySaverEnabledSticky);
Kweku Adamsb243a992019-01-18 11:18:16 -0800921 pw.print(" mSettingBatterySaverStickyAutoDisableEnabled=");
922 pw.println(mSettingBatterySaverStickyAutoDisableEnabled);
923 pw.print(" mSettingBatterySaverStickyAutoDisableThreshold=");
924 pw.println(mSettingBatterySaverStickyAutoDisableThreshold);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700925 pw.print(" mSettingBatterySaverTriggerThreshold=");
926 pw.println(mSettingBatterySaverTriggerThreshold);
Michael Kwane3225022018-06-26 18:03:38 -0700927 pw.print(" mBatterySaverStickyBehaviourDisabled=");
928 pw.println(mBatterySaverStickyBehaviourDisabled);
Kweku Adams9f488e22019-01-14 16:25:08 -0800929
930 pw.print(" mLastAdaptiveBatterySaverChangedExternallyElapsed=");
931 pw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700932 }
933 }
934
935 public void dumpProto(ProtoOutputStream proto, long tag) {
936 synchronized (mLock) {
937 final long token = proto.start(tag);
938
939 proto.write(BatterySaverStateMachineProto.ENABLED,
940 mBatterySaverController.isEnabled());
Kweku Adamsf57f99e2019-03-07 15:38:21 -0800941 proto.write(BatterySaverStateMachineProto.STATE, mState);
Kweku Adams9f488e22019-01-14 16:25:08 -0800942 proto.write(BatterySaverStateMachineProto.IS_FULL_ENABLED,
943 mBatterySaverController.isFullEnabled());
944 proto.write(BatterySaverStateMachineProto.IS_ADAPTIVE_ENABLED,
945 mBatterySaverController.isAdaptiveEnabled());
946 proto.write(BatterySaverStateMachineProto.SHOULD_ADVERTISE_IS_ENABLED,
947 mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled());
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700948
949 proto.write(BatterySaverStateMachineProto.BOOT_COMPLETED, mBootCompleted);
950 proto.write(BatterySaverStateMachineProto.SETTINGS_LOADED, mSettingsLoaded);
951 proto.write(BatterySaverStateMachineProto.BATTERY_STATUS_SET, mBatteryStatusSet);
952
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700953
954 proto.write(BatterySaverStateMachineProto.IS_POWERED, mIsPowered);
955 proto.write(BatterySaverStateMachineProto.BATTERY_LEVEL, mBatteryLevel);
956 proto.write(BatterySaverStateMachineProto.IS_BATTERY_LEVEL_LOW, mIsBatteryLevelLow);
957
958 proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_ENABLED,
959 mSettingBatterySaverEnabled);
960 proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_ENABLED_STICKY,
961 mSettingBatterySaverEnabledSticky);
962 proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_TRIGGER_THRESHOLD,
963 mSettingBatterySaverTriggerThreshold);
Kweku Adamsb243a992019-01-18 11:18:16 -0800964 proto.write(
965 BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_STICKY_AUTO_DISABLE_ENABLED,
966 mSettingBatterySaverStickyAutoDisableEnabled);
967 proto.write(
968 BatterySaverStateMachineProto
969 .SETTING_BATTERY_SAVER_STICKY_AUTO_DISABLE_THRESHOLD,
970 mSettingBatterySaverStickyAutoDisableThreshold);
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700971
Kweku Adams9f488e22019-01-14 16:25:08 -0800972 proto.write(
973 BatterySaverStateMachineProto
974 .LAST_ADAPTIVE_BATTERY_SAVER_CHANGED_EXTERNALLY_ELAPSED,
975 mLastAdaptiveBatterySaverChangedExternallyElapsed);
976
Makoto Onukia3cd7b92018-03-19 14:47:05 -0700977 proto.end(token);
978 }
979 }
980}