blob: f0543454073d8d5a10f7f1382626e0731f32dd79 [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;
Wei Wangbf05e602018-11-21 11:46:48 -080093 @VisibleForTesting IThermalService mThermalService;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080094
Salvador Martinezfd38aa52018-03-28 23:56:59 -070095 @VisibleForTesting int mBatteryLevel = 100;
Salvador Martinez36307962018-02-08 14:29:08 -080096 @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
97
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) {
Salvador Martinez88b0d502018-10-01 11:31:43 -0700304 if (mTimeRemaining <= mEnhancedEstimates.getSevereWarningThreshold()
305 || mBatteryLevel <= mLowBatteryReminderLevels[1]) {
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700306 mSevereWarningShownThisChargeCycle = true;
Salvador Martinez88b0d502018-10-01 11:31:43 -0700307 mLowWarningShownThisChargeCycle = true;
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700308 } else {
309 mLowWarningShownThisChargeCycle = true;
310 }
Salvador Martinez36307962018-02-08 14:29:08 -0800311 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800312 } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
313 isPowerSaver)) {
314 mWarnings.dismissLowBatteryWarning();
315 } else {
316 mWarnings.updateLowBatteryWarning();
317 }
318 }
319
320 @VisibleForTesting
321 boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700322 int bucket, long timeRemaining, boolean isPowerSaver, int batteryStatus) {
323 if (mEnhancedEstimates.isHybridNotificationEnabled()) {
324 // triggering logic when enhanced estimate is available
325 return isEnhancedTrigger(plugged, timeRemaining, isPowerSaver, batteryStatus);
326 }
327 // legacy triggering logic
Salvador Martinezf9e47502018-01-04 13:45:48 -0800328 return !plugged
329 && !isPowerSaver
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700330 && (((bucket < oldBucket || oldPlugged) && bucket < 0))
331 && batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
Salvador Martinezf9e47502018-01-04 13:45:48 -0800332 }
333
Salvador Martinezf9e47502018-01-04 13:45:48 -0800334 @VisibleForTesting
335 boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
336 long timeRemaining, boolean isPowerSaver) {
Salvador Martinezc25604b2018-08-03 14:07:44 -0700337 final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
338 final boolean hybridWouldDismiss = hybridEnabled
Salvador Martinezbb902fc2018-01-22 19:46:55 -0800339 && timeRemaining > mEnhancedEstimates.getLowWarningThreshold();
Salvador Martinezf9e47502018-01-04 13:45:48 -0800340 final boolean standardWouldDismiss = (bucket > oldBucket && bucket > 0);
Salvador Martinezc25604b2018-08-03 14:07:44 -0700341 return (isPowerSaver && !hybridEnabled)
Salvador Martinezf9e47502018-01-04 13:45:48 -0800342 || plugged
343 || (standardWouldDismiss && (!mEnhancedEstimates.isHybridNotificationEnabled()
344 || hybridWouldDismiss));
345 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700346
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700347 private boolean isEnhancedTrigger(boolean plugged, long timeRemaining, boolean isPowerSaver,
348 int batteryStatus) {
Salvador Martinezc25604b2018-08-03 14:07:44 -0700349 if (plugged || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
Salvador Martinez36307962018-02-08 14:29:08 -0800350 return false;
351 }
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700352 int warnLevel = mLowBatteryReminderLevels[0];
353 int critLevel = mLowBatteryReminderLevels[1];
Salvador Martinez36307962018-02-08 14:29:08 -0800354
Salvador Martinezc25604b2018-08-03 14:07:44 -0700355 // Only show the low warning once per charge cycle & no battery saver
356 final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !isPowerSaver
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700357 && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
358 || mBatteryLevel <= warnLevel);
Salvador Martinez36307962018-02-08 14:29:08 -0800359
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700360 // Only show the severe warning once per charge cycle
361 final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
362 && (timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
363 || mBatteryLevel <= critLevel);
Salvador Martinez36307962018-02-08 14:29:08 -0800364
365 return canShowWarning || canShowSevereWarning;
366 }
367
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800368 private void initTemperatureWarning() {
Andrew Sappersteine3352562017-01-20 15:41:03 -0800369 ContentResolver resolver = mContext.getContentResolver();
370 Resources resources = mContext.getResources();
371 if (Settings.Global.getInt(resolver, Settings.Global.SHOW_TEMPERATURE_WARNING,
372 resources.getInteger(R.integer.config_showTemperatureWarning)) == 0) {
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800373 return;
374 }
375
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800376 mThresholdTemp = Settings.Global.getFloat(resolver, Settings.Global.WARNING_TEMPERATURE,
Andrew Sappersteine3352562017-01-20 15:41:03 -0800377 resources.getInteger(R.integer.config_warningTemperature));
Andrew Sapperstein75184712017-01-05 16:45:37 -0800378
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800379 if (mThresholdTemp < 0f) {
Todd Poynorfb95e122017-10-04 13:00:32 -0700380 // Get the shutdown temperature, adjust for warning tolerance.
Andrew Sapperstein75184712017-01-05 16:45:37 -0800381 float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures(
382 HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
Jason Monkd819c312017-08-11 12:53:36 -0400383 HardwarePropertiesManager.TEMPERATURE_SHUTDOWN);
Andrew Sapperstein75184712017-01-05 16:45:37 -0800384 if (throttlingTemps == null
385 || throttlingTemps.length == 0
386 || throttlingTemps[0] == HardwarePropertiesManager.UNDEFINED_TEMPERATURE) {
387 return;
388 }
Jason Monkd819c312017-08-11 12:53:36 -0400389 mThresholdTemp = throttlingTemps[0] -
390 resources.getInteger(R.integer.config_warningTemperatureTolerance);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800391 }
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700392
Todd Poynorfb95e122017-10-04 13:00:32 -0700393 if (mThermalService == null) {
394 // Enable push notifications of throttling from vendor thermal
395 // management subsystem via thermalservice, in addition to our
396 // usual polling, to react to temperature jumps more quickly.
Wei Wangbf05e602018-11-21 11:46:48 -0800397 IBinder b = ServiceManager.getService(Context.THERMAL_SERVICE);
Todd Poynorfb95e122017-10-04 13:00:32 -0700398
399 if (b != null) {
400 mThermalService = IThermalService.Stub.asInterface(b);
401 try {
Wei Wangbad7c202018-11-01 11:57:39 -0700402 mThermalService.registerThermalEventListenerWithType(
403 new ThermalEventListener(), Temperature.TYPE_SKIN);
Todd Poynorfb95e122017-10-04 13:00:32 -0700404 } catch (RemoteException e) {
405 // Should never happen.
406 }
407 } else {
408 Slog.w(TAG, "cannot find thermalservice, no throttling push notifications");
409 }
410 }
411
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800412 setNextLogTime();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800413
414 // We have passed all of the checks, start checking the temp
Wei Wangfeb9de62018-11-26 14:35:17 -0800415 mHandler.post(mUpdateTempCallback);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800416 }
417
Salvador Martineza6f7b252017-04-10 10:46:15 -0700418 private void showThermalShutdownDialog() {
419 if (mPowerManager.getLastShutdownReason()
420 == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
421 mWarnings.showThermalShutdownWarning();
422 }
423 }
424
Jason Monkd819c312017-08-11 12:53:36 -0400425 @VisibleForTesting
426 protected void updateTemperatureWarning() {
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800427 float[] temps = mHardwarePropertiesManager.getDeviceTemperatures(
428 HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
429 HardwarePropertiesManager.TEMPERATURE_CURRENT);
430 if (temps.length != 0) {
431 float temp = temps[0];
432 mRecentTemps[mNumTemps++] = temp;
433
434 StatusBar statusBar = getComponent(StatusBar.class);
435 if (statusBar != null && !statusBar.isDeviceInVrMode()
436 && temp >= mThresholdTemp) {
437 logAtTemperatureThreshold(temp);
Salvador Martineza6f7b252017-04-10 10:46:15 -0700438 mWarnings.showHighTemperatureWarning();
Andrew Sapperstein65d8a5f2016-12-19 14:36:33 -0800439 } else {
Salvador Martineza6f7b252017-04-10 10:46:15 -0700440 mWarnings.dismissHighTemperatureWarning();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800441 }
442 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800443
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800444 logTemperatureStats();
445
Wei Wangfeb9de62018-11-26 14:35:17 -0800446 // Remove any pending callbacks as we only want to enable one
447 mHandler.removeCallbacks(mUpdateTempCallback);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700448 mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800449 }
450
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800451 private void logAtTemperatureThreshold(float temp) {
452 StringBuilder sb = new StringBuilder();
453 sb.append("currentTemp=").append(temp)
454 .append(",thresholdTemp=").append(mThresholdTemp)
455 .append(",batteryStatus=").append(mBatteryStatus)
456 .append(",recentTemps=");
457 for (int i = 0; i < mNumTemps; i++) {
458 sb.append(mRecentTemps[i]).append(',');
459 }
460 Slog.i(TAG, sb.toString());
461 }
462
463 /**
464 * Calculates and logs min, max, and average
465 * {@link HardwarePropertiesManager#DEVICE_TEMPERATURE_SKIN} over the past
466 * {@link #TEMPERATURE_LOGGING_INTERVAL}.
467 */
468 private void logTemperatureStats() {
469 if (mNextLogTime > System.currentTimeMillis() && mNumTemps != MAX_RECENT_TEMPS) {
470 return;
471 }
472
473 if (mNumTemps > 0) {
474 float sum = mRecentTemps[0], min = mRecentTemps[0], max = mRecentTemps[0];
475 for (int i = 1; i < mNumTemps; i++) {
476 float temp = mRecentTemps[i];
477 sum += temp;
478 if (temp > max) {
479 max = temp;
480 }
481 if (temp < min) {
482 min = temp;
483 }
484 }
485
486 float avg = sum / mNumTemps;
487 Slog.i(TAG, "avg=" + avg + ",min=" + min + ",max=" + max);
488 MetricsLogger.histogram(mContext, "device_skin_temp_avg", (int) avg);
489 MetricsLogger.histogram(mContext, "device_skin_temp_min", (int) min);
490 MetricsLogger.histogram(mContext, "device_skin_temp_max", (int) max);
491 }
492 setNextLogTime();
493 mNumTemps = 0;
494 }
495
496 private void setNextLogTime() {
497 mNextLogTime = System.currentTimeMillis() + TEMPERATURE_LOGGING_INTERVAL;
498 }
499
Joe Onorato10523b4d2010-10-25 10:42:46 -0700500 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700501 pw.print("mLowBatteryAlertCloseLevel=");
502 pw.println(mLowBatteryAlertCloseLevel);
503 pw.print("mLowBatteryReminderLevels=");
504 pw.println(Arrays.toString(mLowBatteryReminderLevels));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700505 pw.print("mBatteryLevel=");
506 pw.println(Integer.toString(mBatteryLevel));
507 pw.print("mBatteryStatus=");
508 pw.println(Integer.toString(mBatteryStatus));
509 pw.print("mPlugType=");
510 pw.println(Integer.toString(mPlugType));
511 pw.print("mInvalidCharger=");
512 pw.println(Integer.toString(mInvalidCharger));
Daniel Sandlerdea64622013-09-23 16:05:57 -0400513 pw.print("mScreenOffTime=");
514 pw.print(mScreenOffTime);
515 if (mScreenOffTime >= 0) {
516 pw.print(" (");
517 pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
518 pw.print(" ago)");
519 }
520 pw.println();
521 pw.print("soundTimeout=");
522 pw.println(Settings.Global.getInt(mContext.getContentResolver(),
523 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700524 pw.print("bucket: ");
525 pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -0800526 pw.print("mThresholdTemp=");
527 pw.println(Float.toString(mThresholdTemp));
528 pw.print("mNextLogTime=");
529 pw.println(Long.toString(mNextLogTime));
John Spurlocked452c52014-03-06 12:02:31 -0500530 mWarnings.dump(pw);
531 }
532
533 public interface WarningsUI {
534 void update(int batteryLevel, int bucket, long screenOffTime);
Salvador Martinezf9e47502018-01-04 13:45:48 -0800535 void updateEstimate(Estimate estimate);
Salvador Martinezbb902fc2018-01-22 19:46:55 -0800536 void updateThresholds(long lowThreshold, long severeThreshold);
John Spurlocked452c52014-03-06 12:02:31 -0500537 void dismissLowBatteryWarning();
538 void showLowBatteryWarning(boolean playSound);
539 void dismissInvalidChargerWarning();
540 void showInvalidChargerWarning();
541 void updateLowBatteryWarning();
542 boolean isInvalidChargerWarningShowing();
Salvador Martineza6f7b252017-04-10 10:46:15 -0700543 void dismissHighTemperatureWarning();
544 void showHighTemperatureWarning();
545 void showThermalShutdownWarning();
John Spurlocked452c52014-03-06 12:02:31 -0500546 void dump(PrintWriter pw);
John Spurlockecbc5e82014-10-22 09:05:51 -0400547 void userSwitched();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700548 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700549
Todd Poynorfb95e122017-10-04 13:00:32 -0700550 // Thermal event received from vendor thermal management subsystem
551 private final class ThermalEventListener extends IThermalEventListener.Stub {
Wei Wangbad7c202018-11-01 11:57:39 -0700552 @Override public void notifyThrottling(Temperature temp) {
Wei Wangfeb9de62018-11-26 14:35:17 -0800553 mHandler.post(mUpdateTempCallback);
Todd Poynorfb95e122017-10-04 13:00:32 -0700554 }
555 }
556}