blob: 59ac329e19838736b31ffdbca92ea81a419f8cca [file] [log] [blame]
Joe Onorato10523b4d2010-10-25 10:42:46 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.power;
18
Joe Onorato10523b4d2010-10-25 10:42:46 -070019import android.content.BroadcastReceiver;
Dianne Hackborn14272302014-06-10 23:13:02 -070020import android.content.ContentResolver;
Joe Onorato10523b4d2010-10-25 10:42:46 -070021import android.content.Context;
Joe Onorato10523b4d2010-10-25 10:42:46 -070022import android.content.Intent;
23import android.content.IntentFilter;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070024import android.content.pm.ActivityInfo;
25import android.content.res.Configuration;
Dianne Hackborn14272302014-06-10 23:13:02 -070026import android.database.ContentObserver;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070027import android.os.BatteryManager;
Joe Onorato10523b4d2010-10-25 10:42:46 -070028import android.os.Handler;
Todd Poynorfb95e122017-10-04 13:00:32 -070029import android.os.IThermalEventListener;
30import android.os.IThermalService;
Daniel Sandlerdea64622013-09-23 16:05:57 -040031import android.os.PowerManager;
Todd Poynorfb95e122017-10-04 13:00:32 -070032import android.os.RemoteException;
33import android.os.ServiceManager;
Daniel Sandlerdea64622013-09-23 16:05:57 -040034import android.os.SystemClock;
Todd Poynorfb95e122017-10-04 13:00:32 -070035import android.os.Temperature;
Dianne Hackborn14272302014-06-10 23:13:02 -070036import android.os.UserHandle;
Joe Onorato10523b4d2010-10-25 10:42:46 -070037import android.provider.Settings;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080038import android.text.format.DateUtils;
John Spurlock1bb480a2014-08-02 17:12:43 -040039import android.util.Log;
Daniel Sandlerdea64622013-09-23 16:05:57 -040040import android.util.Slog;
Jason Monkd819c312017-08-11 12:53:36 -040041
42import com.android.internal.annotations.VisibleForTesting;
Salvador Martinez580098fe2019-04-11 10:42:15 -070043import com.android.settingslib.fuelgauge.Estimate;
Salvador Martinez110f9a12018-01-31 09:57:17 -080044import com.android.settingslib.utils.ThreadUtils;
Jason Monkd819c312017-08-11 12:53:36 -040045import com.android.systemui.Dependency;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080046import com.android.systemui.R;
Joe Onorato10523b4d2010-10-25 10:42:46 -070047import com.android.systemui.SystemUI;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040048import com.android.systemui.broadcast.BroadcastDispatcher;
Robert Horvath121e0252019-11-19 14:09:48 +010049import com.android.systemui.statusbar.CommandQueue;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050050import com.android.systemui.statusbar.phone.StatusBar;
Jason Monkd819c312017-08-11 12:53:36 -040051
John Spurlockde84f0e2013-06-12 12:41:00 -040052import java.io.FileDescriptor;
53import java.io.PrintWriter;
Salvador Martinezfd38aa52018-03-28 23:56:59 -070054import java.time.Duration;
John Spurlockde84f0e2013-06-12 12:41:00 -040055import java.util.Arrays;
Salvador Martinez7ad2c172019-02-11 16:09:28 -080056import java.util.concurrent.Future;
John Spurlockde84f0e2013-06-12 12:41:00 -040057
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040058import javax.inject.Inject;
Dave Mankoff9a22a2f2019-10-23 16:12:30 -040059import javax.inject.Singleton;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040060
Dave Mankoff9a22a2f2019-10-23 16:12:30 -040061import dagger.Lazy;
62
63@Singleton
Robert Horvath121e0252019-11-19 14:09:48 +010064public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
Salvador Martinez4387bd52019-02-21 16:16:28 -080065
Joe Onorato10523b4d2010-10-25 10:42:46 -070066 static final String TAG = "PowerUI";
John Spurlock1bb480a2014-08-02 17:12:43 -040067 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080068 private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -080069 private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
70 private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
Salvador Martinezf9e47502018-01-04 13:45:48 -080071 static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
Salvador Martinezfd38aa52018-03-28 23:56:59 -070072 private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
73 private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
Salvador Martinez4387bd52019-02-21 16:16:28 -080074 public static final int NO_ESTIMATE_AVAILABLE = -1;
Sherry Huang9b786732019-08-03 02:36:44 +080075 private static final String BOOT_COUNT_KEY = "boot_count";
76 private static final String PREFS = "powerui_prefs";
Joe Onorato10523b4d2010-10-25 10:42:46 -070077
John Spurlocked452c52014-03-06 12:02:31 -050078 private final Handler mHandler = new Handler();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -040079 @VisibleForTesting
80 final Receiver mReceiver = new Receiver();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070081
John Spurlock3332ba52014-03-10 17:44:07 -040082 private PowerManager mPowerManager;
83 private WarningsUI mWarnings;
Robert Horvath121e0252019-11-19 14:09:48 +010084 private InattentiveSleepWarningView mOverlayView;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070085 private final Configuration mLastConfiguration = new Configuration();
John Spurlocked452c52014-03-06 12:02:31 -050086 private int mPlugType = 0;
87 private int mInvalidCharger = 0;
Salvador Martinezf9e47502018-01-04 13:45:48 -080088 private EnhancedEstimates mEnhancedEstimates;
Salvador Martinez7ad2c172019-02-11 16:09:28 -080089 private Future mLastShowWarningTask;
Sherry Huangce02ed32019-01-17 20:37:29 +080090 private boolean mEnableSkinTemperatureWarning;
91 private boolean mEnableUsbTemperatureAlarm;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070092
John Spurlocked452c52014-03-06 12:02:31 -050093 private int mLowBatteryAlertCloseLevel;
94 private final int[] mLowBatteryReminderLevels = new int[2];
Joe Onorato10523b4d2010-10-25 10:42:46 -070095
Daniel Sandlerdea64622013-09-23 16:05:57 -040096 private long mScreenOffTime = -1;
97
Salvador Martinez4387bd52019-02-21 16:16:28 -080098 @VisibleForTesting boolean mLowWarningShownThisChargeCycle;
99 @VisibleForTesting boolean mSevereWarningShownThisChargeCycle;
100 @VisibleForTesting BatteryStateSnapshot mCurrentBatteryStateSnapshot;
101 @VisibleForTesting BatteryStateSnapshot mLastBatteryStateSnapshot;
Wei Wangbf05e602018-11-21 11:46:48 -0800102 @VisibleForTesting IThermalService mThermalService;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800103
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700104 @VisibleForTesting int mBatteryLevel = 100;
Salvador Martinez36307962018-02-08 14:29:08 -0800105 @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
106
Sherry Huang18d27042019-03-19 21:45:28 +0800107 private IThermalEventListener mSkinThermalEventListener;
108 private IThermalEventListener mUsbThermalEventListener;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400109 private final BroadcastDispatcher mBroadcastDispatcher;
Robert Horvath121e0252019-11-19 14:09:48 +0100110 private final CommandQueue mCommandQueue;
Dave Mankoff9a22a2f2019-10-23 16:12:30 -0400111 private final Lazy<StatusBar> mStatusBarLazy;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400112
113 @Inject
Dave Mankoff9a22a2f2019-10-23 16:12:30 -0400114 public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
Robert Horvath121e0252019-11-19 14:09:48 +0100115 CommandQueue commandQueue, Lazy<StatusBar> statusBarLazy) {
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400116 super(context);
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400117 mBroadcastDispatcher = broadcastDispatcher;
Robert Horvath121e0252019-11-19 14:09:48 +0100118 mCommandQueue = commandQueue;
Dave Mankoff9a22a2f2019-10-23 16:12:30 -0400119 mStatusBarLazy = statusBarLazy;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400120 }
Sherry Huang18d27042019-03-19 21:45:28 +0800121
Joe Onorato10523b4d2010-10-25 10:42:46 -0700122 public void start() {
John Spurlock3332ba52014-03-10 17:44:07 -0400123 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
124 mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
Jason Monkd819c312017-08-11 12:53:36 -0400125 mWarnings = Dependency.get(WarningsUI.class);
Salvador Martinezf9e47502018-01-04 13:45:48 -0800126 mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700127 mLastConfiguration.setTo(mContext.getResources().getConfiguration());
Daniel Sandlerdea64622013-09-23 16:05:57 -0400128
Dianne Hackborn14272302014-06-10 23:13:02 -0700129 ContentObserver obs = new ContentObserver(mHandler) {
130 @Override
131 public void onChange(boolean selfChange) {
132 updateBatteryWarningLevels();
133 }
134 };
135 final ContentResolver resolver = mContext.getContentResolver();
136 resolver.registerContentObserver(Settings.Global.getUriFor(
137 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
138 false, obs, UserHandle.USER_ALL);
139 updateBatteryWarningLevels();
John Spurlock3332ba52014-03-10 17:44:07 -0400140 mReceiver.init();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800141
Salvador Martineza6f7b252017-04-10 10:46:15 -0700142 // Check to see if we need to let the user know that the phone previously shut down due
143 // to the temperature being too high.
Sherry Huang9b786732019-08-03 02:36:44 +0800144 showWarnOnThermalShutdown();
Salvador Martineza6f7b252017-04-10 10:46:15 -0700145
Sherry Huang18d27042019-03-19 21:45:28 +0800146 // Register an observer to configure mEnableSkinTemperatureWarning and perform the
147 // registration of skin thermal event listener upon Settings change.
148 resolver.registerContentObserver(
149 Settings.Global.getUriFor(Settings.Global.SHOW_TEMPERATURE_WARNING),
150 false /*notifyForDescendants*/,
151 new ContentObserver(mHandler) {
152 @Override
153 public void onChange(boolean selfChange) {
154 doSkinThermalEventListenerRegistration();
155 }
156 });
157 // Register an observer to configure mEnableUsbTemperatureAlarm and perform the
158 // registration of usb thermal event listener upon Settings change.
159 resolver.registerContentObserver(
160 Settings.Global.getUriFor(Settings.Global.SHOW_USB_TEMPERATURE_ALARM),
161 false /*notifyForDescendants*/,
162 new ContentObserver(mHandler) {
163 @Override
164 public void onChange(boolean selfChange) {
165 doUsbThermalEventListenerRegistration();
166 }
167 });
168 initThermalEventListeners();
Robert Horvath121e0252019-11-19 14:09:48 +0100169 mCommandQueue.addCallback(this);
John Spurlock3332ba52014-03-10 17:44:07 -0400170 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700171
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700172 @Override
173 protected void onConfigurationChanged(Configuration newConfig) {
174 final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
175
176 // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
177 if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
Sherry Huang18d27042019-03-19 21:45:28 +0800178 mHandler.post(this::initThermalEventListeners);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700179 }
180 }
181
Dianne Hackborn14272302014-06-10 23:13:02 -0700182 void updateBatteryWarningLevels() {
183 int critLevel = mContext.getResources().getInteger(
184 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Makoto Onuki16a0dd22018-03-20 10:40:37 -0700185 int warnLevel = mContext.getResources().getInteger(
Dianne Hackborn14272302014-06-10 23:13:02 -0700186 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Makoto Onuki4a9036b2018-01-11 14:48:20 -0800187
Dianne Hackborn14272302014-06-10 23:13:02 -0700188 if (warnLevel < critLevel) {
189 warnLevel = critLevel;
190 }
191
192 mLowBatteryReminderLevels[0] = warnLevel;
193 mLowBatteryReminderLevels[1] = critLevel;
194 mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
195 + mContext.getResources().getInteger(
196 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
197 }
198
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700199 /**
200 * Buckets the battery level.
201 *
202 * The code in this function is a little weird because I couldn't comprehend
203 * the bucket going up when the battery level was going down. --joeo
204 *
205 * 1 means that the battery is "ok"
206 * 0 means that the battery is between "ok" and what we should warn about.
207 * less than 0 means that the battery is low
208 */
209 private int findBatteryLevelBucket(int level) {
210 if (level >= mLowBatteryAlertCloseLevel) {
211 return 1;
212 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700213 if (level > mLowBatteryReminderLevels[0]) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700214 return 0;
215 }
216 final int N = mLowBatteryReminderLevels.length;
217 for (int i=N-1; i>=0; i--) {
218 if (level <= mLowBatteryReminderLevels[i]) {
219 return -1-i;
220 }
221 }
222 throw new RuntimeException("not possible!");
223 }
224
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400225 @VisibleForTesting
226 final class Receiver extends BroadcastReceiver {
John Spurlock3332ba52014-03-10 17:44:07 -0400227
228 public void init() {
229 // Register for Intent broadcasts for...
230 IntentFilter filter = new IntentFilter();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400231 filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
John Spurlock3332ba52014-03-10 17:44:07 -0400232 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
233 filter.addAction(Intent.ACTION_SCREEN_OFF);
234 filter.addAction(Intent.ACTION_SCREEN_ON);
John Spurlockecbc5e82014-10-22 09:05:51 -0400235 filter.addAction(Intent.ACTION_USER_SWITCHED);
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400236 mBroadcastDispatcher.registerReceiver(this, filter, mHandler);
John Spurlock3332ba52014-03-10 17:44:07 -0400237 }
238
Joe Onorato10523b4d2010-10-25 10:42:46 -0700239 @Override
240 public void onReceive(Context context, Intent intent) {
241 String action = intent.getAction();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400242 if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
243 ThreadUtils.postOnBackgroundThread(() -> {
244 if (mPowerManager.isPowerSaveMode()) {
245 mWarnings.dismissLowBatteryWarning();
246 }
247 });
248 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700249 final int oldBatteryLevel = mBatteryLevel;
250 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
251 final int oldBatteryStatus = mBatteryStatus;
252 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
253 BatteryManager.BATTERY_STATUS_UNKNOWN);
254 final int oldPlugType = mPlugType;
255 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
256 final int oldInvalidCharger = mInvalidCharger;
257 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
Salvador Martinez4387bd52019-02-21 16:16:28 -0800258 mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700259
260 final boolean plugged = mPlugType != 0;
261 final boolean oldPlugged = oldPlugType != 0;
262
263 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
264 int bucket = findBatteryLevelBucket(mBatteryLevel);
265
Daniel Sandler71986622011-07-26 13:06:49 -0400266 if (DEBUG) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400267 Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700268 + " .. " + mLowBatteryReminderLevels[0]
269 + " .. " + mLowBatteryReminderLevels[1]);
Daniel Sandlerdea64622013-09-23 16:05:57 -0400270 Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
271 Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
272 Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
273 Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
274 Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
275 Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700276 }
277
John Spurlocked452c52014-03-06 12:02:31 -0500278 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700279 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400280 Slog.d(TAG, "showing invalid charger warning");
John Spurlocked452c52014-03-06 12:02:31 -0500281 mWarnings.showInvalidChargerWarning();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700282 return;
283 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
John Spurlocked452c52014-03-06 12:02:31 -0500284 mWarnings.dismissInvalidChargerWarning();
285 } else if (mWarnings.isInvalidChargerWarningShowing()) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700286 // if invalid charger is showing, don't show low battery
Salvador Martinez4387bd52019-02-21 16:16:28 -0800287 if (DEBUG) {
288 Slog.d(TAG, "Bad Charger");
289 }
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700290 return;
291 }
292
Salvador Martinezf9e47502018-01-04 13:45:48 -0800293 // Show the correct version of low battery warning if needed
Salvador Martinez7ad2c172019-02-11 16:09:28 -0800294 if (mLastShowWarningTask != null) {
295 mLastShowWarningTask.cancel(true);
Salvador Martinez4387bd52019-02-21 16:16:28 -0800296 if (DEBUG) {
297 Slog.d(TAG, "cancelled task");
298 }
Salvador Martinez7ad2c172019-02-11 16:09:28 -0800299 }
300 mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800301 maybeShowBatteryWarningV2(
302 plugged, bucket);
Salvador Martinez110f9a12018-01-31 09:57:17 -0800303 });
Beverly334bc5f2017-07-31 10:37:17 -0400304
Daniel Sandlerdea64622013-09-23 16:05:57 -0400305 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
306 mScreenOffTime = SystemClock.elapsedRealtime();
307 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
308 mScreenOffTime = -1;
John Spurlockecbc5e82014-10-22 09:05:51 -0400309 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
310 mWarnings.userSwitched();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700311 } else {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400312 Slog.w(TAG, "unknown intent: " + intent);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700313 }
314 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800315 }
316
Salvador Martinez4387bd52019-02-21 16:16:28 -0800317 protected void maybeShowBatteryWarningV2(boolean plugged, int bucket) {
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700318 final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
Salvador Martinez4387bd52019-02-21 16:16:28 -0800319 final boolean isPowerSaverMode = mPowerManager.isPowerSaveMode();
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700320
Salvador Martinez4387bd52019-02-21 16:16:28 -0800321 // Stick current battery state into an immutable container to determine if we should show
322 // a warning.
323 if (DEBUG) {
324 Slog.d(TAG, "evaluating which notification to show");
325 }
326 if (hybridEnabled) {
327 if (DEBUG) {
328 Slog.d(TAG, "using hybrid");
329 }
330 Estimate estimate = refreshEstimateIfNeeded();
331 mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
332 plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
333 mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
Salvador Martinez580098fe2019-04-11 10:42:15 -0700334 estimate.getAverageDischargeTime(),
Salvador Martinez4387bd52019-02-21 16:16:28 -0800335 mEnhancedEstimates.getSevereWarningThreshold(),
Salvador Martinezd73c5aa2019-03-26 12:38:09 -0700336 mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage(),
337 mEnhancedEstimates.getLowWarningEnabled());
Salvador Martinez4387bd52019-02-21 16:16:28 -0800338 } else {
339 if (DEBUG) {
340 Slog.d(TAG, "using standard");
341 }
342 mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
343 plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
344 mLowBatteryReminderLevels[0]);
345 }
346
347 mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
348 if (mCurrentBatteryStateSnapshot.isHybrid()) {
349 maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
350 } else {
351 maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
352 }
353 }
354
355 // updates the time estimate if we don't have one or battery level has changed.
356 @VisibleForTesting
357 Estimate refreshEstimateIfNeeded() {
358 if (mLastBatteryStateSnapshot == null
359 || mLastBatteryStateSnapshot.getTimeRemainingMillis() == NO_ESTIMATE_AVAILABLE
360 || mBatteryLevel != mLastBatteryStateSnapshot.getBatteryLevel()) {
361 final Estimate estimate = mEnhancedEstimates.getEstimate();
362 if (DEBUG) {
363 Slog.d(TAG, "updated estimate: " + estimate.getEstimateMillis());
364 }
365 return estimate;
366 }
367 return new Estimate(mLastBatteryStateSnapshot.getTimeRemainingMillis(),
Salvador Martinez580098fe2019-04-11 10:42:15 -0700368 mLastBatteryStateSnapshot.isBasedOnUsage(),
369 mLastBatteryStateSnapshot.getAverageTimeToDischargeMillis());
Salvador Martinez4387bd52019-02-21 16:16:28 -0800370 }
371
372 @VisibleForTesting
373 void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
374 BatteryStateSnapshot lastSnapshot) {
375 // if we are now over 45% battery & 6 hours remaining so we can trigger hybrid
376 // notification again
377 if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET
378 && currentSnapshot.getTimeRemainingMillis() > SIX_HOURS_MILLIS) {
379 mLowWarningShownThisChargeCycle = false;
380 mSevereWarningShownThisChargeCycle = false;
381 if (DEBUG) {
382 Slog.d(TAG, "Charge cycle reset! Can show warnings again");
Salvador Martinezf9e47502018-01-04 13:45:48 -0800383 }
384 }
385
Salvador Martinez4387bd52019-02-21 16:16:28 -0800386 final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
387 || lastSnapshot.getPlugged();
Salvador Martinez36307962018-02-08 14:29:08 -0800388
Salvador Martinez4387bd52019-02-21 16:16:28 -0800389 if (shouldShowHybridWarning(currentSnapshot)) {
390 mWarnings.showLowBatteryWarning(playSound);
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700391 // mark if we've already shown a warning this cycle. This will prevent the notification
392 // trigger from spamming users by only showing low/critical warnings once per cycle
Salvador Martinez4387bd52019-02-21 16:16:28 -0800393 if (currentSnapshot.getTimeRemainingMillis()
Salvador Martinez290496e2019-04-04 10:30:19 -0700394 <= currentSnapshot.getSevereThresholdMillis()
395 || currentSnapshot.getBatteryLevel()
396 <= currentSnapshot.getSevereLevelThreshold()) {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800397 mSevereWarningShownThisChargeCycle = true;
398 mLowWarningShownThisChargeCycle = true;
399 if (DEBUG) {
400 Slog.d(TAG, "Severe warning marked as shown this cycle");
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700401 }
Salvador Martinez4387bd52019-02-21 16:16:28 -0800402 } else {
403 Slog.d(TAG, "Low warning marked as shown this cycle");
404 mLowWarningShownThisChargeCycle = true;
Salvador Martinez36307962018-02-08 14:29:08 -0800405 }
Salvador Martinez4387bd52019-02-21 16:16:28 -0800406 } else if (shouldDismissHybridWarning(currentSnapshot)) {
407 if (DEBUG) {
408 Slog.d(TAG, "Dismissing warning");
409 }
410 mWarnings.dismissLowBatteryWarning();
411 } else {
412 if (DEBUG) {
413 Slog.d(TAG, "Updating warning");
414 }
415 mWarnings.updateLowBatteryWarning();
416 }
417 }
418
419 @VisibleForTesting
420 boolean shouldShowHybridWarning(BatteryStateSnapshot snapshot) {
421 if (snapshot.getPlugged()
422 || snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN) {
423 Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged()
424 + " status unknown: "
425 + (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN));
426 return false;
427 }
428
Salvador Martinezd73c5aa2019-03-26 12:38:09 -0700429 // Only show the low warning if enabled once per charge cycle & no battery saver
430 final boolean canShowWarning = snapshot.isLowWarningEnabled()
431 && !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
Salvador Martinez4387bd52019-02-21 16:16:28 -0800432 && (snapshot.getTimeRemainingMillis() < snapshot.getLowThresholdMillis()
433 || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());
434
435 // Only show the severe warning once per charge cycle
436 final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
437 && (snapshot.getTimeRemainingMillis() < snapshot.getSevereThresholdMillis()
438 || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());
439
440 final boolean canShow = canShowWarning || canShowSevereWarning;
Salvador Martinezd73c5aa2019-03-26 12:38:09 -0700441
Salvador Martinez4387bd52019-02-21 16:16:28 -0800442 if (DEBUG) {
443 Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:"
444 + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
445 + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
446 + "\n" + snapshot.toString());
447 }
448 return canShow;
449 }
450
451 @VisibleForTesting
452 boolean shouldDismissHybridWarning(BatteryStateSnapshot snapshot) {
453 return snapshot.getPlugged()
454 || snapshot.getTimeRemainingMillis() > snapshot.getLowThresholdMillis();
455 }
456
457 protected void maybeShowBatteryWarning(
458 BatteryStateSnapshot currentSnapshot,
459 BatteryStateSnapshot lastSnapshot) {
460 final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
461 || lastSnapshot.getPlugged();
462
463 if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {
464 mWarnings.showLowBatteryWarning(playSound);
465 } else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {
Salvador Martinezf9e47502018-01-04 13:45:48 -0800466 mWarnings.dismissLowBatteryWarning();
467 } else {
468 mWarnings.updateLowBatteryWarning();
469 }
470 }
471
472 @VisibleForTesting
Salvador Martinez4387bd52019-02-21 16:16:28 -0800473 boolean shouldShowLowBatteryWarning(
474 BatteryStateSnapshot currentSnapshot,
475 BatteryStateSnapshot lastSnapshot) {
476 return !currentSnapshot.getPlugged()
477 && !currentSnapshot.isPowerSaver()
478 && (((currentSnapshot.getBucket() < lastSnapshot.getBucket()
479 || lastSnapshot.getPlugged())
480 && currentSnapshot.getBucket() < 0))
481 && currentSnapshot.getBatteryStatus() != BatteryManager.BATTERY_STATUS_UNKNOWN;
Salvador Martinezf9e47502018-01-04 13:45:48 -0800482 }
483
Salvador Martinezf9e47502018-01-04 13:45:48 -0800484 @VisibleForTesting
Salvador Martinez4387bd52019-02-21 16:16:28 -0800485 boolean shouldDismissLowBatteryWarning(
486 BatteryStateSnapshot currentSnapshot,
487 BatteryStateSnapshot lastSnapshot) {
488 return currentSnapshot.isPowerSaver()
489 || currentSnapshot.getPlugged()
490 || (currentSnapshot.getBucket() > lastSnapshot.getBucket()
491 && currentSnapshot.getBucket() > 0);
Salvador Martinez36307962018-02-08 14:29:08 -0800492 }
493
Sherry Huang18d27042019-03-19 21:45:28 +0800494 private void initThermalEventListeners() {
495 doSkinThermalEventListenerRegistration();
496 doUsbThermalEventListenerRegistration();
497 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800498
Sherry Huang18d27042019-03-19 21:45:28 +0800499 @VisibleForTesting
500 synchronized void doSkinThermalEventListenerRegistration() {
501 final boolean oldEnableSkinTemperatureWarning = mEnableSkinTemperatureWarning;
502 boolean ret = false;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700503
Sherry Huang18d27042019-03-19 21:45:28 +0800504 mEnableSkinTemperatureWarning = Settings.Global.getInt(mContext.getContentResolver(),
505 Settings.Global.SHOW_TEMPERATURE_WARNING,
506 mContext.getResources().getInteger(R.integer.config_showTemperatureWarning)) != 0;
Todd Poynorfb95e122017-10-04 13:00:32 -0700507
Sherry Huang18d27042019-03-19 21:45:28 +0800508 if (mEnableSkinTemperatureWarning != oldEnableSkinTemperatureWarning) {
509 try {
510 if (mSkinThermalEventListener == null) {
511 mSkinThermalEventListener = new SkinThermalEventListener();
512 }
513 if (mThermalService == null) {
514 mThermalService = IThermalService.Stub.asInterface(
515 ServiceManager.getService(Context.THERMAL_SERVICE));
516 }
517 if (mEnableSkinTemperatureWarning) {
518 ret = mThermalService.registerThermalEventListenerWithType(
519 mSkinThermalEventListener, Temperature.TYPE_SKIN);
520 } else {
521 ret = mThermalService.unregisterThermalEventListener(mSkinThermalEventListener);
522 }
523 } catch (RemoteException e) {
524 Slog.e(TAG, "Exception while (un)registering skin thermal event listener.", e);
525 }
526
527 if (!ret) {
528 mEnableSkinTemperatureWarning = !mEnableSkinTemperatureWarning;
529 Slog.e(TAG, "Failed to register or unregister skin thermal event listener.");
Todd Poynorfb95e122017-10-04 13:00:32 -0700530 }
531 }
Sherry Huangce02ed32019-01-17 20:37:29 +0800532 }
Todd Poynorfb95e122017-10-04 13:00:32 -0700533
Sherry Huangce02ed32019-01-17 20:37:29 +0800534 @VisibleForTesting
Sherry Huang18d27042019-03-19 21:45:28 +0800535 synchronized void doUsbThermalEventListenerRegistration() {
536 final boolean oldEnableUsbTemperatureAlarm = mEnableUsbTemperatureAlarm;
537 boolean ret = false;
538
539 mEnableUsbTemperatureAlarm = Settings.Global.getInt(mContext.getContentResolver(),
540 Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
541 mContext.getResources().getInteger(R.integer.config_showUsbPortAlarm)) != 0;
542
543 if (mEnableUsbTemperatureAlarm != oldEnableUsbTemperatureAlarm) {
544 try {
545 if (mUsbThermalEventListener == null) {
546 mUsbThermalEventListener = new UsbThermalEventListener();
547 }
548 if (mThermalService == null) {
549 mThermalService = IThermalService.Stub.asInterface(
550 ServiceManager.getService(Context.THERMAL_SERVICE));
551 }
552 if (mEnableUsbTemperatureAlarm) {
553 ret = mThermalService.registerThermalEventListenerWithType(
554 mUsbThermalEventListener, Temperature.TYPE_USB_PORT);
555 } else {
556 ret = mThermalService.unregisterThermalEventListener(mUsbThermalEventListener);
557 }
558 } catch (RemoteException e) {
559 Slog.e(TAG, "Exception while (un)registering usb thermal event listener.", e);
Sherry Huangce02ed32019-01-17 20:37:29 +0800560 }
Sherry Huang18d27042019-03-19 21:45:28 +0800561
562 if (!ret) {
563 mEnableUsbTemperatureAlarm = !mEnableUsbTemperatureAlarm;
564 Slog.e(TAG, "Failed to register or unregister usb thermal event listener.");
Sherry Huangce02ed32019-01-17 20:37:29 +0800565 }
Sherry Huangce02ed32019-01-17 20:37:29 +0800566 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800567 }
568
Sherry Huang9b786732019-08-03 02:36:44 +0800569 private void showWarnOnThermalShutdown() {
570 int bootCount = -1;
571 int lastReboot = mContext.getSharedPreferences(PREFS, 0).getInt(BOOT_COUNT_KEY, -1);
572 try {
573 bootCount = Settings.Global.getInt(mContext.getContentResolver(),
574 Settings.Global.BOOT_COUNT);
575 } catch (Settings.SettingNotFoundException e) {
576 Slog.e(TAG, "Failed to read system boot count from Settings.Global.BOOT_COUNT");
577 }
578 // Only show the thermal shutdown warning when there is a thermal reboot.
579 if (bootCount > lastReboot) {
580 mContext.getSharedPreferences(PREFS, 0).edit().putInt(BOOT_COUNT_KEY,
581 bootCount).apply();
582 if (mPowerManager.getLastShutdownReason()
583 == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
584 mWarnings.showThermalShutdownWarning();
585 }
Salvador Martineza6f7b252017-04-10 10:46:15 -0700586 }
587 }
588
Robert Horvath121e0252019-11-19 14:09:48 +0100589 @Override
590 public void showInattentiveSleepWarning() {
591 if (mOverlayView == null) {
592 mOverlayView = new InattentiveSleepWarningView(mContext);
593 }
594
595 mOverlayView.show();
596 }
597
598 @Override
599 public void dismissInattentiveSleepWarning(boolean animated) {
600 if (mOverlayView != null) {
601 mOverlayView.dismiss(animated);
602 }
603 }
604
Joe Onorato10523b4d2010-10-25 10:42:46 -0700605 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700606 pw.print("mLowBatteryAlertCloseLevel=");
607 pw.println(mLowBatteryAlertCloseLevel);
608 pw.print("mLowBatteryReminderLevels=");
609 pw.println(Arrays.toString(mLowBatteryReminderLevels));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700610 pw.print("mBatteryLevel=");
611 pw.println(Integer.toString(mBatteryLevel));
612 pw.print("mBatteryStatus=");
613 pw.println(Integer.toString(mBatteryStatus));
614 pw.print("mPlugType=");
615 pw.println(Integer.toString(mPlugType));
616 pw.print("mInvalidCharger=");
617 pw.println(Integer.toString(mInvalidCharger));
Daniel Sandlerdea64622013-09-23 16:05:57 -0400618 pw.print("mScreenOffTime=");
619 pw.print(mScreenOffTime);
620 if (mScreenOffTime >= 0) {
621 pw.print(" (");
622 pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
623 pw.print(" ago)");
624 }
625 pw.println();
626 pw.print("soundTimeout=");
627 pw.println(Settings.Global.getInt(mContext.getContentResolver(),
628 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700629 pw.print("bucket: ");
630 pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
Sherry Huangce02ed32019-01-17 20:37:29 +0800631 pw.print("mEnableSkinTemperatureWarning=");
632 pw.println(mEnableSkinTemperatureWarning);
633 pw.print("mEnableUsbTemperatureAlarm=");
634 pw.println(mEnableUsbTemperatureAlarm);
John Spurlocked452c52014-03-06 12:02:31 -0500635 mWarnings.dump(pw);
636 }
637
Salvador Martinez4387bd52019-02-21 16:16:28 -0800638 /**
639 * The interface to allow PowerUI to communicate with whatever implementation of WarningsUI
640 * is being used by the system.
641 */
John Spurlocked452c52014-03-06 12:02:31 -0500642 public interface WarningsUI {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800643
644 /**
645 * Updates battery and screen info for determining whether to trigger battery warnings or
646 * not.
647 * @param batteryLevel The current battery level
648 * @param bucket The current battery bucket
649 * @param screenOffTime How long the screen has been off in millis
650 */
John Spurlocked452c52014-03-06 12:02:31 -0500651 void update(int batteryLevel, int bucket, long screenOffTime);
Sherry Huangce02ed32019-01-17 20:37:29 +0800652
John Spurlocked452c52014-03-06 12:02:31 -0500653 void dismissLowBatteryWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800654
John Spurlocked452c52014-03-06 12:02:31 -0500655 void showLowBatteryWarning(boolean playSound);
Sherry Huangce02ed32019-01-17 20:37:29 +0800656
John Spurlocked452c52014-03-06 12:02:31 -0500657 void dismissInvalidChargerWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800658
John Spurlocked452c52014-03-06 12:02:31 -0500659 void showInvalidChargerWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800660
John Spurlocked452c52014-03-06 12:02:31 -0500661 void updateLowBatteryWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800662
John Spurlocked452c52014-03-06 12:02:31 -0500663 boolean isInvalidChargerWarningShowing();
Sherry Huangce02ed32019-01-17 20:37:29 +0800664
Salvador Martineza6f7b252017-04-10 10:46:15 -0700665 void dismissHighTemperatureWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800666
Salvador Martineza6f7b252017-04-10 10:46:15 -0700667 void showHighTemperatureWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800668
669 /**
Sherry Huang18d27042019-03-19 21:45:28 +0800670 * Display USB port overheat alarm
Sherry Huangce02ed32019-01-17 20:37:29 +0800671 */
672 void showUsbHighTemperatureAlarm();
673
Salvador Martineza6f7b252017-04-10 10:46:15 -0700674 void showThermalShutdownWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800675
John Spurlocked452c52014-03-06 12:02:31 -0500676 void dump(PrintWriter pw);
Sherry Huangce02ed32019-01-17 20:37:29 +0800677
John Spurlockecbc5e82014-10-22 09:05:51 -0400678 void userSwitched();
Salvador Martinez4387bd52019-02-21 16:16:28 -0800679
680 /**
681 * Updates the snapshot of battery state used for evaluating battery warnings
682 * @param snapshot object containing relevant values for making battery warning decisions.
683 */
684 void updateSnapshot(BatteryStateSnapshot snapshot);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700685 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700686
Sherry Huang18d27042019-03-19 21:45:28 +0800687 // Skin thermal event received from thermal service manager subsystem
Sherry Huangce02ed32019-01-17 20:37:29 +0800688 @VisibleForTesting
Sherry Huang18d27042019-03-19 21:45:28 +0800689 final class SkinThermalEventListener extends IThermalEventListener.Stub {
Wei Wangbad7c202018-11-01 11:57:39 -0700690 @Override public void notifyThrottling(Temperature temp) {
Sherry Huangce02ed32019-01-17 20:37:29 +0800691 int status = temp.getStatus();
692
693 if (status >= Temperature.THROTTLING_EMERGENCY) {
Dave Mankoff9a22a2f2019-10-23 16:12:30 -0400694 if (!mStatusBarLazy.get().isDeviceInVrMode()) {
Sherry Huangce02ed32019-01-17 20:37:29 +0800695 mWarnings.showHighTemperatureWarning();
Sherry Huang18d27042019-03-19 21:45:28 +0800696 Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
Sherry Huangce02ed32019-01-17 20:37:29 +0800697 + ", current skin status = " + status
698 + ", temperature = " + temp.getValue());
699 }
700 } else {
701 mWarnings.dismissHighTemperatureWarning();
702 }
703 }
704 }
705
Sherry Huang18d27042019-03-19 21:45:28 +0800706 // Usb thermal event received from thermal service manager subsystem
Sherry Huangce02ed32019-01-17 20:37:29 +0800707 @VisibleForTesting
Sherry Huang18d27042019-03-19 21:45:28 +0800708 final class UsbThermalEventListener extends IThermalEventListener.Stub {
Sherry Huangce02ed32019-01-17 20:37:29 +0800709 @Override public void notifyThrottling(Temperature temp) {
710 int status = temp.getStatus();
711
712 if (status >= Temperature.THROTTLING_EMERGENCY) {
713 mWarnings.showUsbHighTemperatureAlarm();
Sherry Huang18d27042019-03-19 21:45:28 +0800714 Slog.d(TAG, "UsbThermalEventListener: notifyThrottling was called "
Sherry Huangce02ed32019-01-17 20:37:29 +0800715 + ", current usb port status = " + status
716 + ", temperature = " + temp.getValue());
717 }
Todd Poynorfb95e122017-10-04 13:00:32 -0700718 }
719 }
720}