blob: 0b9067e5dc1ff0b471df15c7760f218021fb98f5 [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();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -040069 @VisibleForTesting
70 final Receiver mReceiver = new Receiver();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070071
John Spurlock3332ba52014-03-10 17:44:07 -040072 private PowerManager mPowerManager;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080073 private HardwarePropertiesManager mHardwarePropertiesManager;
John Spurlock3332ba52014-03-10 17:44:07 -040074 private WarningsUI mWarnings;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070075 private final Configuration mLastConfiguration = new Configuration();
Salvador Martinezf9e47502018-01-04 13:45:48 -080076 private long mTimeRemaining = Long.MAX_VALUE;
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 Martinez926f0712018-07-03 18:07:07 -070080 private Estimate mLastEstimate;
Salvador Martinez36307962018-02-08 14:29:08 -080081 private boolean mLowWarningShownThisChargeCycle;
82 private boolean mSevereWarningShownThisChargeCycle;
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
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -080089 private float mThresholdTemp;
90 private float[] mRecentTemps = new float[MAX_RECENT_TEMPS];
91 private int mNumTemps;
92 private long mNextLogTime;
Todd Poynorfb95e122017-10-04 13:00:32 -070093 private 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
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070098 // by using the same instance (method references are not guaranteed to be the same object
Salvador Martinezf9e47502018-01-04 13:45:48 -080099 // We create a method reference here so that we are guaranteed that we can remove a callback
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700100 // each time they are created).
101 private final Runnable mUpdateTempCallback = this::updateTemperatureWarning;
102
Joe Onorato10523b4d2010-10-25 10:42:46 -0700103 public void start() {
John Spurlock3332ba52014-03-10 17:44:07 -0400104 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800105 mHardwarePropertiesManager = (HardwarePropertiesManager)
106 mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE);
John Spurlock3332ba52014-03-10 17:44:07 -0400107 mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
Jason Monkd819c312017-08-11 12:53:36 -0400108 mWarnings = Dependency.get(WarningsUI.class);
Salvador Martinezf9e47502018-01-04 13:45:48 -0800109 mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700110 mLastConfiguration.setTo(mContext.getResources().getConfiguration());
Daniel Sandlerdea64622013-09-23 16:05:57 -0400111
Dianne Hackborn14272302014-06-10 23:13:02 -0700112 ContentObserver obs = new ContentObserver(mHandler) {
113 @Override
114 public void onChange(boolean selfChange) {
115 updateBatteryWarningLevels();
116 }
117 };
118 final ContentResolver resolver = mContext.getContentResolver();
119 resolver.registerContentObserver(Settings.Global.getUriFor(
120 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
121 false, obs, UserHandle.USER_ALL);
122 updateBatteryWarningLevels();
John Spurlock3332ba52014-03-10 17:44:07 -0400123 mReceiver.init();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800124
Salvador Martineza6f7b252017-04-10 10:46:15 -0700125 // Check to see if we need to let the user know that the phone previously shut down due
126 // to the temperature being too high.
127 showThermalShutdownDialog();
128
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800129 initTemperatureWarning();
John Spurlock3332ba52014-03-10 17:44:07 -0400130 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700131
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700132 @Override
133 protected void onConfigurationChanged(Configuration newConfig) {
134 final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
135
136 // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
137 if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
138 mHandler.post(this::initTemperatureWarning);
139 }
140 }
141
Dianne Hackborn14272302014-06-10 23:13:02 -0700142 void updateBatteryWarningLevels() {
143 int critLevel = mContext.getResources().getInteger(
144 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Makoto Onuki16a0dd22018-03-20 10:40:37 -0700145 int warnLevel = mContext.getResources().getInteger(
Dianne Hackborn14272302014-06-10 23:13:02 -0700146 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Makoto Onuki4a9036b2018-01-11 14:48:20 -0800147
Dianne Hackborn14272302014-06-10 23:13:02 -0700148 if (warnLevel < critLevel) {
149 warnLevel = critLevel;
150 }
151
152 mLowBatteryReminderLevels[0] = warnLevel;
153 mLowBatteryReminderLevels[1] = critLevel;
154 mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
155 + mContext.getResources().getInteger(
156 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
157 }
158
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700159 /**
160 * Buckets the battery level.
161 *
162 * The code in this function is a little weird because I couldn't comprehend
163 * the bucket going up when the battery level was going down. --joeo
164 *
165 * 1 means that the battery is "ok"
166 * 0 means that the battery is between "ok" and what we should warn about.
167 * less than 0 means that the battery is low
168 */
169 private int findBatteryLevelBucket(int level) {
170 if (level >= mLowBatteryAlertCloseLevel) {
171 return 1;
172 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700173 if (level > mLowBatteryReminderLevels[0]) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700174 return 0;
175 }
176 final int N = mLowBatteryReminderLevels.length;
177 for (int i=N-1; i>=0; i--) {
178 if (level <= mLowBatteryReminderLevels[i]) {
179 return -1-i;
180 }
181 }
182 throw new RuntimeException("not possible!");
183 }
184
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400185 @VisibleForTesting
186 final class Receiver extends BroadcastReceiver {
John Spurlock3332ba52014-03-10 17:44:07 -0400187
188 public void init() {
189 // Register for Intent broadcasts for...
190 IntentFilter filter = new IntentFilter();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400191 filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
John Spurlock3332ba52014-03-10 17:44:07 -0400192 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
193 filter.addAction(Intent.ACTION_SCREEN_OFF);
194 filter.addAction(Intent.ACTION_SCREEN_ON);
John Spurlockecbc5e82014-10-22 09:05:51 -0400195 filter.addAction(Intent.ACTION_USER_SWITCHED);
John Spurlock3332ba52014-03-10 17:44:07 -0400196 mContext.registerReceiver(this, filter, null, mHandler);
John Spurlock3332ba52014-03-10 17:44:07 -0400197 }
198
Joe Onorato10523b4d2010-10-25 10:42:46 -0700199 @Override
200 public void onReceive(Context context, Intent intent) {
201 String action = intent.getAction();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400202 if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
203 ThreadUtils.postOnBackgroundThread(() -> {
204 if (mPowerManager.isPowerSaveMode()) {
205 mWarnings.dismissLowBatteryWarning();
206 }
207 });
208 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700209 final int oldBatteryLevel = mBatteryLevel;
210 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
211 final int oldBatteryStatus = mBatteryStatus;
212 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
213 BatteryManager.BATTERY_STATUS_UNKNOWN);
214 final int oldPlugType = mPlugType;
215 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
216 final int oldInvalidCharger = mInvalidCharger;
217 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
218
219 final boolean plugged = mPlugType != 0;
220 final boolean oldPlugged = oldPlugType != 0;
221
222 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
223 int bucket = findBatteryLevelBucket(mBatteryLevel);
224
Daniel Sandler71986622011-07-26 13:06:49 -0400225 if (DEBUG) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400226 Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700227 + " .. " + mLowBatteryReminderLevels[0]
228 + " .. " + mLowBatteryReminderLevels[1]);
Daniel Sandlerdea64622013-09-23 16:05:57 -0400229 Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
230 Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
231 Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
232 Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
233 Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
234 Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700235 }
236
John Spurlocked452c52014-03-06 12:02:31 -0500237 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700238 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400239 Slog.d(TAG, "showing invalid charger warning");
John Spurlocked452c52014-03-06 12:02:31 -0500240 mWarnings.showInvalidChargerWarning();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700241 return;
242 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
John Spurlocked452c52014-03-06 12:02:31 -0500243 mWarnings.dismissInvalidChargerWarning();
244 } else if (mWarnings.isInvalidChargerWarningShowing()) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700245 // if invalid charger is showing, don't show low battery
246 return;
247 }
248
Salvador Martinezf9e47502018-01-04 13:45:48 -0800249 // Show the correct version of low battery warning if needed
Salvador Martinez110f9a12018-01-31 09:57:17 -0800250 ThreadUtils.postOnBackgroundThread(() -> {
Salvador Martinez926f0712018-07-03 18:07:07 -0700251 maybeShowBatteryWarning(
252 oldBatteryLevel, plugged, oldPlugged, oldBucket, bucket);
Salvador Martinez110f9a12018-01-31 09:57:17 -0800253 });
Beverly334bc5f2017-07-31 10:37:17 -0400254
Daniel Sandlerdea64622013-09-23 16:05:57 -0400255 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
256 mScreenOffTime = SystemClock.elapsedRealtime();
257 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
258 mScreenOffTime = -1;
John Spurlockecbc5e82014-10-22 09:05:51 -0400259 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
260 mWarnings.userSwitched();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700261 } else {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400262 Slog.w(TAG, "unknown intent: " + intent);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700263 }
264 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800265 }
266
Salvador Martinez926f0712018-07-03 18:07:07 -0700267 protected void maybeShowBatteryWarning(int oldBatteryLevel, boolean plugged, boolean oldPlugged,
268 int oldBucket, int bucket) {
Salvador Martinezf9e47502018-01-04 13:45:48 -0800269 boolean isPowerSaver = mPowerManager.isPowerSaveMode();
270 // only play SFX when the dialog comes up or the bucket changes
271 final boolean playSound = bucket != oldBucket || oldPlugged;
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700272 final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
273 if (hybridEnabled) {
Salvador Martinez926f0712018-07-03 18:07:07 -0700274 Estimate estimate = mLastEstimate;
275 if (estimate == null || mBatteryLevel != oldBatteryLevel) {
276 estimate = mEnhancedEstimates.getEstimate();
277 mLastEstimate = estimate;
278 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800279 // Turbo is not always booted once SysUI is running so we have ot make sure we actually
280 // get data back
281 if (estimate != null) {
282 mTimeRemaining = estimate.estimateMillis;
283 mWarnings.updateEstimate(estimate);
Salvador Martinezbb902fc2018-01-22 19:46:55 -0800284 mWarnings.updateThresholds(mEnhancedEstimates.getLowWarningThreshold(),
285 mEnhancedEstimates.getSevereWarningThreshold());
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700286
287 // if we are now over 45% battery & 6 hours remaining we can trigger hybrid
288 // notification again
289 if (mBatteryLevel >= CHARGE_CYCLE_PERCENT_RESET
290 && mTimeRemaining > SIX_HOURS_MILLIS) {
291 mLowWarningShownThisChargeCycle = false;
292 mSevereWarningShownThisChargeCycle = false;
293 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800294 }
295 }
296
Salvador Martinez36307962018-02-08 14:29:08 -0800297 if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket,
298 mTimeRemaining, isPowerSaver, mBatteryStatus)) {
Salvador Martinezf9e47502018-01-04 13:45:48 -0800299 mWarnings.showLowBatteryWarning(playSound);
Salvador Martinez36307962018-02-08 14:29:08 -0800300
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700301 // mark if we've already shown a warning this cycle. This will prevent the notification
302 // trigger from spamming users by only showing low/critical warnings once per cycle
303 if (hybridEnabled) {
304 if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
305 || mBatteryLevel < mLowBatteryReminderLevels[1]) {
306 mSevereWarningShownThisChargeCycle = true;
307 } else {
308 mLowWarningShownThisChargeCycle = true;
309 }
Salvador Martinez36307962018-02-08 14:29:08 -0800310 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800311 } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
312 isPowerSaver)) {
313 mWarnings.dismissLowBatteryWarning();
314 } else {
315 mWarnings.updateLowBatteryWarning();
316 }
317 }
318
319 @VisibleForTesting
320 boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700321 int bucket, long timeRemaining, boolean isPowerSaver, int batteryStatus) {
322 if (mEnhancedEstimates.isHybridNotificationEnabled()) {
323 // triggering logic when enhanced estimate is available
324 return isEnhancedTrigger(plugged, timeRemaining, isPowerSaver, batteryStatus);
325 }
326 // legacy triggering logic
Salvador Martinezf9e47502018-01-04 13:45:48 -0800327 return !plugged
328 && !isPowerSaver
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700329 && (((bucket < oldBucket || oldPlugged) && bucket < 0))
330 && batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
Salvador Martinezf9e47502018-01-04 13:45:48 -0800331 }
332
Salvador Martinezf9e47502018-01-04 13:45:48 -0800333 @VisibleForTesting
334 boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
335 long timeRemaining, boolean isPowerSaver) {
Salvador Martinezc50a9bd2018-08-03 14:07:44 -0700336 final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
337 final boolean hybridWouldDismiss = hybridEnabled
Salvador Martinezbb902fc2018-01-22 19:46:55 -0800338 && timeRemaining > mEnhancedEstimates.getLowWarningThreshold();
Salvador Martinezf9e47502018-01-04 13:45:48 -0800339 final boolean standardWouldDismiss = (bucket > oldBucket && bucket > 0);
Salvador Martinezc50a9bd2018-08-03 14:07:44 -0700340 return (isPowerSaver && !hybridEnabled)
Salvador Martinezf9e47502018-01-04 13:45:48 -0800341 || plugged
342 || (standardWouldDismiss && (!mEnhancedEstimates.isHybridNotificationEnabled()
343 || hybridWouldDismiss));
344 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700345
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700346 private boolean isEnhancedTrigger(boolean plugged, long timeRemaining, boolean isPowerSaver,
347 int batteryStatus) {
Salvador Martinezc50a9bd2018-08-03 14:07:44 -0700348 if (plugged || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
Salvador Martinez36307962018-02-08 14:29:08 -0800349 return false;
350 }
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700351 int warnLevel = mLowBatteryReminderLevels[0];
352 int critLevel = mLowBatteryReminderLevels[1];
Salvador Martinez36307962018-02-08 14:29:08 -0800353
Salvador Martinezc50a9bd2018-08-03 14:07:44 -0700354 // Only show the low warning once per charge cycle & no battery saver
355 final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !isPowerSaver
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700356 && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
357 || mBatteryLevel <= warnLevel);
Salvador Martinez36307962018-02-08 14:29:08 -0800358
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700359 // Only show the severe warning once per charge cycle
360 final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
361 && (timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
362 || mBatteryLevel <= critLevel);
Salvador Martinez36307962018-02-08 14:29:08 -0800363
364 return canShowWarning || canShowSevereWarning;
365 }
366
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800367 private void initTemperatureWarning() {
Andrew Sappersteine3352562017-01-20 15:41:03 -0800368 ContentResolver resolver = mContext.getContentResolver();
369 Resources resources = mContext.getResources();
370 if (Settings.Global.getInt(resolver, Settings.Global.SHOW_TEMPERATURE_WARNING,
371 resources.getInteger(R.integer.config_showTemperatureWarning)) == 0) {
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800372 return;
373 }
374
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800375 mThresholdTemp = Settings.Global.getFloat(resolver, Settings.Global.WARNING_TEMPERATURE,
Andrew Sappersteine3352562017-01-20 15:41:03 -0800376 resources.getInteger(R.integer.config_warningTemperature));
Andrew Sapperstein75184712017-01-05 16:45:37 -0800377
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800378 if (mThresholdTemp < 0f) {
Todd Poynorfb95e122017-10-04 13:00:32 -0700379 // Get the shutdown temperature, adjust for warning tolerance.
Andrew Sapperstein75184712017-01-05 16:45:37 -0800380 float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures(
381 HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
Jason Monkd819c312017-08-11 12:53:36 -0400382 HardwarePropertiesManager.TEMPERATURE_SHUTDOWN);
Andrew Sapperstein75184712017-01-05 16:45:37 -0800383 if (throttlingTemps == null
384 || throttlingTemps.length == 0
385 || throttlingTemps[0] == HardwarePropertiesManager.UNDEFINED_TEMPERATURE) {
386 return;
387 }
Jason Monkd819c312017-08-11 12:53:36 -0400388 mThresholdTemp = throttlingTemps[0] -
389 resources.getInteger(R.integer.config_warningTemperatureTolerance);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800390 }
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700391
Todd Poynorfb95e122017-10-04 13:00:32 -0700392 if (mThermalService == null) {
393 // Enable push notifications of throttling from vendor thermal
394 // management subsystem via thermalservice, in addition to our
395 // usual polling, to react to temperature jumps more quickly.
396 IBinder b = ServiceManager.getService("thermalservice");
397
398 if (b != null) {
399 mThermalService = IThermalService.Stub.asInterface(b);
400 try {
401 mThermalService.registerThermalEventListener(
402 new ThermalEventListener());
403 } catch (RemoteException e) {
404 // Should never happen.
405 }
406 } else {
407 Slog.w(TAG, "cannot find thermalservice, no throttling push notifications");
408 }
409 }
410
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800411 setNextLogTime();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800412
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700413 // This initialization method may be called on a configuration change. Only one set of
414 // ongoing callbacks should be occurring, so remove any now. updateTemperatureWarning will
415 // schedule an ongoing callback.
416 mHandler.removeCallbacks(mUpdateTempCallback);
417
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800418 // We have passed all of the checks, start checking the temp
419 updateTemperatureWarning();
420 }
421
Salvador Martineza6f7b252017-04-10 10:46:15 -0700422 private void showThermalShutdownDialog() {
423 if (mPowerManager.getLastShutdownReason()
424 == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
425 mWarnings.showThermalShutdownWarning();
426 }
427 }
428
Jason Monkd819c312017-08-11 12:53:36 -0400429 @VisibleForTesting
430 protected void updateTemperatureWarning() {
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800431 float[] temps = mHardwarePropertiesManager.getDeviceTemperatures(
432 HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
433 HardwarePropertiesManager.TEMPERATURE_CURRENT);
434 if (temps.length != 0) {
435 float temp = temps[0];
436 mRecentTemps[mNumTemps++] = temp;
437
438 StatusBar statusBar = getComponent(StatusBar.class);
439 if (statusBar != null && !statusBar.isDeviceInVrMode()
440 && temp >= mThresholdTemp) {
441 logAtTemperatureThreshold(temp);
Salvador Martineza6f7b252017-04-10 10:46:15 -0700442 mWarnings.showHighTemperatureWarning();
Andrew Sapperstein65d8a5f2016-12-19 14:36:33 -0800443 } else {
Salvador Martineza6f7b252017-04-10 10:46:15 -0700444 mWarnings.dismissHighTemperatureWarning();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800445 }
446 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800447
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800448 logTemperatureStats();
449
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700450 mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800451 }
452
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800453 private void logAtTemperatureThreshold(float temp) {
454 StringBuilder sb = new StringBuilder();
455 sb.append("currentTemp=").append(temp)
456 .append(",thresholdTemp=").append(mThresholdTemp)
457 .append(",batteryStatus=").append(mBatteryStatus)
458 .append(",recentTemps=");
459 for (int i = 0; i < mNumTemps; i++) {
460 sb.append(mRecentTemps[i]).append(',');
461 }
462 Slog.i(TAG, sb.toString());
463 }
464
465 /**
466 * Calculates and logs min, max, and average
467 * {@link HardwarePropertiesManager#DEVICE_TEMPERATURE_SKIN} over the past
468 * {@link #TEMPERATURE_LOGGING_INTERVAL}.
469 */
470 private void logTemperatureStats() {
471 if (mNextLogTime > System.currentTimeMillis() && mNumTemps != MAX_RECENT_TEMPS) {
472 return;
473 }
474
475 if (mNumTemps > 0) {
476 float sum = mRecentTemps[0], min = mRecentTemps[0], max = mRecentTemps[0];
477 for (int i = 1; i < mNumTemps; i++) {
478 float temp = mRecentTemps[i];
479 sum += temp;
480 if (temp > max) {
481 max = temp;
482 }
483 if (temp < min) {
484 min = temp;
485 }
486 }
487
488 float avg = sum / mNumTemps;
489 Slog.i(TAG, "avg=" + avg + ",min=" + min + ",max=" + max);
490 MetricsLogger.histogram(mContext, "device_skin_temp_avg", (int) avg);
491 MetricsLogger.histogram(mContext, "device_skin_temp_min", (int) min);
492 MetricsLogger.histogram(mContext, "device_skin_temp_max", (int) max);
493 }
494 setNextLogTime();
495 mNumTemps = 0;
496 }
497
498 private void setNextLogTime() {
499 mNextLogTime = System.currentTimeMillis() + TEMPERATURE_LOGGING_INTERVAL;
500 }
501
Joe Onorato10523b4d2010-10-25 10:42:46 -0700502 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700503 pw.print("mLowBatteryAlertCloseLevel=");
504 pw.println(mLowBatteryAlertCloseLevel);
505 pw.print("mLowBatteryReminderLevels=");
506 pw.println(Arrays.toString(mLowBatteryReminderLevels));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700507 pw.print("mBatteryLevel=");
508 pw.println(Integer.toString(mBatteryLevel));
509 pw.print("mBatteryStatus=");
510 pw.println(Integer.toString(mBatteryStatus));
511 pw.print("mPlugType=");
512 pw.println(Integer.toString(mPlugType));
513 pw.print("mInvalidCharger=");
514 pw.println(Integer.toString(mInvalidCharger));
Daniel Sandlerdea64622013-09-23 16:05:57 -0400515 pw.print("mScreenOffTime=");
516 pw.print(mScreenOffTime);
517 if (mScreenOffTime >= 0) {
518 pw.print(" (");
519 pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
520 pw.print(" ago)");
521 }
522 pw.println();
523 pw.print("soundTimeout=");
524 pw.println(Settings.Global.getInt(mContext.getContentResolver(),
525 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700526 pw.print("bucket: ");
527 pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800528 pw.print("mThresholdTemp=");
529 pw.println(Float.toString(mThresholdTemp));
530 pw.print("mNextLogTime=");
531 pw.println(Long.toString(mNextLogTime));
John Spurlocked452c52014-03-06 12:02:31 -0500532 mWarnings.dump(pw);
533 }
534
535 public interface WarningsUI {
536 void update(int batteryLevel, int bucket, long screenOffTime);
Salvador Martinezf9e47502018-01-04 13:45:48 -0800537 void updateEstimate(Estimate estimate);
Salvador Martinezbb902fc2018-01-22 19:46:55 -0800538 void updateThresholds(long lowThreshold, long severeThreshold);
John Spurlocked452c52014-03-06 12:02:31 -0500539 void dismissLowBatteryWarning();
540 void showLowBatteryWarning(boolean playSound);
541 void dismissInvalidChargerWarning();
542 void showInvalidChargerWarning();
543 void updateLowBatteryWarning();
544 boolean isInvalidChargerWarningShowing();
Salvador Martineza6f7b252017-04-10 10:46:15 -0700545 void dismissHighTemperatureWarning();
546 void showHighTemperatureWarning();
547 void showThermalShutdownWarning();
John Spurlocked452c52014-03-06 12:02:31 -0500548 void dump(PrintWriter pw);
John Spurlockecbc5e82014-10-22 09:05:51 -0400549 void userSwitched();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700550 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700551
Todd Poynorfb95e122017-10-04 13:00:32 -0700552 // Thermal event received from vendor thermal management subsystem
553 private final class ThermalEventListener extends IThermalEventListener.Stub {
554 @Override public void notifyThrottling(boolean isThrottling, Temperature temp) {
555 // Trigger an update of the temperature warning. Only one
556 // callback can be enabled at a time, so remove any existing
557 // callback; updateTemperatureWarning will schedule another one.
558 mHandler.removeCallbacks(mUpdateTempCallback);
559 updateTemperatureWarning();
560 }
561 }
562}