blob: c409f738ec2a1b4fcae05a67208640b1e3c5b28a [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;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080030import android.os.HardwarePropertiesManager;
Todd Poynorfb95e122017-10-04 13:00:32 -070031import android.os.IBinder;
32import android.os.IThermalEventListener;
33import android.os.IThermalService;
Daniel Sandlerdea64622013-09-23 16:05:57 -040034import android.os.PowerManager;
Todd Poynorfb95e122017-10-04 13:00:32 -070035import android.os.RemoteException;
36import android.os.ServiceManager;
Daniel Sandlerdea64622013-09-23 16:05:57 -040037import android.os.SystemClock;
Todd Poynorfb95e122017-10-04 13:00:32 -070038import android.os.Temperature;
Dianne Hackborn14272302014-06-10 23:13:02 -070039import android.os.UserHandle;
Joe Onorato10523b4d2010-10-25 10:42:46 -070040import android.provider.Settings;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080041import android.text.format.DateUtils;
John Spurlock1bb480a2014-08-02 17:12:43 -040042import android.util.Log;
Daniel Sandlerdea64622013-09-23 16:05:57 -040043import android.util.Slog;
Jason Monkd819c312017-08-11 12:53:36 -040044
45import com.android.internal.annotations.VisibleForTesting;
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -080046import com.android.internal.logging.MetricsLogger;
Salvador Martinez110f9a12018-01-31 09:57:17 -080047import com.android.settingslib.utils.ThreadUtils;
Jason Monkd819c312017-08-11 12:53:36 -040048import com.android.systemui.Dependency;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080049import com.android.systemui.R;
Joe Onorato10523b4d2010-10-25 10:42:46 -070050import com.android.systemui.SystemUI;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050051import com.android.systemui.statusbar.phone.StatusBar;
Jason Monkd819c312017-08-11 12:53:36 -040052
John Spurlockde84f0e2013-06-12 12:41:00 -040053import java.io.FileDescriptor;
54import java.io.PrintWriter;
Salvador Martinezfd38aa52018-03-28 23:56:59 -070055import java.time.Duration;
John Spurlockde84f0e2013-06-12 12:41:00 -040056import java.util.Arrays;
57
Joe Onorato10523b4d2010-10-25 10:42:46 -070058public class PowerUI extends SystemUI {
59 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();
Joe Onorato10523b4d2010-10-25 10:42:46 -070067
John Spurlocked452c52014-03-06 12:02:31 -050068 private final Handler mHandler = new Handler();
John Spurlock3332ba52014-03-10 17:44:07 -040069 private final Receiver mReceiver = new Receiver();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070070
John Spurlock3332ba52014-03-10 17:44:07 -040071 private PowerManager mPowerManager;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080072 private HardwarePropertiesManager mHardwarePropertiesManager;
John Spurlock3332ba52014-03-10 17:44:07 -040073 private WarningsUI mWarnings;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070074 private final Configuration mLastConfiguration = new Configuration();
Salvador Martinezf9e47502018-01-04 13:45:48 -080075 private long mTimeRemaining = Long.MAX_VALUE;
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 Martinez36307962018-02-08 14:29:08 -080079 private boolean mLowWarningShownThisChargeCycle;
80 private boolean mSevereWarningShownThisChargeCycle;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070081
John Spurlocked452c52014-03-06 12:02:31 -050082 private int mLowBatteryAlertCloseLevel;
83 private final int[] mLowBatteryReminderLevels = new int[2];
Joe Onorato10523b4d2010-10-25 10:42:46 -070084
Daniel Sandlerdea64622013-09-23 16:05:57 -040085 private long mScreenOffTime = -1;
86
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -080087 private float mThresholdTemp;
88 private float[] mRecentTemps = new float[MAX_RECENT_TEMPS];
89 private int mNumTemps;
90 private long mNextLogTime;
Todd Poynorfb95e122017-10-04 13:00:32 -070091 private IThermalService mThermalService;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080092
Salvador Martinezfd38aa52018-03-28 23:56:59 -070093 @VisibleForTesting int mBatteryLevel = 100;
Salvador Martinez36307962018-02-08 14:29:08 -080094 @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
95
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070096 // by using the same instance (method references are not guaranteed to be the same object
Salvador Martinezf9e47502018-01-04 13:45:48 -080097 // We create a method reference here so that we are guaranteed that we can remove a callback
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070098 // each time they are created).
99 private final Runnable mUpdateTempCallback = this::updateTemperatureWarning;
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);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800103 mHardwarePropertiesManager = (HardwarePropertiesManager)
104 mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE);
John Spurlock3332ba52014-03-10 17:44:07 -0400105 mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
Jason Monkd819c312017-08-11 12:53:36 -0400106 mWarnings = Dependency.get(WarningsUI.class);
Salvador Martinezf9e47502018-01-04 13:45:48 -0800107 mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700108 mLastConfiguration.setTo(mContext.getResources().getConfiguration());
Daniel Sandlerdea64622013-09-23 16:05:57 -0400109
Dianne Hackborn14272302014-06-10 23:13:02 -0700110 ContentObserver obs = new ContentObserver(mHandler) {
111 @Override
112 public void onChange(boolean selfChange) {
113 updateBatteryWarningLevels();
114 }
115 };
116 final ContentResolver resolver = mContext.getContentResolver();
117 resolver.registerContentObserver(Settings.Global.getUriFor(
118 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
119 false, obs, UserHandle.USER_ALL);
120 updateBatteryWarningLevels();
John Spurlock3332ba52014-03-10 17:44:07 -0400121 mReceiver.init();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800122
Salvador Martineza6f7b252017-04-10 10:46:15 -0700123 // Check to see if we need to let the user know that the phone previously shut down due
124 // to the temperature being too high.
125 showThermalShutdownDialog();
126
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800127 initTemperatureWarning();
John Spurlock3332ba52014-03-10 17:44:07 -0400128 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700129
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700130 @Override
131 protected void onConfigurationChanged(Configuration newConfig) {
132 final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
133
134 // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
135 if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
136 mHandler.post(this::initTemperatureWarning);
137 }
138 }
139
Dianne Hackborn14272302014-06-10 23:13:02 -0700140 void updateBatteryWarningLevels() {
141 int critLevel = mContext.getResources().getInteger(
142 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Makoto Onuki16a0dd22018-03-20 10:40:37 -0700143 int warnLevel = mContext.getResources().getInteger(
Dianne Hackborn14272302014-06-10 23:13:02 -0700144 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Makoto Onuki4a9036b2018-01-11 14:48:20 -0800145
Dianne Hackborn14272302014-06-10 23:13:02 -0700146 if (warnLevel < critLevel) {
147 warnLevel = critLevel;
148 }
149
150 mLowBatteryReminderLevels[0] = warnLevel;
151 mLowBatteryReminderLevels[1] = critLevel;
152 mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
153 + mContext.getResources().getInteger(
154 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
155 }
156
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700157 /**
158 * Buckets the battery level.
159 *
160 * The code in this function is a little weird because I couldn't comprehend
161 * the bucket going up when the battery level was going down. --joeo
162 *
163 * 1 means that the battery is "ok"
164 * 0 means that the battery is between "ok" and what we should warn about.
165 * less than 0 means that the battery is low
166 */
167 private int findBatteryLevelBucket(int level) {
168 if (level >= mLowBatteryAlertCloseLevel) {
169 return 1;
170 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700171 if (level > mLowBatteryReminderLevels[0]) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700172 return 0;
173 }
174 final int N = mLowBatteryReminderLevels.length;
175 for (int i=N-1; i>=0; i--) {
176 if (level <= mLowBatteryReminderLevels[i]) {
177 return -1-i;
178 }
179 }
180 throw new RuntimeException("not possible!");
181 }
182
John Spurlock3332ba52014-03-10 17:44:07 -0400183 private final class Receiver extends BroadcastReceiver {
184
185 public void init() {
186 // Register for Intent broadcasts for...
187 IntentFilter filter = new IntentFilter();
188 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
189 filter.addAction(Intent.ACTION_SCREEN_OFF);
190 filter.addAction(Intent.ACTION_SCREEN_ON);
John Spurlockecbc5e82014-10-22 09:05:51 -0400191 filter.addAction(Intent.ACTION_USER_SWITCHED);
John Spurlock3332ba52014-03-10 17:44:07 -0400192 mContext.registerReceiver(this, filter, null, mHandler);
John Spurlock3332ba52014-03-10 17:44:07 -0400193 }
194
Joe Onorato10523b4d2010-10-25 10:42:46 -0700195 @Override
196 public void onReceive(Context context, Intent intent) {
197 String action = intent.getAction();
198 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700199 final int oldBatteryLevel = mBatteryLevel;
200 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
201 final int oldBatteryStatus = mBatteryStatus;
202 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
203 BatteryManager.BATTERY_STATUS_UNKNOWN);
204 final int oldPlugType = mPlugType;
205 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
206 final int oldInvalidCharger = mInvalidCharger;
207 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
208
209 final boolean plugged = mPlugType != 0;
210 final boolean oldPlugged = oldPlugType != 0;
211
212 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
213 int bucket = findBatteryLevelBucket(mBatteryLevel);
214
Daniel Sandler71986622011-07-26 13:06:49 -0400215 if (DEBUG) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400216 Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700217 + " .. " + mLowBatteryReminderLevels[0]
218 + " .. " + mLowBatteryReminderLevels[1]);
Daniel Sandlerdea64622013-09-23 16:05:57 -0400219 Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
220 Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
221 Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
222 Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
223 Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
224 Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700225 }
226
John Spurlocked452c52014-03-06 12:02:31 -0500227 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700228 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400229 Slog.d(TAG, "showing invalid charger warning");
John Spurlocked452c52014-03-06 12:02:31 -0500230 mWarnings.showInvalidChargerWarning();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700231 return;
232 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
John Spurlocked452c52014-03-06 12:02:31 -0500233 mWarnings.dismissInvalidChargerWarning();
234 } else if (mWarnings.isInvalidChargerWarningShowing()) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700235 // if invalid charger is showing, don't show low battery
236 return;
237 }
238
Salvador Martinezf9e47502018-01-04 13:45:48 -0800239 // Show the correct version of low battery warning if needed
Salvador Martinez110f9a12018-01-31 09:57:17 -0800240 ThreadUtils.postOnBackgroundThread(() -> {
241 maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket);
242 });
Beverly334bc5f2017-07-31 10:37:17 -0400243
Daniel Sandlerdea64622013-09-23 16:05:57 -0400244 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
245 mScreenOffTime = SystemClock.elapsedRealtime();
246 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
247 mScreenOffTime = -1;
John Spurlockecbc5e82014-10-22 09:05:51 -0400248 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
249 mWarnings.userSwitched();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700250 } else {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400251 Slog.w(TAG, "unknown intent: " + intent);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700252 }
253 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800254 }
255
256 protected void maybeShowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
257 int bucket) {
258 boolean isPowerSaver = mPowerManager.isPowerSaveMode();
259 // only play SFX when the dialog comes up or the bucket changes
260 final boolean playSound = bucket != oldBucket || oldPlugged;
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700261 final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
262 if (hybridEnabled) {
Salvador Martinezf9e47502018-01-04 13:45:48 -0800263 final Estimate estimate = mEnhancedEstimates.getEstimate();
264 // Turbo is not always booted once SysUI is running so we have ot make sure we actually
265 // get data back
266 if (estimate != null) {
267 mTimeRemaining = estimate.estimateMillis;
268 mWarnings.updateEstimate(estimate);
Salvador Martinezbb902fc2018-01-22 19:46:55 -0800269 mWarnings.updateThresholds(mEnhancedEstimates.getLowWarningThreshold(),
270 mEnhancedEstimates.getSevereWarningThreshold());
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700271
272 // if we are now over 45% battery & 6 hours remaining we can trigger hybrid
273 // notification again
274 if (mBatteryLevel >= CHARGE_CYCLE_PERCENT_RESET
275 && mTimeRemaining > SIX_HOURS_MILLIS) {
276 mLowWarningShownThisChargeCycle = false;
277 mSevereWarningShownThisChargeCycle = false;
278 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800279 }
280 }
281
Salvador Martinez36307962018-02-08 14:29:08 -0800282 if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket,
283 mTimeRemaining, isPowerSaver, mBatteryStatus)) {
Salvador Martinezf9e47502018-01-04 13:45:48 -0800284 mWarnings.showLowBatteryWarning(playSound);
Salvador Martinez36307962018-02-08 14:29:08 -0800285
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700286 // mark if we've already shown a warning this cycle. This will prevent the notification
287 // trigger from spamming users by only showing low/critical warnings once per cycle
288 if (hybridEnabled) {
289 if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
290 || mBatteryLevel < mLowBatteryReminderLevels[1]) {
291 mSevereWarningShownThisChargeCycle = true;
292 } else {
293 mLowWarningShownThisChargeCycle = true;
294 }
Salvador Martinez36307962018-02-08 14:29:08 -0800295 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800296 } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
297 isPowerSaver)) {
298 mWarnings.dismissLowBatteryWarning();
299 } else {
300 mWarnings.updateLowBatteryWarning();
301 }
302 }
303
304 @VisibleForTesting
305 boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700306 int bucket, long timeRemaining, boolean isPowerSaver, int batteryStatus) {
307 if (mEnhancedEstimates.isHybridNotificationEnabled()) {
308 // triggering logic when enhanced estimate is available
309 return isEnhancedTrigger(plugged, timeRemaining, isPowerSaver, batteryStatus);
310 }
311 // legacy triggering logic
Salvador Martinezf9e47502018-01-04 13:45:48 -0800312 return !plugged
313 && !isPowerSaver
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700314 && (((bucket < oldBucket || oldPlugged) && bucket < 0))
315 && batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
Salvador Martinezf9e47502018-01-04 13:45:48 -0800316 }
317
Salvador Martinezf9e47502018-01-04 13:45:48 -0800318 @VisibleForTesting
319 boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
320 long timeRemaining, boolean isPowerSaver) {
321 final boolean hybridWouldDismiss = mEnhancedEstimates.isHybridNotificationEnabled()
Salvador Martinezbb902fc2018-01-22 19:46:55 -0800322 && timeRemaining > mEnhancedEstimates.getLowWarningThreshold();
Salvador Martinezf9e47502018-01-04 13:45:48 -0800323 final boolean standardWouldDismiss = (bucket > oldBucket && bucket > 0);
324 return isPowerSaver
325 || plugged
326 || (standardWouldDismiss && (!mEnhancedEstimates.isHybridNotificationEnabled()
327 || hybridWouldDismiss));
328 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700329
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700330 private boolean isEnhancedTrigger(boolean plugged, long timeRemaining, boolean isPowerSaver,
331 int batteryStatus) {
332 if (plugged || isPowerSaver || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
Salvador Martinez36307962018-02-08 14:29:08 -0800333 return false;
334 }
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700335 int warnLevel = mLowBatteryReminderLevels[0];
336 int critLevel = mLowBatteryReminderLevels[1];
Salvador Martinez36307962018-02-08 14:29:08 -0800337
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700338 // Only show the low warning once per charge cycle
339 final boolean canShowWarning = !mLowWarningShownThisChargeCycle
340 && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
341 || mBatteryLevel <= warnLevel);
Salvador Martinez36307962018-02-08 14:29:08 -0800342
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700343 // Only show the severe warning once per charge cycle
344 final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
345 && (timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
346 || mBatteryLevel <= critLevel);
Salvador Martinez36307962018-02-08 14:29:08 -0800347
348 return canShowWarning || canShowSevereWarning;
349 }
350
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800351 private void initTemperatureWarning() {
Andrew Sappersteine3352562017-01-20 15:41:03 -0800352 ContentResolver resolver = mContext.getContentResolver();
353 Resources resources = mContext.getResources();
354 if (Settings.Global.getInt(resolver, Settings.Global.SHOW_TEMPERATURE_WARNING,
355 resources.getInteger(R.integer.config_showTemperatureWarning)) == 0) {
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800356 return;
357 }
358
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800359 mThresholdTemp = Settings.Global.getFloat(resolver, Settings.Global.WARNING_TEMPERATURE,
Andrew Sappersteine3352562017-01-20 15:41:03 -0800360 resources.getInteger(R.integer.config_warningTemperature));
Andrew Sapperstein75184712017-01-05 16:45:37 -0800361
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800362 if (mThresholdTemp < 0f) {
Todd Poynorfb95e122017-10-04 13:00:32 -0700363 // Get the shutdown temperature, adjust for warning tolerance.
Andrew Sapperstein75184712017-01-05 16:45:37 -0800364 float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures(
365 HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
Jason Monkd819c312017-08-11 12:53:36 -0400366 HardwarePropertiesManager.TEMPERATURE_SHUTDOWN);
Andrew Sapperstein75184712017-01-05 16:45:37 -0800367 if (throttlingTemps == null
368 || throttlingTemps.length == 0
369 || throttlingTemps[0] == HardwarePropertiesManager.UNDEFINED_TEMPERATURE) {
370 return;
371 }
Jason Monkd819c312017-08-11 12:53:36 -0400372 mThresholdTemp = throttlingTemps[0] -
373 resources.getInteger(R.integer.config_warningTemperatureTolerance);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800374 }
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700375
Todd Poynorfb95e122017-10-04 13:00:32 -0700376 if (mThermalService == null) {
377 // Enable push notifications of throttling from vendor thermal
378 // management subsystem via thermalservice, in addition to our
379 // usual polling, to react to temperature jumps more quickly.
380 IBinder b = ServiceManager.getService("thermalservice");
381
382 if (b != null) {
383 mThermalService = IThermalService.Stub.asInterface(b);
384 try {
385 mThermalService.registerThermalEventListener(
386 new ThermalEventListener());
387 } catch (RemoteException e) {
388 // Should never happen.
389 }
390 } else {
391 Slog.w(TAG, "cannot find thermalservice, no throttling push notifications");
392 }
393 }
394
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800395 setNextLogTime();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800396
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700397 // This initialization method may be called on a configuration change. Only one set of
398 // ongoing callbacks should be occurring, so remove any now. updateTemperatureWarning will
399 // schedule an ongoing callback.
400 mHandler.removeCallbacks(mUpdateTempCallback);
401
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800402 // We have passed all of the checks, start checking the temp
403 updateTemperatureWarning();
404 }
405
Salvador Martineza6f7b252017-04-10 10:46:15 -0700406 private void showThermalShutdownDialog() {
407 if (mPowerManager.getLastShutdownReason()
408 == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
409 mWarnings.showThermalShutdownWarning();
410 }
411 }
412
Jason Monkd819c312017-08-11 12:53:36 -0400413 @VisibleForTesting
414 protected void updateTemperatureWarning() {
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800415 float[] temps = mHardwarePropertiesManager.getDeviceTemperatures(
416 HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
417 HardwarePropertiesManager.TEMPERATURE_CURRENT);
418 if (temps.length != 0) {
419 float temp = temps[0];
420 mRecentTemps[mNumTemps++] = temp;
421
422 StatusBar statusBar = getComponent(StatusBar.class);
423 if (statusBar != null && !statusBar.isDeviceInVrMode()
424 && temp >= mThresholdTemp) {
425 logAtTemperatureThreshold(temp);
Salvador Martineza6f7b252017-04-10 10:46:15 -0700426 mWarnings.showHighTemperatureWarning();
Andrew Sapperstein65d8a5f2016-12-19 14:36:33 -0800427 } else {
Salvador Martineza6f7b252017-04-10 10:46:15 -0700428 mWarnings.dismissHighTemperatureWarning();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800429 }
430 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800431
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800432 logTemperatureStats();
433
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700434 mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800435 }
436
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800437 private void logAtTemperatureThreshold(float temp) {
438 StringBuilder sb = new StringBuilder();
439 sb.append("currentTemp=").append(temp)
440 .append(",thresholdTemp=").append(mThresholdTemp)
441 .append(",batteryStatus=").append(mBatteryStatus)
442 .append(",recentTemps=");
443 for (int i = 0; i < mNumTemps; i++) {
444 sb.append(mRecentTemps[i]).append(',');
445 }
446 Slog.i(TAG, sb.toString());
447 }
448
449 /**
450 * Calculates and logs min, max, and average
451 * {@link HardwarePropertiesManager#DEVICE_TEMPERATURE_SKIN} over the past
452 * {@link #TEMPERATURE_LOGGING_INTERVAL}.
453 */
454 private void logTemperatureStats() {
455 if (mNextLogTime > System.currentTimeMillis() && mNumTemps != MAX_RECENT_TEMPS) {
456 return;
457 }
458
459 if (mNumTemps > 0) {
460 float sum = mRecentTemps[0], min = mRecentTemps[0], max = mRecentTemps[0];
461 for (int i = 1; i < mNumTemps; i++) {
462 float temp = mRecentTemps[i];
463 sum += temp;
464 if (temp > max) {
465 max = temp;
466 }
467 if (temp < min) {
468 min = temp;
469 }
470 }
471
472 float avg = sum / mNumTemps;
473 Slog.i(TAG, "avg=" + avg + ",min=" + min + ",max=" + max);
474 MetricsLogger.histogram(mContext, "device_skin_temp_avg", (int) avg);
475 MetricsLogger.histogram(mContext, "device_skin_temp_min", (int) min);
476 MetricsLogger.histogram(mContext, "device_skin_temp_max", (int) max);
477 }
478 setNextLogTime();
479 mNumTemps = 0;
480 }
481
482 private void setNextLogTime() {
483 mNextLogTime = System.currentTimeMillis() + TEMPERATURE_LOGGING_INTERVAL;
484 }
485
Joe Onorato10523b4d2010-10-25 10:42:46 -0700486 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700487 pw.print("mLowBatteryAlertCloseLevel=");
488 pw.println(mLowBatteryAlertCloseLevel);
489 pw.print("mLowBatteryReminderLevels=");
490 pw.println(Arrays.toString(mLowBatteryReminderLevels));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700491 pw.print("mBatteryLevel=");
492 pw.println(Integer.toString(mBatteryLevel));
493 pw.print("mBatteryStatus=");
494 pw.println(Integer.toString(mBatteryStatus));
495 pw.print("mPlugType=");
496 pw.println(Integer.toString(mPlugType));
497 pw.print("mInvalidCharger=");
498 pw.println(Integer.toString(mInvalidCharger));
Daniel Sandlerdea64622013-09-23 16:05:57 -0400499 pw.print("mScreenOffTime=");
500 pw.print(mScreenOffTime);
501 if (mScreenOffTime >= 0) {
502 pw.print(" (");
503 pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
504 pw.print(" ago)");
505 }
506 pw.println();
507 pw.print("soundTimeout=");
508 pw.println(Settings.Global.getInt(mContext.getContentResolver(),
509 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700510 pw.print("bucket: ");
511 pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800512 pw.print("mThresholdTemp=");
513 pw.println(Float.toString(mThresholdTemp));
514 pw.print("mNextLogTime=");
515 pw.println(Long.toString(mNextLogTime));
John Spurlocked452c52014-03-06 12:02:31 -0500516 mWarnings.dump(pw);
517 }
518
519 public interface WarningsUI {
520 void update(int batteryLevel, int bucket, long screenOffTime);
Salvador Martinezf9e47502018-01-04 13:45:48 -0800521 void updateEstimate(Estimate estimate);
Salvador Martinezbb902fc2018-01-22 19:46:55 -0800522 void updateThresholds(long lowThreshold, long severeThreshold);
John Spurlocked452c52014-03-06 12:02:31 -0500523 void dismissLowBatteryWarning();
524 void showLowBatteryWarning(boolean playSound);
525 void dismissInvalidChargerWarning();
526 void showInvalidChargerWarning();
527 void updateLowBatteryWarning();
528 boolean isInvalidChargerWarningShowing();
Salvador Martineza6f7b252017-04-10 10:46:15 -0700529 void dismissHighTemperatureWarning();
530 void showHighTemperatureWarning();
531 void showThermalShutdownWarning();
John Spurlocked452c52014-03-06 12:02:31 -0500532 void dump(PrintWriter pw);
John Spurlockecbc5e82014-10-22 09:05:51 -0400533 void userSwitched();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700534 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700535
Todd Poynorfb95e122017-10-04 13:00:32 -0700536 // Thermal event received from vendor thermal management subsystem
537 private final class ThermalEventListener extends IThermalEventListener.Stub {
538 @Override public void notifyThrottling(boolean isThrottling, Temperature temp) {
539 // Trigger an update of the temperature warning. Only one
540 // callback can be enabled at a time, so remove any existing
541 // callback; updateTemperatureWarning will schedule another one.
542 mHandler.removeCallbacks(mUpdateTempCallback);
543 updateTemperatureWarning();
544 }
545 }
546}