blob: 75dc39722bcf70b1c4ab8061955006aa59d969d8 [file] [log] [blame]
Joe Onorato10523b4d2010-10-25 10:42:46 -07001/*
2 * Copyright (C) 2008 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
17package com.android.systemui.power;
18
Joe Onorato10523b4d2010-10-25 10:42:46 -070019import android.content.BroadcastReceiver;
Dianne Hackborn14272302014-06-10 23:13:02 -070020import android.content.ContentResolver;
Joe Onorato10523b4d2010-10-25 10:42:46 -070021import android.content.Context;
Joe Onorato10523b4d2010-10-25 10:42:46 -070022import android.content.Intent;
23import android.content.IntentFilter;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070024import android.content.pm.ActivityInfo;
25import android.content.res.Configuration;
Dianne Hackborn14272302014-06-10 23:13:02 -070026import android.database.ContentObserver;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070027import android.os.BatteryManager;
Joe Onorato10523b4d2010-10-25 10:42:46 -070028import android.os.Handler;
Todd Poynorfb95e122017-10-04 13:00:32 -070029import android.os.IThermalEventListener;
30import android.os.IThermalService;
Daniel Sandlerdea64622013-09-23 16:05:57 -040031import android.os.PowerManager;
Todd Poynorfb95e122017-10-04 13:00:32 -070032import android.os.RemoteException;
33import android.os.ServiceManager;
Daniel Sandlerdea64622013-09-23 16:05:57 -040034import android.os.SystemClock;
Todd Poynorfb95e122017-10-04 13:00:32 -070035import android.os.Temperature;
Dianne Hackborn14272302014-06-10 23:13:02 -070036import android.os.UserHandle;
Joe Onorato10523b4d2010-10-25 10:42:46 -070037import android.provider.Settings;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080038import android.text.format.DateUtils;
John Spurlock1bb480a2014-08-02 17:12:43 -040039import android.util.Log;
Daniel Sandlerdea64622013-09-23 16:05:57 -040040import android.util.Slog;
Jason Monkd819c312017-08-11 12:53:36 -040041
42import com.android.internal.annotations.VisibleForTesting;
Salvador Martinez580098fe2019-04-11 10:42:15 -070043import com.android.settingslib.fuelgauge.Estimate;
Salvador Martinez110f9a12018-01-31 09:57:17 -080044import com.android.settingslib.utils.ThreadUtils;
Jason Monkd819c312017-08-11 12:53:36 -040045import com.android.systemui.Dependency;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080046import com.android.systemui.R;
Joe Onorato10523b4d2010-10-25 10:42:46 -070047import com.android.systemui.SystemUI;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050048import com.android.systemui.statusbar.phone.StatusBar;
Jason Monkd819c312017-08-11 12:53:36 -040049
John Spurlockde84f0e2013-06-12 12:41:00 -040050import java.io.FileDescriptor;
51import java.io.PrintWriter;
Salvador Martinezfd38aa52018-03-28 23:56:59 -070052import java.time.Duration;
John Spurlockde84f0e2013-06-12 12:41:00 -040053import java.util.Arrays;
Salvador Martinez7ad2c172019-02-11 16:09:28 -080054import java.util.concurrent.Future;
John Spurlockde84f0e2013-06-12 12:41:00 -040055
Joe Onorato10523b4d2010-10-25 10:42:46 -070056public class PowerUI extends SystemUI {
Salvador Martinez4387bd52019-02-21 16:16:28 -080057
Joe Onorato10523b4d2010-10-25 10:42:46 -070058 static final String TAG = "PowerUI";
John Spurlock1bb480a2014-08-02 17:12:43 -040059 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080060 private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -080061 private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
62 private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
Salvador Martinezf9e47502018-01-04 13:45:48 -080063 static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
Salvador Martinezfd38aa52018-03-28 23:56:59 -070064 private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
65 private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
Salvador Martinez4387bd52019-02-21 16:16:28 -080066 public static final int NO_ESTIMATE_AVAILABLE = -1;
Sherry Huang9b786732019-08-03 02:36:44 +080067 private static final String BOOT_COUNT_KEY = "boot_count";
68 private static final String PREFS = "powerui_prefs";
Joe Onorato10523b4d2010-10-25 10:42:46 -070069
John Spurlocked452c52014-03-06 12:02:31 -050070 private final Handler mHandler = new Handler();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -040071 @VisibleForTesting
72 final Receiver mReceiver = new Receiver();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070073
John Spurlock3332ba52014-03-10 17:44:07 -040074 private PowerManager mPowerManager;
75 private WarningsUI mWarnings;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070076 private final Configuration mLastConfiguration = new Configuration();
John Spurlocked452c52014-03-06 12:02:31 -050077 private int mPlugType = 0;
78 private int mInvalidCharger = 0;
Salvador Martinezf9e47502018-01-04 13:45:48 -080079 private EnhancedEstimates mEnhancedEstimates;
Salvador Martinez7ad2c172019-02-11 16:09:28 -080080 private Future mLastShowWarningTask;
Sherry Huangce02ed32019-01-17 20:37:29 +080081 private boolean mEnableSkinTemperatureWarning;
82 private boolean mEnableUsbTemperatureAlarm;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070083
John Spurlocked452c52014-03-06 12:02:31 -050084 private int mLowBatteryAlertCloseLevel;
85 private final int[] mLowBatteryReminderLevels = new int[2];
Joe Onorato10523b4d2010-10-25 10:42:46 -070086
Daniel Sandlerdea64622013-09-23 16:05:57 -040087 private long mScreenOffTime = -1;
88
Salvador Martinez4387bd52019-02-21 16:16:28 -080089 @VisibleForTesting boolean mLowWarningShownThisChargeCycle;
90 @VisibleForTesting boolean mSevereWarningShownThisChargeCycle;
91 @VisibleForTesting BatteryStateSnapshot mCurrentBatteryStateSnapshot;
92 @VisibleForTesting BatteryStateSnapshot mLastBatteryStateSnapshot;
Wei Wangbf05e602018-11-21 11:46:48 -080093 @VisibleForTesting IThermalService mThermalService;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080094
Salvador Martinezfd38aa52018-03-28 23:56:59 -070095 @VisibleForTesting int mBatteryLevel = 100;
Salvador Martinez36307962018-02-08 14:29:08 -080096 @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
97
Sherry Huang18d27042019-03-19 21:45:28 +080098 private IThermalEventListener mSkinThermalEventListener;
99 private IThermalEventListener mUsbThermalEventListener;
100
Joe Onorato10523b4d2010-10-25 10:42:46 -0700101 public void start() {
John Spurlock3332ba52014-03-10 17:44:07 -0400102 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
103 mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
Jason Monkd819c312017-08-11 12:53:36 -0400104 mWarnings = Dependency.get(WarningsUI.class);
Salvador Martinezf9e47502018-01-04 13:45:48 -0800105 mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700106 mLastConfiguration.setTo(mContext.getResources().getConfiguration());
Daniel Sandlerdea64622013-09-23 16:05:57 -0400107
Dianne Hackborn14272302014-06-10 23:13:02 -0700108 ContentObserver obs = new ContentObserver(mHandler) {
109 @Override
110 public void onChange(boolean selfChange) {
111 updateBatteryWarningLevels();
112 }
113 };
114 final ContentResolver resolver = mContext.getContentResolver();
115 resolver.registerContentObserver(Settings.Global.getUriFor(
116 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
117 false, obs, UserHandle.USER_ALL);
118 updateBatteryWarningLevels();
John Spurlock3332ba52014-03-10 17:44:07 -0400119 mReceiver.init();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800120
Salvador Martineza6f7b252017-04-10 10:46:15 -0700121 // Check to see if we need to let the user know that the phone previously shut down due
122 // to the temperature being too high.
Sherry Huang9b786732019-08-03 02:36:44 +0800123 showWarnOnThermalShutdown();
Salvador Martineza6f7b252017-04-10 10:46:15 -0700124
Sherry Huang18d27042019-03-19 21:45:28 +0800125 // Register an observer to configure mEnableSkinTemperatureWarning and perform the
126 // registration of skin thermal event listener upon Settings change.
127 resolver.registerContentObserver(
128 Settings.Global.getUriFor(Settings.Global.SHOW_TEMPERATURE_WARNING),
129 false /*notifyForDescendants*/,
130 new ContentObserver(mHandler) {
131 @Override
132 public void onChange(boolean selfChange) {
133 doSkinThermalEventListenerRegistration();
134 }
135 });
136 // Register an observer to configure mEnableUsbTemperatureAlarm and perform the
137 // registration of usb thermal event listener upon Settings change.
138 resolver.registerContentObserver(
139 Settings.Global.getUriFor(Settings.Global.SHOW_USB_TEMPERATURE_ALARM),
140 false /*notifyForDescendants*/,
141 new ContentObserver(mHandler) {
142 @Override
143 public void onChange(boolean selfChange) {
144 doUsbThermalEventListenerRegistration();
145 }
146 });
147 initThermalEventListeners();
John Spurlock3332ba52014-03-10 17:44:07 -0400148 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700149
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700150 @Override
151 protected void onConfigurationChanged(Configuration newConfig) {
152 final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
153
154 // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
155 if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
Sherry Huang18d27042019-03-19 21:45:28 +0800156 mHandler.post(this::initThermalEventListeners);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700157 }
158 }
159
Dianne Hackborn14272302014-06-10 23:13:02 -0700160 void updateBatteryWarningLevels() {
161 int critLevel = mContext.getResources().getInteger(
162 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Makoto Onuki16a0dd22018-03-20 10:40:37 -0700163 int warnLevel = mContext.getResources().getInteger(
Dianne Hackborn14272302014-06-10 23:13:02 -0700164 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Makoto Onuki4a9036b2018-01-11 14:48:20 -0800165
Dianne Hackborn14272302014-06-10 23:13:02 -0700166 if (warnLevel < critLevel) {
167 warnLevel = critLevel;
168 }
169
170 mLowBatteryReminderLevels[0] = warnLevel;
171 mLowBatteryReminderLevels[1] = critLevel;
172 mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
173 + mContext.getResources().getInteger(
174 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
175 }
176
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700177 /**
178 * Buckets the battery level.
179 *
180 * The code in this function is a little weird because I couldn't comprehend
181 * the bucket going up when the battery level was going down. --joeo
182 *
183 * 1 means that the battery is "ok"
184 * 0 means that the battery is between "ok" and what we should warn about.
185 * less than 0 means that the battery is low
186 */
187 private int findBatteryLevelBucket(int level) {
188 if (level >= mLowBatteryAlertCloseLevel) {
189 return 1;
190 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700191 if (level > mLowBatteryReminderLevels[0]) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700192 return 0;
193 }
194 final int N = mLowBatteryReminderLevels.length;
195 for (int i=N-1; i>=0; i--) {
196 if (level <= mLowBatteryReminderLevels[i]) {
197 return -1-i;
198 }
199 }
200 throw new RuntimeException("not possible!");
201 }
202
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400203 @VisibleForTesting
204 final class Receiver extends BroadcastReceiver {
John Spurlock3332ba52014-03-10 17:44:07 -0400205
206 public void init() {
207 // Register for Intent broadcasts for...
208 IntentFilter filter = new IntentFilter();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400209 filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
John Spurlock3332ba52014-03-10 17:44:07 -0400210 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
211 filter.addAction(Intent.ACTION_SCREEN_OFF);
212 filter.addAction(Intent.ACTION_SCREEN_ON);
John Spurlockecbc5e82014-10-22 09:05:51 -0400213 filter.addAction(Intent.ACTION_USER_SWITCHED);
John Spurlock3332ba52014-03-10 17:44:07 -0400214 mContext.registerReceiver(this, filter, null, mHandler);
John Spurlock3332ba52014-03-10 17:44:07 -0400215 }
216
Joe Onorato10523b4d2010-10-25 10:42:46 -0700217 @Override
218 public void onReceive(Context context, Intent intent) {
219 String action = intent.getAction();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400220 if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
221 ThreadUtils.postOnBackgroundThread(() -> {
222 if (mPowerManager.isPowerSaveMode()) {
223 mWarnings.dismissLowBatteryWarning();
224 }
225 });
226 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700227 final int oldBatteryLevel = mBatteryLevel;
228 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
229 final int oldBatteryStatus = mBatteryStatus;
230 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
231 BatteryManager.BATTERY_STATUS_UNKNOWN);
232 final int oldPlugType = mPlugType;
233 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
234 final int oldInvalidCharger = mInvalidCharger;
235 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
Salvador Martinez4387bd52019-02-21 16:16:28 -0800236 mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700237
238 final boolean plugged = mPlugType != 0;
239 final boolean oldPlugged = oldPlugType != 0;
240
241 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
242 int bucket = findBatteryLevelBucket(mBatteryLevel);
243
Daniel Sandler71986622011-07-26 13:06:49 -0400244 if (DEBUG) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400245 Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700246 + " .. " + mLowBatteryReminderLevels[0]
247 + " .. " + mLowBatteryReminderLevels[1]);
Daniel Sandlerdea64622013-09-23 16:05:57 -0400248 Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
249 Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
250 Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
251 Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
252 Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
253 Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700254 }
255
John Spurlocked452c52014-03-06 12:02:31 -0500256 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700257 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400258 Slog.d(TAG, "showing invalid charger warning");
John Spurlocked452c52014-03-06 12:02:31 -0500259 mWarnings.showInvalidChargerWarning();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700260 return;
261 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
John Spurlocked452c52014-03-06 12:02:31 -0500262 mWarnings.dismissInvalidChargerWarning();
263 } else if (mWarnings.isInvalidChargerWarningShowing()) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700264 // if invalid charger is showing, don't show low battery
Salvador Martinez4387bd52019-02-21 16:16:28 -0800265 if (DEBUG) {
266 Slog.d(TAG, "Bad Charger");
267 }
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700268 return;
269 }
270
Salvador Martinezf9e47502018-01-04 13:45:48 -0800271 // Show the correct version of low battery warning if needed
Salvador Martinez7ad2c172019-02-11 16:09:28 -0800272 if (mLastShowWarningTask != null) {
273 mLastShowWarningTask.cancel(true);
Salvador Martinez4387bd52019-02-21 16:16:28 -0800274 if (DEBUG) {
275 Slog.d(TAG, "cancelled task");
276 }
Salvador Martinez7ad2c172019-02-11 16:09:28 -0800277 }
278 mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800279 maybeShowBatteryWarningV2(
280 plugged, bucket);
Salvador Martinez110f9a12018-01-31 09:57:17 -0800281 });
Beverly334bc5f2017-07-31 10:37:17 -0400282
Daniel Sandlerdea64622013-09-23 16:05:57 -0400283 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
284 mScreenOffTime = SystemClock.elapsedRealtime();
285 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
286 mScreenOffTime = -1;
John Spurlockecbc5e82014-10-22 09:05:51 -0400287 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
288 mWarnings.userSwitched();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700289 } else {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400290 Slog.w(TAG, "unknown intent: " + intent);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700291 }
292 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800293 }
294
Salvador Martinez4387bd52019-02-21 16:16:28 -0800295 protected void maybeShowBatteryWarningV2(boolean plugged, int bucket) {
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700296 final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
Salvador Martinez4387bd52019-02-21 16:16:28 -0800297 final boolean isPowerSaverMode = mPowerManager.isPowerSaveMode();
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700298
Salvador Martinez4387bd52019-02-21 16:16:28 -0800299 // Stick current battery state into an immutable container to determine if we should show
300 // a warning.
301 if (DEBUG) {
302 Slog.d(TAG, "evaluating which notification to show");
303 }
304 if (hybridEnabled) {
305 if (DEBUG) {
306 Slog.d(TAG, "using hybrid");
307 }
308 Estimate estimate = refreshEstimateIfNeeded();
309 mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
310 plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
311 mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
Salvador Martinez580098fe2019-04-11 10:42:15 -0700312 estimate.getAverageDischargeTime(),
Salvador Martinez4387bd52019-02-21 16:16:28 -0800313 mEnhancedEstimates.getSevereWarningThreshold(),
Salvador Martinezd73c5aa2019-03-26 12:38:09 -0700314 mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage(),
315 mEnhancedEstimates.getLowWarningEnabled());
Salvador Martinez4387bd52019-02-21 16:16:28 -0800316 } else {
317 if (DEBUG) {
318 Slog.d(TAG, "using standard");
319 }
320 mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
321 plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
322 mLowBatteryReminderLevels[0]);
323 }
324
325 mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
326 if (mCurrentBatteryStateSnapshot.isHybrid()) {
327 maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
328 } else {
329 maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
330 }
331 }
332
333 // updates the time estimate if we don't have one or battery level has changed.
334 @VisibleForTesting
335 Estimate refreshEstimateIfNeeded() {
336 if (mLastBatteryStateSnapshot == null
337 || mLastBatteryStateSnapshot.getTimeRemainingMillis() == NO_ESTIMATE_AVAILABLE
338 || mBatteryLevel != mLastBatteryStateSnapshot.getBatteryLevel()) {
339 final Estimate estimate = mEnhancedEstimates.getEstimate();
340 if (DEBUG) {
341 Slog.d(TAG, "updated estimate: " + estimate.getEstimateMillis());
342 }
343 return estimate;
344 }
345 return new Estimate(mLastBatteryStateSnapshot.getTimeRemainingMillis(),
Salvador Martinez580098fe2019-04-11 10:42:15 -0700346 mLastBatteryStateSnapshot.isBasedOnUsage(),
347 mLastBatteryStateSnapshot.getAverageTimeToDischargeMillis());
Salvador Martinez4387bd52019-02-21 16:16:28 -0800348 }
349
350 @VisibleForTesting
351 void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
352 BatteryStateSnapshot lastSnapshot) {
353 // if we are now over 45% battery & 6 hours remaining so we can trigger hybrid
354 // notification again
355 if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET
356 && currentSnapshot.getTimeRemainingMillis() > SIX_HOURS_MILLIS) {
357 mLowWarningShownThisChargeCycle = false;
358 mSevereWarningShownThisChargeCycle = false;
359 if (DEBUG) {
360 Slog.d(TAG, "Charge cycle reset! Can show warnings again");
Salvador Martinezf9e47502018-01-04 13:45:48 -0800361 }
362 }
363
Salvador Martinez4387bd52019-02-21 16:16:28 -0800364 final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
365 || lastSnapshot.getPlugged();
Salvador Martinez36307962018-02-08 14:29:08 -0800366
Salvador Martinez4387bd52019-02-21 16:16:28 -0800367 if (shouldShowHybridWarning(currentSnapshot)) {
368 mWarnings.showLowBatteryWarning(playSound);
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700369 // mark if we've already shown a warning this cycle. This will prevent the notification
370 // trigger from spamming users by only showing low/critical warnings once per cycle
Salvador Martinez4387bd52019-02-21 16:16:28 -0800371 if (currentSnapshot.getTimeRemainingMillis()
Salvador Martinez290496e2019-04-04 10:30:19 -0700372 <= currentSnapshot.getSevereThresholdMillis()
373 || currentSnapshot.getBatteryLevel()
374 <= currentSnapshot.getSevereLevelThreshold()) {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800375 mSevereWarningShownThisChargeCycle = true;
376 mLowWarningShownThisChargeCycle = true;
377 if (DEBUG) {
378 Slog.d(TAG, "Severe warning marked as shown this cycle");
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700379 }
Salvador Martinez4387bd52019-02-21 16:16:28 -0800380 } else {
381 Slog.d(TAG, "Low warning marked as shown this cycle");
382 mLowWarningShownThisChargeCycle = true;
Salvador Martinez36307962018-02-08 14:29:08 -0800383 }
Salvador Martinez4387bd52019-02-21 16:16:28 -0800384 } else if (shouldDismissHybridWarning(currentSnapshot)) {
385 if (DEBUG) {
386 Slog.d(TAG, "Dismissing warning");
387 }
388 mWarnings.dismissLowBatteryWarning();
389 } else {
390 if (DEBUG) {
391 Slog.d(TAG, "Updating warning");
392 }
393 mWarnings.updateLowBatteryWarning();
394 }
395 }
396
397 @VisibleForTesting
398 boolean shouldShowHybridWarning(BatteryStateSnapshot snapshot) {
399 if (snapshot.getPlugged()
400 || snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN) {
401 Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged()
402 + " status unknown: "
403 + (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN));
404 return false;
405 }
406
Salvador Martinezd73c5aa2019-03-26 12:38:09 -0700407 // Only show the low warning if enabled once per charge cycle & no battery saver
408 final boolean canShowWarning = snapshot.isLowWarningEnabled()
409 && !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
Salvador Martinez4387bd52019-02-21 16:16:28 -0800410 && (snapshot.getTimeRemainingMillis() < snapshot.getLowThresholdMillis()
411 || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());
412
413 // Only show the severe warning once per charge cycle
414 final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
415 && (snapshot.getTimeRemainingMillis() < snapshot.getSevereThresholdMillis()
416 || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());
417
418 final boolean canShow = canShowWarning || canShowSevereWarning;
Salvador Martinezd73c5aa2019-03-26 12:38:09 -0700419
Salvador Martinez4387bd52019-02-21 16:16:28 -0800420 if (DEBUG) {
421 Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:"
422 + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
423 + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
424 + "\n" + snapshot.toString());
425 }
426 return canShow;
427 }
428
429 @VisibleForTesting
430 boolean shouldDismissHybridWarning(BatteryStateSnapshot snapshot) {
431 return snapshot.getPlugged()
432 || snapshot.getTimeRemainingMillis() > snapshot.getLowThresholdMillis();
433 }
434
435 protected void maybeShowBatteryWarning(
436 BatteryStateSnapshot currentSnapshot,
437 BatteryStateSnapshot lastSnapshot) {
438 final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
439 || lastSnapshot.getPlugged();
440
441 if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {
442 mWarnings.showLowBatteryWarning(playSound);
443 } else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {
Salvador Martinezf9e47502018-01-04 13:45:48 -0800444 mWarnings.dismissLowBatteryWarning();
445 } else {
446 mWarnings.updateLowBatteryWarning();
447 }
448 }
449
450 @VisibleForTesting
Salvador Martinez4387bd52019-02-21 16:16:28 -0800451 boolean shouldShowLowBatteryWarning(
452 BatteryStateSnapshot currentSnapshot,
453 BatteryStateSnapshot lastSnapshot) {
454 return !currentSnapshot.getPlugged()
455 && !currentSnapshot.isPowerSaver()
456 && (((currentSnapshot.getBucket() < lastSnapshot.getBucket()
457 || lastSnapshot.getPlugged())
458 && currentSnapshot.getBucket() < 0))
459 && currentSnapshot.getBatteryStatus() != BatteryManager.BATTERY_STATUS_UNKNOWN;
Salvador Martinezf9e47502018-01-04 13:45:48 -0800460 }
461
Salvador Martinezf9e47502018-01-04 13:45:48 -0800462 @VisibleForTesting
Salvador Martinez4387bd52019-02-21 16:16:28 -0800463 boolean shouldDismissLowBatteryWarning(
464 BatteryStateSnapshot currentSnapshot,
465 BatteryStateSnapshot lastSnapshot) {
466 return currentSnapshot.isPowerSaver()
467 || currentSnapshot.getPlugged()
468 || (currentSnapshot.getBucket() > lastSnapshot.getBucket()
469 && currentSnapshot.getBucket() > 0);
Salvador Martinez36307962018-02-08 14:29:08 -0800470 }
471
Sherry Huang18d27042019-03-19 21:45:28 +0800472 private void initThermalEventListeners() {
473 doSkinThermalEventListenerRegistration();
474 doUsbThermalEventListenerRegistration();
475 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800476
Sherry Huang18d27042019-03-19 21:45:28 +0800477 @VisibleForTesting
478 synchronized void doSkinThermalEventListenerRegistration() {
479 final boolean oldEnableSkinTemperatureWarning = mEnableSkinTemperatureWarning;
480 boolean ret = false;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700481
Sherry Huang18d27042019-03-19 21:45:28 +0800482 mEnableSkinTemperatureWarning = Settings.Global.getInt(mContext.getContentResolver(),
483 Settings.Global.SHOW_TEMPERATURE_WARNING,
484 mContext.getResources().getInteger(R.integer.config_showTemperatureWarning)) != 0;
Todd Poynorfb95e122017-10-04 13:00:32 -0700485
Sherry Huang18d27042019-03-19 21:45:28 +0800486 if (mEnableSkinTemperatureWarning != oldEnableSkinTemperatureWarning) {
487 try {
488 if (mSkinThermalEventListener == null) {
489 mSkinThermalEventListener = new SkinThermalEventListener();
490 }
491 if (mThermalService == null) {
492 mThermalService = IThermalService.Stub.asInterface(
493 ServiceManager.getService(Context.THERMAL_SERVICE));
494 }
495 if (mEnableSkinTemperatureWarning) {
496 ret = mThermalService.registerThermalEventListenerWithType(
497 mSkinThermalEventListener, Temperature.TYPE_SKIN);
498 } else {
499 ret = mThermalService.unregisterThermalEventListener(mSkinThermalEventListener);
500 }
501 } catch (RemoteException e) {
502 Slog.e(TAG, "Exception while (un)registering skin thermal event listener.", e);
503 }
504
505 if (!ret) {
506 mEnableSkinTemperatureWarning = !mEnableSkinTemperatureWarning;
507 Slog.e(TAG, "Failed to register or unregister skin thermal event listener.");
Todd Poynorfb95e122017-10-04 13:00:32 -0700508 }
509 }
Sherry Huangce02ed32019-01-17 20:37:29 +0800510 }
Todd Poynorfb95e122017-10-04 13:00:32 -0700511
Sherry Huangce02ed32019-01-17 20:37:29 +0800512 @VisibleForTesting
Sherry Huang18d27042019-03-19 21:45:28 +0800513 synchronized void doUsbThermalEventListenerRegistration() {
514 final boolean oldEnableUsbTemperatureAlarm = mEnableUsbTemperatureAlarm;
515 boolean ret = false;
516
517 mEnableUsbTemperatureAlarm = Settings.Global.getInt(mContext.getContentResolver(),
518 Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
519 mContext.getResources().getInteger(R.integer.config_showUsbPortAlarm)) != 0;
520
521 if (mEnableUsbTemperatureAlarm != oldEnableUsbTemperatureAlarm) {
522 try {
523 if (mUsbThermalEventListener == null) {
524 mUsbThermalEventListener = new UsbThermalEventListener();
525 }
526 if (mThermalService == null) {
527 mThermalService = IThermalService.Stub.asInterface(
528 ServiceManager.getService(Context.THERMAL_SERVICE));
529 }
530 if (mEnableUsbTemperatureAlarm) {
531 ret = mThermalService.registerThermalEventListenerWithType(
532 mUsbThermalEventListener, Temperature.TYPE_USB_PORT);
533 } else {
534 ret = mThermalService.unregisterThermalEventListener(mUsbThermalEventListener);
535 }
536 } catch (RemoteException e) {
537 Slog.e(TAG, "Exception while (un)registering usb thermal event listener.", e);
Sherry Huangce02ed32019-01-17 20:37:29 +0800538 }
Sherry Huang18d27042019-03-19 21:45:28 +0800539
540 if (!ret) {
541 mEnableUsbTemperatureAlarm = !mEnableUsbTemperatureAlarm;
542 Slog.e(TAG, "Failed to register or unregister usb thermal event listener.");
Sherry Huangce02ed32019-01-17 20:37:29 +0800543 }
Sherry Huangce02ed32019-01-17 20:37:29 +0800544 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800545 }
546
Sherry Huang9b786732019-08-03 02:36:44 +0800547 private void showWarnOnThermalShutdown() {
548 int bootCount = -1;
549 int lastReboot = mContext.getSharedPreferences(PREFS, 0).getInt(BOOT_COUNT_KEY, -1);
550 try {
551 bootCount = Settings.Global.getInt(mContext.getContentResolver(),
552 Settings.Global.BOOT_COUNT);
553 } catch (Settings.SettingNotFoundException e) {
554 Slog.e(TAG, "Failed to read system boot count from Settings.Global.BOOT_COUNT");
555 }
556 // Only show the thermal shutdown warning when there is a thermal reboot.
557 if (bootCount > lastReboot) {
558 mContext.getSharedPreferences(PREFS, 0).edit().putInt(BOOT_COUNT_KEY,
559 bootCount).apply();
560 if (mPowerManager.getLastShutdownReason()
561 == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
562 mWarnings.showThermalShutdownWarning();
563 }
Salvador Martineza6f7b252017-04-10 10:46:15 -0700564 }
565 }
566
Joe Onorato10523b4d2010-10-25 10:42:46 -0700567 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700568 pw.print("mLowBatteryAlertCloseLevel=");
569 pw.println(mLowBatteryAlertCloseLevel);
570 pw.print("mLowBatteryReminderLevels=");
571 pw.println(Arrays.toString(mLowBatteryReminderLevels));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700572 pw.print("mBatteryLevel=");
573 pw.println(Integer.toString(mBatteryLevel));
574 pw.print("mBatteryStatus=");
575 pw.println(Integer.toString(mBatteryStatus));
576 pw.print("mPlugType=");
577 pw.println(Integer.toString(mPlugType));
578 pw.print("mInvalidCharger=");
579 pw.println(Integer.toString(mInvalidCharger));
Daniel Sandlerdea64622013-09-23 16:05:57 -0400580 pw.print("mScreenOffTime=");
581 pw.print(mScreenOffTime);
582 if (mScreenOffTime >= 0) {
583 pw.print(" (");
584 pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
585 pw.print(" ago)");
586 }
587 pw.println();
588 pw.print("soundTimeout=");
589 pw.println(Settings.Global.getInt(mContext.getContentResolver(),
590 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700591 pw.print("bucket: ");
592 pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
Sherry Huangce02ed32019-01-17 20:37:29 +0800593 pw.print("mEnableSkinTemperatureWarning=");
594 pw.println(mEnableSkinTemperatureWarning);
595 pw.print("mEnableUsbTemperatureAlarm=");
596 pw.println(mEnableUsbTemperatureAlarm);
John Spurlocked452c52014-03-06 12:02:31 -0500597 mWarnings.dump(pw);
598 }
599
Salvador Martinez4387bd52019-02-21 16:16:28 -0800600 /**
601 * The interface to allow PowerUI to communicate with whatever implementation of WarningsUI
602 * is being used by the system.
603 */
John Spurlocked452c52014-03-06 12:02:31 -0500604 public interface WarningsUI {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800605
606 /**
607 * Updates battery and screen info for determining whether to trigger battery warnings or
608 * not.
609 * @param batteryLevel The current battery level
610 * @param bucket The current battery bucket
611 * @param screenOffTime How long the screen has been off in millis
612 */
John Spurlocked452c52014-03-06 12:02:31 -0500613 void update(int batteryLevel, int bucket, long screenOffTime);
Sherry Huangce02ed32019-01-17 20:37:29 +0800614
John Spurlocked452c52014-03-06 12:02:31 -0500615 void dismissLowBatteryWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800616
John Spurlocked452c52014-03-06 12:02:31 -0500617 void showLowBatteryWarning(boolean playSound);
Sherry Huangce02ed32019-01-17 20:37:29 +0800618
John Spurlocked452c52014-03-06 12:02:31 -0500619 void dismissInvalidChargerWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800620
John Spurlocked452c52014-03-06 12:02:31 -0500621 void showInvalidChargerWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800622
John Spurlocked452c52014-03-06 12:02:31 -0500623 void updateLowBatteryWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800624
John Spurlocked452c52014-03-06 12:02:31 -0500625 boolean isInvalidChargerWarningShowing();
Sherry Huangce02ed32019-01-17 20:37:29 +0800626
Salvador Martineza6f7b252017-04-10 10:46:15 -0700627 void dismissHighTemperatureWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800628
Salvador Martineza6f7b252017-04-10 10:46:15 -0700629 void showHighTemperatureWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800630
631 /**
Sherry Huang18d27042019-03-19 21:45:28 +0800632 * Display USB port overheat alarm
Sherry Huangce02ed32019-01-17 20:37:29 +0800633 */
634 void showUsbHighTemperatureAlarm();
635
Salvador Martineza6f7b252017-04-10 10:46:15 -0700636 void showThermalShutdownWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800637
John Spurlocked452c52014-03-06 12:02:31 -0500638 void dump(PrintWriter pw);
Sherry Huangce02ed32019-01-17 20:37:29 +0800639
John Spurlockecbc5e82014-10-22 09:05:51 -0400640 void userSwitched();
Salvador Martinez4387bd52019-02-21 16:16:28 -0800641
642 /**
643 * Updates the snapshot of battery state used for evaluating battery warnings
644 * @param snapshot object containing relevant values for making battery warning decisions.
645 */
646 void updateSnapshot(BatteryStateSnapshot snapshot);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700647 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700648
Sherry Huang18d27042019-03-19 21:45:28 +0800649 // Skin thermal event received from thermal service manager subsystem
Sherry Huangce02ed32019-01-17 20:37:29 +0800650 @VisibleForTesting
Sherry Huang18d27042019-03-19 21:45:28 +0800651 final class SkinThermalEventListener extends IThermalEventListener.Stub {
Wei Wangbad7c202018-11-01 11:57:39 -0700652 @Override public void notifyThrottling(Temperature temp) {
Sherry Huangce02ed32019-01-17 20:37:29 +0800653 int status = temp.getStatus();
654
655 if (status >= Temperature.THROTTLING_EMERGENCY) {
656 StatusBar statusBar = getComponent(StatusBar.class);
657 if (statusBar != null && !statusBar.isDeviceInVrMode()) {
658 mWarnings.showHighTemperatureWarning();
Sherry Huang18d27042019-03-19 21:45:28 +0800659 Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
Sherry Huangce02ed32019-01-17 20:37:29 +0800660 + ", current skin status = " + status
661 + ", temperature = " + temp.getValue());
662 }
663 } else {
664 mWarnings.dismissHighTemperatureWarning();
665 }
666 }
667 }
668
Sherry Huang18d27042019-03-19 21:45:28 +0800669 // Usb thermal event received from thermal service manager subsystem
Sherry Huangce02ed32019-01-17 20:37:29 +0800670 @VisibleForTesting
Sherry Huang18d27042019-03-19 21:45:28 +0800671 final class UsbThermalEventListener extends IThermalEventListener.Stub {
Sherry Huangce02ed32019-01-17 20:37:29 +0800672 @Override public void notifyThrottling(Temperature temp) {
673 int status = temp.getStatus();
674
675 if (status >= Temperature.THROTTLING_EMERGENCY) {
676 mWarnings.showUsbHighTemperatureAlarm();
Sherry Huang18d27042019-03-19 21:45:28 +0800677 Slog.d(TAG, "UsbThermalEventListener: notifyThrottling was called "
Sherry Huangce02ed32019-01-17 20:37:29 +0800678 + ", current usb port status = " + status
679 + ", temperature = " + temp.getValue());
680 }
Todd Poynorfb95e122017-10-04 13:00:32 -0700681 }
682 }
683}