blob: 18638606a2518291aaab782c19fd449f362e0797 [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;
Andrew Sappersteine3352562017-01-20 15:41:03 -080026import android.content.res.Resources;
Dianne Hackborn14272302014-06-10 23:13:02 -070027import android.database.ContentObserver;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070028import android.os.BatteryManager;
Joe Onorato10523b4d2010-10-25 10:42:46 -070029import android.os.Handler;
Todd Poynorfb95e122017-10-04 13:00:32 -070030import android.os.IBinder;
31import android.os.IThermalEventListener;
32import android.os.IThermalService;
Daniel Sandlerdea64622013-09-23 16:05:57 -040033import android.os.PowerManager;
Todd Poynorfb95e122017-10-04 13:00:32 -070034import android.os.RemoteException;
35import android.os.ServiceManager;
Daniel Sandlerdea64622013-09-23 16:05:57 -040036import android.os.SystemClock;
Todd Poynorfb95e122017-10-04 13:00:32 -070037import android.os.Temperature;
Dianne Hackborn14272302014-06-10 23:13:02 -070038import android.os.UserHandle;
Joe Onorato10523b4d2010-10-25 10:42:46 -070039import android.provider.Settings;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080040import android.text.format.DateUtils;
John Spurlock1bb480a2014-08-02 17:12:43 -040041import android.util.Log;
Daniel Sandlerdea64622013-09-23 16:05:57 -040042import android.util.Slog;
Jason Monkd819c312017-08-11 12:53:36 -040043
44import com.android.internal.annotations.VisibleForTesting;
Salvador Martinez110f9a12018-01-31 09:57:17 -080045import com.android.settingslib.utils.ThreadUtils;
Jason Monkd819c312017-08-11 12:53:36 -040046import com.android.systemui.Dependency;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080047import com.android.systemui.R;
Joe Onorato10523b4d2010-10-25 10:42:46 -070048import com.android.systemui.SystemUI;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050049import com.android.systemui.statusbar.phone.StatusBar;
Jason Monkd819c312017-08-11 12:53:36 -040050
John Spurlockde84f0e2013-06-12 12:41:00 -040051import java.io.FileDescriptor;
52import java.io.PrintWriter;
Salvador Martinezfd38aa52018-03-28 23:56:59 -070053import java.time.Duration;
John Spurlockde84f0e2013-06-12 12:41:00 -040054import java.util.Arrays;
Salvador Martinez7ad2c172019-02-11 16:09:28 -080055import java.util.concurrent.Future;
John Spurlockde84f0e2013-06-12 12:41:00 -040056
Joe Onorato10523b4d2010-10-25 10:42:46 -070057public class PowerUI extends SystemUI {
Salvador Martinez4387bd52019-02-21 16:16:28 -080058
Joe Onorato10523b4d2010-10-25 10:42:46 -070059 static final String TAG = "PowerUI";
John Spurlock1bb480a2014-08-02 17:12:43 -040060 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080061 private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -080062 private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
63 private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
Salvador Martinezf9e47502018-01-04 13:45:48 -080064 static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
Salvador Martinezfd38aa52018-03-28 23:56:59 -070065 private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
66 private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
Salvador Martinez4387bd52019-02-21 16:16:28 -080067 public static final int NO_ESTIMATE_AVAILABLE = -1;
Joe Onorato10523b4d2010-10-25 10:42:46 -070068
John Spurlocked452c52014-03-06 12:02:31 -050069 private final Handler mHandler = new Handler();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -040070 @VisibleForTesting
71 final Receiver mReceiver = new Receiver();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070072
John Spurlock3332ba52014-03-10 17:44:07 -040073 private PowerManager mPowerManager;
74 private WarningsUI mWarnings;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070075 private final Configuration mLastConfiguration = new Configuration();
John Spurlocked452c52014-03-06 12:02:31 -050076 private int mPlugType = 0;
77 private int mInvalidCharger = 0;
Salvador Martinezf9e47502018-01-04 13:45:48 -080078 private EnhancedEstimates mEnhancedEstimates;
Salvador Martinez7ad2c172019-02-11 16:09:28 -080079 private Future mLastShowWarningTask;
Sherry Huangce02ed32019-01-17 20:37:29 +080080 private boolean mEnableSkinTemperatureWarning;
81 private boolean mEnableUsbTemperatureAlarm;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070082
John Spurlocked452c52014-03-06 12:02:31 -050083 private int mLowBatteryAlertCloseLevel;
84 private final int[] mLowBatteryReminderLevels = new int[2];
Joe Onorato10523b4d2010-10-25 10:42:46 -070085
Daniel Sandlerdea64622013-09-23 16:05:57 -040086 private long mScreenOffTime = -1;
87
Salvador Martinez4387bd52019-02-21 16:16:28 -080088 @VisibleForTesting boolean mLowWarningShownThisChargeCycle;
89 @VisibleForTesting boolean mSevereWarningShownThisChargeCycle;
90 @VisibleForTesting BatteryStateSnapshot mCurrentBatteryStateSnapshot;
91 @VisibleForTesting BatteryStateSnapshot mLastBatteryStateSnapshot;
Wei Wangbf05e602018-11-21 11:46:48 -080092 @VisibleForTesting IThermalService mThermalService;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080093
Salvador Martinezfd38aa52018-03-28 23:56:59 -070094 @VisibleForTesting int mBatteryLevel = 100;
Salvador Martinez36307962018-02-08 14:29:08 -080095 @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
96
Joe Onorato10523b4d2010-10-25 10:42:46 -070097 public void start() {
John Spurlock3332ba52014-03-10 17:44:07 -040098 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
99 mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
Jason Monkd819c312017-08-11 12:53:36 -0400100 mWarnings = Dependency.get(WarningsUI.class);
Salvador Martinezf9e47502018-01-04 13:45:48 -0800101 mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700102 mLastConfiguration.setTo(mContext.getResources().getConfiguration());
Daniel Sandlerdea64622013-09-23 16:05:57 -0400103
Dianne Hackborn14272302014-06-10 23:13:02 -0700104 ContentObserver obs = new ContentObserver(mHandler) {
105 @Override
106 public void onChange(boolean selfChange) {
107 updateBatteryWarningLevels();
108 }
109 };
110 final ContentResolver resolver = mContext.getContentResolver();
111 resolver.registerContentObserver(Settings.Global.getUriFor(
112 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
113 false, obs, UserHandle.USER_ALL);
114 updateBatteryWarningLevels();
John Spurlock3332ba52014-03-10 17:44:07 -0400115 mReceiver.init();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800116
Salvador Martineza6f7b252017-04-10 10:46:15 -0700117 // Check to see if we need to let the user know that the phone previously shut down due
118 // to the temperature being too high.
119 showThermalShutdownDialog();
120
Sherry Huangce02ed32019-01-17 20:37:29 +0800121 initTemperature();
John Spurlock3332ba52014-03-10 17:44:07 -0400122 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700123
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700124 @Override
125 protected void onConfigurationChanged(Configuration newConfig) {
126 final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
127
128 // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
129 if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
Sherry Huangce02ed32019-01-17 20:37:29 +0800130 mHandler.post(this::initTemperature);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700131 }
132 }
133
Dianne Hackborn14272302014-06-10 23:13:02 -0700134 void updateBatteryWarningLevels() {
135 int critLevel = mContext.getResources().getInteger(
136 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Makoto Onuki16a0dd22018-03-20 10:40:37 -0700137 int warnLevel = mContext.getResources().getInteger(
Dianne Hackborn14272302014-06-10 23:13:02 -0700138 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Makoto Onuki4a9036b2018-01-11 14:48:20 -0800139
Dianne Hackborn14272302014-06-10 23:13:02 -0700140 if (warnLevel < critLevel) {
141 warnLevel = critLevel;
142 }
143
144 mLowBatteryReminderLevels[0] = warnLevel;
145 mLowBatteryReminderLevels[1] = critLevel;
146 mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
147 + mContext.getResources().getInteger(
148 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
149 }
150
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700151 /**
152 * Buckets the battery level.
153 *
154 * The code in this function is a little weird because I couldn't comprehend
155 * the bucket going up when the battery level was going down. --joeo
156 *
157 * 1 means that the battery is "ok"
158 * 0 means that the battery is between "ok" and what we should warn about.
159 * less than 0 means that the battery is low
160 */
161 private int findBatteryLevelBucket(int level) {
162 if (level >= mLowBatteryAlertCloseLevel) {
163 return 1;
164 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700165 if (level > mLowBatteryReminderLevels[0]) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700166 return 0;
167 }
168 final int N = mLowBatteryReminderLevels.length;
169 for (int i=N-1; i>=0; i--) {
170 if (level <= mLowBatteryReminderLevels[i]) {
171 return -1-i;
172 }
173 }
174 throw new RuntimeException("not possible!");
175 }
176
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400177 @VisibleForTesting
178 final class Receiver extends BroadcastReceiver {
John Spurlock3332ba52014-03-10 17:44:07 -0400179
180 public void init() {
181 // Register for Intent broadcasts for...
182 IntentFilter filter = new IntentFilter();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400183 filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
John Spurlock3332ba52014-03-10 17:44:07 -0400184 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
185 filter.addAction(Intent.ACTION_SCREEN_OFF);
186 filter.addAction(Intent.ACTION_SCREEN_ON);
John Spurlockecbc5e82014-10-22 09:05:51 -0400187 filter.addAction(Intent.ACTION_USER_SWITCHED);
John Spurlock3332ba52014-03-10 17:44:07 -0400188 mContext.registerReceiver(this, filter, null, mHandler);
John Spurlock3332ba52014-03-10 17:44:07 -0400189 }
190
Joe Onorato10523b4d2010-10-25 10:42:46 -0700191 @Override
192 public void onReceive(Context context, Intent intent) {
193 String action = intent.getAction();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400194 if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
195 ThreadUtils.postOnBackgroundThread(() -> {
196 if (mPowerManager.isPowerSaveMode()) {
197 mWarnings.dismissLowBatteryWarning();
198 }
199 });
200 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700201 final int oldBatteryLevel = mBatteryLevel;
202 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
203 final int oldBatteryStatus = mBatteryStatus;
204 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
205 BatteryManager.BATTERY_STATUS_UNKNOWN);
206 final int oldPlugType = mPlugType;
207 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
208 final int oldInvalidCharger = mInvalidCharger;
209 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
Salvador Martinez4387bd52019-02-21 16:16:28 -0800210 mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700211
212 final boolean plugged = mPlugType != 0;
213 final boolean oldPlugged = oldPlugType != 0;
214
215 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
216 int bucket = findBatteryLevelBucket(mBatteryLevel);
217
Daniel Sandler71986622011-07-26 13:06:49 -0400218 if (DEBUG) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400219 Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700220 + " .. " + mLowBatteryReminderLevels[0]
221 + " .. " + mLowBatteryReminderLevels[1]);
Daniel Sandlerdea64622013-09-23 16:05:57 -0400222 Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
223 Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
224 Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
225 Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
226 Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
227 Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700228 }
229
John Spurlocked452c52014-03-06 12:02:31 -0500230 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700231 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400232 Slog.d(TAG, "showing invalid charger warning");
John Spurlocked452c52014-03-06 12:02:31 -0500233 mWarnings.showInvalidChargerWarning();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700234 return;
235 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
John Spurlocked452c52014-03-06 12:02:31 -0500236 mWarnings.dismissInvalidChargerWarning();
237 } else if (mWarnings.isInvalidChargerWarningShowing()) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700238 // if invalid charger is showing, don't show low battery
Salvador Martinez4387bd52019-02-21 16:16:28 -0800239 if (DEBUG) {
240 Slog.d(TAG, "Bad Charger");
241 }
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700242 return;
243 }
244
Salvador Martinezf9e47502018-01-04 13:45:48 -0800245 // Show the correct version of low battery warning if needed
Salvador Martinez7ad2c172019-02-11 16:09:28 -0800246 if (mLastShowWarningTask != null) {
247 mLastShowWarningTask.cancel(true);
Salvador Martinez4387bd52019-02-21 16:16:28 -0800248 if (DEBUG) {
249 Slog.d(TAG, "cancelled task");
250 }
Salvador Martinez7ad2c172019-02-11 16:09:28 -0800251 }
252 mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800253 maybeShowBatteryWarningV2(
254 plugged, bucket);
Salvador Martinez110f9a12018-01-31 09:57:17 -0800255 });
Beverly334bc5f2017-07-31 10:37:17 -0400256
Daniel Sandlerdea64622013-09-23 16:05:57 -0400257 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
258 mScreenOffTime = SystemClock.elapsedRealtime();
259 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
260 mScreenOffTime = -1;
John Spurlockecbc5e82014-10-22 09:05:51 -0400261 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
262 mWarnings.userSwitched();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700263 } else {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400264 Slog.w(TAG, "unknown intent: " + intent);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700265 }
266 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800267 }
268
Salvador Martinez4387bd52019-02-21 16:16:28 -0800269 protected void maybeShowBatteryWarningV2(boolean plugged, int bucket) {
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700270 final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
Salvador Martinez4387bd52019-02-21 16:16:28 -0800271 final boolean isPowerSaverMode = mPowerManager.isPowerSaveMode();
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700272
Salvador Martinez4387bd52019-02-21 16:16:28 -0800273 // Stick current battery state into an immutable container to determine if we should show
274 // a warning.
275 if (DEBUG) {
276 Slog.d(TAG, "evaluating which notification to show");
277 }
278 if (hybridEnabled) {
279 if (DEBUG) {
280 Slog.d(TAG, "using hybrid");
281 }
282 Estimate estimate = refreshEstimateIfNeeded();
283 mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
284 plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
285 mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
286 mEnhancedEstimates.getSevereWarningThreshold(),
287 mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage());
288 } else {
289 if (DEBUG) {
290 Slog.d(TAG, "using standard");
291 }
292 mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
293 plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
294 mLowBatteryReminderLevels[0]);
295 }
296
297 mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
298 if (mCurrentBatteryStateSnapshot.isHybrid()) {
299 maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
300 } else {
301 maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
302 }
303 }
304
305 // updates the time estimate if we don't have one or battery level has changed.
306 @VisibleForTesting
307 Estimate refreshEstimateIfNeeded() {
308 if (mLastBatteryStateSnapshot == null
309 || mLastBatteryStateSnapshot.getTimeRemainingMillis() == NO_ESTIMATE_AVAILABLE
310 || mBatteryLevel != mLastBatteryStateSnapshot.getBatteryLevel()) {
311 final Estimate estimate = mEnhancedEstimates.getEstimate();
312 if (DEBUG) {
313 Slog.d(TAG, "updated estimate: " + estimate.getEstimateMillis());
314 }
315 return estimate;
316 }
317 return new Estimate(mLastBatteryStateSnapshot.getTimeRemainingMillis(),
318 mLastBatteryStateSnapshot.isBasedOnUsage());
319 }
320
321 @VisibleForTesting
322 void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
323 BatteryStateSnapshot lastSnapshot) {
324 // if we are now over 45% battery & 6 hours remaining so we can trigger hybrid
325 // notification again
326 if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET
327 && currentSnapshot.getTimeRemainingMillis() > SIX_HOURS_MILLIS) {
328 mLowWarningShownThisChargeCycle = false;
329 mSevereWarningShownThisChargeCycle = false;
330 if (DEBUG) {
331 Slog.d(TAG, "Charge cycle reset! Can show warnings again");
Salvador Martinezf9e47502018-01-04 13:45:48 -0800332 }
333 }
334
Salvador Martinez4387bd52019-02-21 16:16:28 -0800335 final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
336 || lastSnapshot.getPlugged();
Salvador Martinez36307962018-02-08 14:29:08 -0800337
Salvador Martinez4387bd52019-02-21 16:16:28 -0800338 if (shouldShowHybridWarning(currentSnapshot)) {
339 mWarnings.showLowBatteryWarning(playSound);
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700340 // mark if we've already shown a warning this cycle. This will prevent the notification
341 // trigger from spamming users by only showing low/critical warnings once per cycle
Salvador Martinez4387bd52019-02-21 16:16:28 -0800342 if (currentSnapshot.getTimeRemainingMillis()
343 <= currentSnapshot.getSevereLevelThreshold()
344 || currentSnapshot.getBatteryLevel() <= mLowBatteryReminderLevels[1]) {
345 mSevereWarningShownThisChargeCycle = true;
346 mLowWarningShownThisChargeCycle = true;
347 if (DEBUG) {
348 Slog.d(TAG, "Severe warning marked as shown this cycle");
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700349 }
Salvador Martinez4387bd52019-02-21 16:16:28 -0800350 } else {
351 Slog.d(TAG, "Low warning marked as shown this cycle");
352 mLowWarningShownThisChargeCycle = true;
Salvador Martinez36307962018-02-08 14:29:08 -0800353 }
Salvador Martinez4387bd52019-02-21 16:16:28 -0800354
355 } else if (shouldDismissHybridWarning(currentSnapshot)) {
356 if (DEBUG) {
357 Slog.d(TAG, "Dismissing warning");
358 }
359 mWarnings.dismissLowBatteryWarning();
360 } else {
361 if (DEBUG) {
362 Slog.d(TAG, "Updating warning");
363 }
364 mWarnings.updateLowBatteryWarning();
365 }
366 }
367
368 @VisibleForTesting
369 boolean shouldShowHybridWarning(BatteryStateSnapshot snapshot) {
370 if (snapshot.getPlugged()
371 || snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN) {
372 Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged()
373 + " status unknown: "
374 + (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN));
375 return false;
376 }
377
378 // Only show the low warning once per charge cycle & no battery saver
379 final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
380 && (snapshot.getTimeRemainingMillis() < snapshot.getLowThresholdMillis()
381 || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());
382
383 // Only show the severe warning once per charge cycle
384 final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
385 && (snapshot.getTimeRemainingMillis() < snapshot.getSevereThresholdMillis()
386 || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());
387
388 final boolean canShow = canShowWarning || canShowSevereWarning;
389 if (DEBUG) {
390 Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:"
391 + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
392 + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
393 + "\n" + snapshot.toString());
394 }
395 return canShow;
396 }
397
398 @VisibleForTesting
399 boolean shouldDismissHybridWarning(BatteryStateSnapshot snapshot) {
400 return snapshot.getPlugged()
401 || snapshot.getTimeRemainingMillis() > snapshot.getLowThresholdMillis();
402 }
403
404 protected void maybeShowBatteryWarning(
405 BatteryStateSnapshot currentSnapshot,
406 BatteryStateSnapshot lastSnapshot) {
407 final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
408 || lastSnapshot.getPlugged();
409
410 if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {
411 mWarnings.showLowBatteryWarning(playSound);
412 } else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {
Salvador Martinezf9e47502018-01-04 13:45:48 -0800413 mWarnings.dismissLowBatteryWarning();
414 } else {
415 mWarnings.updateLowBatteryWarning();
416 }
417 }
418
419 @VisibleForTesting
Salvador Martinez4387bd52019-02-21 16:16:28 -0800420 boolean shouldShowLowBatteryWarning(
421 BatteryStateSnapshot currentSnapshot,
422 BatteryStateSnapshot lastSnapshot) {
423 return !currentSnapshot.getPlugged()
424 && !currentSnapshot.isPowerSaver()
425 && (((currentSnapshot.getBucket() < lastSnapshot.getBucket()
426 || lastSnapshot.getPlugged())
427 && currentSnapshot.getBucket() < 0))
428 && currentSnapshot.getBatteryStatus() != BatteryManager.BATTERY_STATUS_UNKNOWN;
Salvador Martinezf9e47502018-01-04 13:45:48 -0800429 }
430
Salvador Martinezf9e47502018-01-04 13:45:48 -0800431 @VisibleForTesting
Salvador Martinez4387bd52019-02-21 16:16:28 -0800432 boolean shouldDismissLowBatteryWarning(
433 BatteryStateSnapshot currentSnapshot,
434 BatteryStateSnapshot lastSnapshot) {
435 return currentSnapshot.isPowerSaver()
436 || currentSnapshot.getPlugged()
437 || (currentSnapshot.getBucket() > lastSnapshot.getBucket()
438 && currentSnapshot.getBucket() > 0);
Salvador Martinez36307962018-02-08 14:29:08 -0800439 }
440
Sherry Huangce02ed32019-01-17 20:37:29 +0800441 private void initTemperature() {
Andrew Sappersteine3352562017-01-20 15:41:03 -0800442 ContentResolver resolver = mContext.getContentResolver();
443 Resources resources = mContext.getResources();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800444
Sherry Huangce02ed32019-01-17 20:37:29 +0800445 mEnableSkinTemperatureWarning = Settings.Global.getInt(resolver,
446 Settings.Global.SHOW_TEMPERATURE_WARNING,
447 resources.getInteger(R.integer.config_showTemperatureWarning)) != 0;
448 mEnableUsbTemperatureAlarm = Settings.Global.getInt(resolver,
449 Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
450 resources.getInteger(R.integer.config_showUsbPortAlarm)) != 0;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700451
Todd Poynorfb95e122017-10-04 13:00:32 -0700452 if (mThermalService == null) {
453 // Enable push notifications of throttling from vendor thermal
454 // management subsystem via thermalservice, in addition to our
455 // usual polling, to react to temperature jumps more quickly.
Wei Wangbf05e602018-11-21 11:46:48 -0800456 IBinder b = ServiceManager.getService(Context.THERMAL_SERVICE);
Todd Poynorfb95e122017-10-04 13:00:32 -0700457
458 if (b != null) {
459 mThermalService = IThermalService.Stub.asInterface(b);
Sherry Huangce02ed32019-01-17 20:37:29 +0800460 registerThermalEventListener();
Todd Poynorfb95e122017-10-04 13:00:32 -0700461 } else {
462 Slog.w(TAG, "cannot find thermalservice, no throttling push notifications");
463 }
464 }
Sherry Huangce02ed32019-01-17 20:37:29 +0800465 }
Todd Poynorfb95e122017-10-04 13:00:32 -0700466
Sherry Huangce02ed32019-01-17 20:37:29 +0800467 @VisibleForTesting
468 void registerThermalEventListener() {
469 try {
470 if (mEnableSkinTemperatureWarning) {
471 mThermalService.registerThermalEventListenerWithType(
472 new ThermalEventSkinListener(), Temperature.TYPE_SKIN);
473 }
474 if (mEnableUsbTemperatureAlarm) {
475 mThermalService.registerThermalEventListenerWithType(
476 new ThermalEventUsbListener(), Temperature.TYPE_USB_PORT);
477 }
478 } catch (RemoteException e) {
479 Slog.e(TAG, "Failed to register thermal callback.", e);
480 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800481 }
482
Salvador Martineza6f7b252017-04-10 10:46:15 -0700483 private void showThermalShutdownDialog() {
484 if (mPowerManager.getLastShutdownReason()
485 == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
486 mWarnings.showThermalShutdownWarning();
487 }
488 }
489
Joe Onorato10523b4d2010-10-25 10:42:46 -0700490 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700491 pw.print("mLowBatteryAlertCloseLevel=");
492 pw.println(mLowBatteryAlertCloseLevel);
493 pw.print("mLowBatteryReminderLevels=");
494 pw.println(Arrays.toString(mLowBatteryReminderLevels));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700495 pw.print("mBatteryLevel=");
496 pw.println(Integer.toString(mBatteryLevel));
497 pw.print("mBatteryStatus=");
498 pw.println(Integer.toString(mBatteryStatus));
499 pw.print("mPlugType=");
500 pw.println(Integer.toString(mPlugType));
501 pw.print("mInvalidCharger=");
502 pw.println(Integer.toString(mInvalidCharger));
Daniel Sandlerdea64622013-09-23 16:05:57 -0400503 pw.print("mScreenOffTime=");
504 pw.print(mScreenOffTime);
505 if (mScreenOffTime >= 0) {
506 pw.print(" (");
507 pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
508 pw.print(" ago)");
509 }
510 pw.println();
511 pw.print("soundTimeout=");
512 pw.println(Settings.Global.getInt(mContext.getContentResolver(),
513 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700514 pw.print("bucket: ");
515 pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
Sherry Huangce02ed32019-01-17 20:37:29 +0800516 pw.print("mEnableSkinTemperatureWarning=");
517 pw.println(mEnableSkinTemperatureWarning);
518 pw.print("mEnableUsbTemperatureAlarm=");
519 pw.println(mEnableUsbTemperatureAlarm);
John Spurlocked452c52014-03-06 12:02:31 -0500520 mWarnings.dump(pw);
521 }
522
Salvador Martinez4387bd52019-02-21 16:16:28 -0800523 /**
524 * The interface to allow PowerUI to communicate with whatever implementation of WarningsUI
525 * is being used by the system.
526 */
John Spurlocked452c52014-03-06 12:02:31 -0500527 public interface WarningsUI {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800528
529 /**
530 * Updates battery and screen info for determining whether to trigger battery warnings or
531 * not.
532 * @param batteryLevel The current battery level
533 * @param bucket The current battery bucket
534 * @param screenOffTime How long the screen has been off in millis
535 */
John Spurlocked452c52014-03-06 12:02:31 -0500536 void update(int batteryLevel, int bucket, long screenOffTime);
Sherry Huangce02ed32019-01-17 20:37:29 +0800537
John Spurlocked452c52014-03-06 12:02:31 -0500538 void dismissLowBatteryWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800539
John Spurlocked452c52014-03-06 12:02:31 -0500540 void showLowBatteryWarning(boolean playSound);
Sherry Huangce02ed32019-01-17 20:37:29 +0800541
John Spurlocked452c52014-03-06 12:02:31 -0500542 void dismissInvalidChargerWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800543
John Spurlocked452c52014-03-06 12:02:31 -0500544 void showInvalidChargerWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800545
John Spurlocked452c52014-03-06 12:02:31 -0500546 void updateLowBatteryWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800547
John Spurlocked452c52014-03-06 12:02:31 -0500548 boolean isInvalidChargerWarningShowing();
Sherry Huangce02ed32019-01-17 20:37:29 +0800549
Salvador Martineza6f7b252017-04-10 10:46:15 -0700550 void dismissHighTemperatureWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800551
Salvador Martineza6f7b252017-04-10 10:46:15 -0700552 void showHighTemperatureWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800553
554 /**
555 * Display USB overheat alarm
556 */
557 void showUsbHighTemperatureAlarm();
558
Salvador Martineza6f7b252017-04-10 10:46:15 -0700559 void showThermalShutdownWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800560
John Spurlocked452c52014-03-06 12:02:31 -0500561 void dump(PrintWriter pw);
Sherry Huangce02ed32019-01-17 20:37:29 +0800562
John Spurlockecbc5e82014-10-22 09:05:51 -0400563 void userSwitched();
Salvador Martinez4387bd52019-02-21 16:16:28 -0800564
565 /**
566 * Updates the snapshot of battery state used for evaluating battery warnings
567 * @param snapshot object containing relevant values for making battery warning decisions.
568 */
569 void updateSnapshot(BatteryStateSnapshot snapshot);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700570 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700571
Sherry Huangce02ed32019-01-17 20:37:29 +0800572 // Thermal event received from thermal service manager subsystem
573 @VisibleForTesting
574 final class ThermalEventSkinListener extends IThermalEventListener.Stub {
Wei Wangbad7c202018-11-01 11:57:39 -0700575 @Override public void notifyThrottling(Temperature temp) {
Sherry Huangce02ed32019-01-17 20:37:29 +0800576 int status = temp.getStatus();
577
578 if (status >= Temperature.THROTTLING_EMERGENCY) {
579 StatusBar statusBar = getComponent(StatusBar.class);
580 if (statusBar != null && !statusBar.isDeviceInVrMode()) {
581 mWarnings.showHighTemperatureWarning();
582 Slog.d(TAG, "ThermalEventSkinListener: notifyThrottling was called "
583 + ", current skin status = " + status
584 + ", temperature = " + temp.getValue());
585 }
586 } else {
587 mWarnings.dismissHighTemperatureWarning();
588 }
589 }
590 }
591
592 // Thermal event received from thermal service manager subsystem
593 @VisibleForTesting
594 final class ThermalEventUsbListener extends IThermalEventListener.Stub {
595 @Override public void notifyThrottling(Temperature temp) {
596 int status = temp.getStatus();
597
598 if (status >= Temperature.THROTTLING_EMERGENCY) {
599 mWarnings.showUsbHighTemperatureAlarm();
600 Slog.d(TAG, "ThermalEventUsbListener: notifyThrottling was called "
601 + ", current usb port status = " + status
602 + ", temperature = " + temp.getValue());
603 }
Todd Poynorfb95e122017-10-04 13:00:32 -0700604 }
605 }
606}