blob: f60d9db7ac9c317e04983697f7c1a69c7f1af81b [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;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050049import com.android.systemui.statusbar.phone.StatusBar;
Jason Monkd819c312017-08-11 12:53:36 -040050
John Spurlockde84f0e2013-06-12 12:41:00 -040051import java.io.FileDescriptor;
52import java.io.PrintWriter;
Salvador Martinezfd38aa52018-03-28 23:56:59 -070053import java.time.Duration;
John Spurlockde84f0e2013-06-12 12:41:00 -040054import java.util.Arrays;
Salvador Martinez7ad2c172019-02-11 16:09:28 -080055import java.util.concurrent.Future;
John Spurlockde84f0e2013-06-12 12:41:00 -040056
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040057import javax.inject.Inject;
Dave Mankoff9a22a2f2019-10-23 16:12:30 -040058import javax.inject.Singleton;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040059
Dave Mankoff9a22a2f2019-10-23 16:12:30 -040060import dagger.Lazy;
61
62@Singleton
Joe Onorato10523b4d2010-10-25 10:42:46 -070063public class PowerUI extends SystemUI {
Salvador Martinez4387bd52019-02-21 16:16:28 -080064
Joe Onorato10523b4d2010-10-25 10:42:46 -070065 static final String TAG = "PowerUI";
John Spurlock1bb480a2014-08-02 17:12:43 -040066 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -080067 private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
Andrew Sappersteina6ed7f82017-02-01 17:13:08 -080068 private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
69 private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
Salvador Martinezf9e47502018-01-04 13:45:48 -080070 static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
Salvador Martinezfd38aa52018-03-28 23:56:59 -070071 private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
72 private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
Salvador Martinez4387bd52019-02-21 16:16:28 -080073 public static final int NO_ESTIMATE_AVAILABLE = -1;
Sherry Huang9b786732019-08-03 02:36:44 +080074 private static final String BOOT_COUNT_KEY = "boot_count";
75 private static final String PREFS = "powerui_prefs";
Joe Onorato10523b4d2010-10-25 10:42:46 -070076
John Spurlocked452c52014-03-06 12:02:31 -050077 private final Handler mHandler = new Handler();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -040078 @VisibleForTesting
79 final Receiver mReceiver = new Receiver();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070080
John Spurlock3332ba52014-03-10 17:44:07 -040081 private PowerManager mPowerManager;
82 private WarningsUI mWarnings;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -070083 private final Configuration mLastConfiguration = new Configuration();
John Spurlocked452c52014-03-06 12:02:31 -050084 private int mPlugType = 0;
85 private int mInvalidCharger = 0;
Salvador Martinezf9e47502018-01-04 13:45:48 -080086 private EnhancedEstimates mEnhancedEstimates;
Salvador Martinez7ad2c172019-02-11 16:09:28 -080087 private Future mLastShowWarningTask;
Sherry Huangce02ed32019-01-17 20:37:29 +080088 private boolean mEnableSkinTemperatureWarning;
89 private boolean mEnableUsbTemperatureAlarm;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070090
John Spurlocked452c52014-03-06 12:02:31 -050091 private int mLowBatteryAlertCloseLevel;
92 private final int[] mLowBatteryReminderLevels = new int[2];
Joe Onorato10523b4d2010-10-25 10:42:46 -070093
Daniel Sandlerdea64622013-09-23 16:05:57 -040094 private long mScreenOffTime = -1;
95
Salvador Martinez4387bd52019-02-21 16:16:28 -080096 @VisibleForTesting boolean mLowWarningShownThisChargeCycle;
97 @VisibleForTesting boolean mSevereWarningShownThisChargeCycle;
98 @VisibleForTesting BatteryStateSnapshot mCurrentBatteryStateSnapshot;
99 @VisibleForTesting BatteryStateSnapshot mLastBatteryStateSnapshot;
Wei Wangbf05e602018-11-21 11:46:48 -0800100 @VisibleForTesting IThermalService mThermalService;
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800101
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700102 @VisibleForTesting int mBatteryLevel = 100;
Salvador Martinez36307962018-02-08 14:29:08 -0800103 @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
104
Sherry Huang18d27042019-03-19 21:45:28 +0800105 private IThermalEventListener mSkinThermalEventListener;
106 private IThermalEventListener mUsbThermalEventListener;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400107 private final BroadcastDispatcher mBroadcastDispatcher;
Dave Mankoff9a22a2f2019-10-23 16:12:30 -0400108 private final Lazy<StatusBar> mStatusBarLazy;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400109
110 @Inject
Dave Mankoff9a22a2f2019-10-23 16:12:30 -0400111 public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
112 Lazy<StatusBar> statusBarLazy) {
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400113 super(context);
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400114 mBroadcastDispatcher = broadcastDispatcher;
Dave Mankoff9a22a2f2019-10-23 16:12:30 -0400115 mStatusBarLazy = statusBarLazy;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400116 }
Sherry Huang18d27042019-03-19 21:45:28 +0800117
Joe Onorato10523b4d2010-10-25 10:42:46 -0700118 public void start() {
John Spurlock3332ba52014-03-10 17:44:07 -0400119 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
120 mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
Jason Monkd819c312017-08-11 12:53:36 -0400121 mWarnings = Dependency.get(WarningsUI.class);
Salvador Martinezf9e47502018-01-04 13:45:48 -0800122 mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700123 mLastConfiguration.setTo(mContext.getResources().getConfiguration());
Daniel Sandlerdea64622013-09-23 16:05:57 -0400124
Dianne Hackborn14272302014-06-10 23:13:02 -0700125 ContentObserver obs = new ContentObserver(mHandler) {
126 @Override
127 public void onChange(boolean selfChange) {
128 updateBatteryWarningLevels();
129 }
130 };
131 final ContentResolver resolver = mContext.getContentResolver();
132 resolver.registerContentObserver(Settings.Global.getUriFor(
133 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
134 false, obs, UserHandle.USER_ALL);
135 updateBatteryWarningLevels();
John Spurlock3332ba52014-03-10 17:44:07 -0400136 mReceiver.init();
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800137
Salvador Martineza6f7b252017-04-10 10:46:15 -0700138 // Check to see if we need to let the user know that the phone previously shut down due
139 // to the temperature being too high.
Sherry Huang9b786732019-08-03 02:36:44 +0800140 showWarnOnThermalShutdown();
Salvador Martineza6f7b252017-04-10 10:46:15 -0700141
Sherry Huang18d27042019-03-19 21:45:28 +0800142 // Register an observer to configure mEnableSkinTemperatureWarning and perform the
143 // registration of skin thermal event listener upon Settings change.
144 resolver.registerContentObserver(
145 Settings.Global.getUriFor(Settings.Global.SHOW_TEMPERATURE_WARNING),
146 false /*notifyForDescendants*/,
147 new ContentObserver(mHandler) {
148 @Override
149 public void onChange(boolean selfChange) {
150 doSkinThermalEventListenerRegistration();
151 }
152 });
153 // Register an observer to configure mEnableUsbTemperatureAlarm and perform the
154 // registration of usb thermal event listener upon Settings change.
155 resolver.registerContentObserver(
156 Settings.Global.getUriFor(Settings.Global.SHOW_USB_TEMPERATURE_ALARM),
157 false /*notifyForDescendants*/,
158 new ContentObserver(mHandler) {
159 @Override
160 public void onChange(boolean selfChange) {
161 doUsbThermalEventListenerRegistration();
162 }
163 });
164 initThermalEventListeners();
John Spurlock3332ba52014-03-10 17:44:07 -0400165 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700166
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700167 @Override
168 protected void onConfigurationChanged(Configuration newConfig) {
169 final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
170
171 // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
172 if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
Sherry Huang18d27042019-03-19 21:45:28 +0800173 mHandler.post(this::initThermalEventListeners);
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700174 }
175 }
176
Dianne Hackborn14272302014-06-10 23:13:02 -0700177 void updateBatteryWarningLevels() {
178 int critLevel = mContext.getResources().getInteger(
179 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Makoto Onuki16a0dd22018-03-20 10:40:37 -0700180 int warnLevel = mContext.getResources().getInteger(
Dianne Hackborn14272302014-06-10 23:13:02 -0700181 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Makoto Onuki4a9036b2018-01-11 14:48:20 -0800182
Dianne Hackborn14272302014-06-10 23:13:02 -0700183 if (warnLevel < critLevel) {
184 warnLevel = critLevel;
185 }
186
187 mLowBatteryReminderLevels[0] = warnLevel;
188 mLowBatteryReminderLevels[1] = critLevel;
189 mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
190 + mContext.getResources().getInteger(
191 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
192 }
193
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700194 /**
195 * Buckets the battery level.
196 *
197 * The code in this function is a little weird because I couldn't comprehend
198 * the bucket going up when the battery level was going down. --joeo
199 *
200 * 1 means that the battery is "ok"
201 * 0 means that the battery is between "ok" and what we should warn about.
202 * less than 0 means that the battery is low
203 */
204 private int findBatteryLevelBucket(int level) {
205 if (level >= mLowBatteryAlertCloseLevel) {
206 return 1;
207 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700208 if (level > mLowBatteryReminderLevels[0]) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700209 return 0;
210 }
211 final int N = mLowBatteryReminderLevels.length;
212 for (int i=N-1; i>=0; i--) {
213 if (level <= mLowBatteryReminderLevels[i]) {
214 return -1-i;
215 }
216 }
217 throw new RuntimeException("not possible!");
218 }
219
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400220 @VisibleForTesting
221 final class Receiver extends BroadcastReceiver {
John Spurlock3332ba52014-03-10 17:44:07 -0400222
223 public void init() {
224 // Register for Intent broadcasts for...
225 IntentFilter filter = new IntentFilter();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400226 filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
John Spurlock3332ba52014-03-10 17:44:07 -0400227 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
228 filter.addAction(Intent.ACTION_SCREEN_OFF);
229 filter.addAction(Intent.ACTION_SCREEN_ON);
John Spurlockecbc5e82014-10-22 09:05:51 -0400230 filter.addAction(Intent.ACTION_USER_SWITCHED);
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400231 mBroadcastDispatcher.registerReceiver(this, filter, mHandler);
John Spurlock3332ba52014-03-10 17:44:07 -0400232 }
233
Joe Onorato10523b4d2010-10-25 10:42:46 -0700234 @Override
235 public void onReceive(Context context, Intent intent) {
236 String action = intent.getAction();
Amin Shaikh2f6c45c2018-04-16 14:00:09 -0400237 if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
238 ThreadUtils.postOnBackgroundThread(() -> {
239 if (mPowerManager.isPowerSaveMode()) {
240 mWarnings.dismissLowBatteryWarning();
241 }
242 });
243 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700244 final int oldBatteryLevel = mBatteryLevel;
245 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
246 final int oldBatteryStatus = mBatteryStatus;
247 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
248 BatteryManager.BATTERY_STATUS_UNKNOWN);
249 final int oldPlugType = mPlugType;
250 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
251 final int oldInvalidCharger = mInvalidCharger;
252 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
Salvador Martinez4387bd52019-02-21 16:16:28 -0800253 mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700254
255 final boolean plugged = mPlugType != 0;
256 final boolean oldPlugged = oldPlugType != 0;
257
258 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
259 int bucket = findBatteryLevelBucket(mBatteryLevel);
260
Daniel Sandler71986622011-07-26 13:06:49 -0400261 if (DEBUG) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400262 Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700263 + " .. " + mLowBatteryReminderLevels[0]
264 + " .. " + mLowBatteryReminderLevels[1]);
Daniel Sandlerdea64622013-09-23 16:05:57 -0400265 Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
266 Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
267 Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
268 Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
269 Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
270 Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700271 }
272
John Spurlocked452c52014-03-06 12:02:31 -0500273 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700274 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400275 Slog.d(TAG, "showing invalid charger warning");
John Spurlocked452c52014-03-06 12:02:31 -0500276 mWarnings.showInvalidChargerWarning();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700277 return;
278 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
John Spurlocked452c52014-03-06 12:02:31 -0500279 mWarnings.dismissInvalidChargerWarning();
280 } else if (mWarnings.isInvalidChargerWarningShowing()) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700281 // if invalid charger is showing, don't show low battery
Salvador Martinez4387bd52019-02-21 16:16:28 -0800282 if (DEBUG) {
283 Slog.d(TAG, "Bad Charger");
284 }
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700285 return;
286 }
287
Salvador Martinezf9e47502018-01-04 13:45:48 -0800288 // Show the correct version of low battery warning if needed
Salvador Martinez7ad2c172019-02-11 16:09:28 -0800289 if (mLastShowWarningTask != null) {
290 mLastShowWarningTask.cancel(true);
Salvador Martinez4387bd52019-02-21 16:16:28 -0800291 if (DEBUG) {
292 Slog.d(TAG, "cancelled task");
293 }
Salvador Martinez7ad2c172019-02-11 16:09:28 -0800294 }
295 mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800296 maybeShowBatteryWarningV2(
297 plugged, bucket);
Salvador Martinez110f9a12018-01-31 09:57:17 -0800298 });
Beverly334bc5f2017-07-31 10:37:17 -0400299
Daniel Sandlerdea64622013-09-23 16:05:57 -0400300 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
301 mScreenOffTime = SystemClock.elapsedRealtime();
302 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
303 mScreenOffTime = -1;
John Spurlockecbc5e82014-10-22 09:05:51 -0400304 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
305 mWarnings.userSwitched();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700306 } else {
Daniel Sandlerdea64622013-09-23 16:05:57 -0400307 Slog.w(TAG, "unknown intent: " + intent);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700308 }
309 }
Salvador Martinezf9e47502018-01-04 13:45:48 -0800310 }
311
Salvador Martinez4387bd52019-02-21 16:16:28 -0800312 protected void maybeShowBatteryWarningV2(boolean plugged, int bucket) {
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700313 final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
Salvador Martinez4387bd52019-02-21 16:16:28 -0800314 final boolean isPowerSaverMode = mPowerManager.isPowerSaveMode();
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700315
Salvador Martinez4387bd52019-02-21 16:16:28 -0800316 // Stick current battery state into an immutable container to determine if we should show
317 // a warning.
318 if (DEBUG) {
319 Slog.d(TAG, "evaluating which notification to show");
320 }
321 if (hybridEnabled) {
322 if (DEBUG) {
323 Slog.d(TAG, "using hybrid");
324 }
325 Estimate estimate = refreshEstimateIfNeeded();
326 mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
327 plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
328 mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
Salvador Martinez580098fe2019-04-11 10:42:15 -0700329 estimate.getAverageDischargeTime(),
Salvador Martinez4387bd52019-02-21 16:16:28 -0800330 mEnhancedEstimates.getSevereWarningThreshold(),
Salvador Martinezd73c5aa2019-03-26 12:38:09 -0700331 mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage(),
332 mEnhancedEstimates.getLowWarningEnabled());
Salvador Martinez4387bd52019-02-21 16:16:28 -0800333 } else {
334 if (DEBUG) {
335 Slog.d(TAG, "using standard");
336 }
337 mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
338 plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
339 mLowBatteryReminderLevels[0]);
340 }
341
342 mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
343 if (mCurrentBatteryStateSnapshot.isHybrid()) {
344 maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
345 } else {
346 maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
347 }
348 }
349
350 // updates the time estimate if we don't have one or battery level has changed.
351 @VisibleForTesting
352 Estimate refreshEstimateIfNeeded() {
353 if (mLastBatteryStateSnapshot == null
354 || mLastBatteryStateSnapshot.getTimeRemainingMillis() == NO_ESTIMATE_AVAILABLE
355 || mBatteryLevel != mLastBatteryStateSnapshot.getBatteryLevel()) {
356 final Estimate estimate = mEnhancedEstimates.getEstimate();
357 if (DEBUG) {
358 Slog.d(TAG, "updated estimate: " + estimate.getEstimateMillis());
359 }
360 return estimate;
361 }
362 return new Estimate(mLastBatteryStateSnapshot.getTimeRemainingMillis(),
Salvador Martinez580098fe2019-04-11 10:42:15 -0700363 mLastBatteryStateSnapshot.isBasedOnUsage(),
364 mLastBatteryStateSnapshot.getAverageTimeToDischargeMillis());
Salvador Martinez4387bd52019-02-21 16:16:28 -0800365 }
366
367 @VisibleForTesting
368 void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
369 BatteryStateSnapshot lastSnapshot) {
370 // if we are now over 45% battery & 6 hours remaining so we can trigger hybrid
371 // notification again
372 if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET
373 && currentSnapshot.getTimeRemainingMillis() > SIX_HOURS_MILLIS) {
374 mLowWarningShownThisChargeCycle = false;
375 mSevereWarningShownThisChargeCycle = false;
376 if (DEBUG) {
377 Slog.d(TAG, "Charge cycle reset! Can show warnings again");
Salvador Martinezf9e47502018-01-04 13:45:48 -0800378 }
379 }
380
Salvador Martinez4387bd52019-02-21 16:16:28 -0800381 final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
382 || lastSnapshot.getPlugged();
Salvador Martinez36307962018-02-08 14:29:08 -0800383
Salvador Martinez4387bd52019-02-21 16:16:28 -0800384 if (shouldShowHybridWarning(currentSnapshot)) {
385 mWarnings.showLowBatteryWarning(playSound);
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700386 // mark if we've already shown a warning this cycle. This will prevent the notification
387 // trigger from spamming users by only showing low/critical warnings once per cycle
Salvador Martinez4387bd52019-02-21 16:16:28 -0800388 if (currentSnapshot.getTimeRemainingMillis()
Salvador Martinez290496e2019-04-04 10:30:19 -0700389 <= currentSnapshot.getSevereThresholdMillis()
390 || currentSnapshot.getBatteryLevel()
391 <= currentSnapshot.getSevereLevelThreshold()) {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800392 mSevereWarningShownThisChargeCycle = true;
393 mLowWarningShownThisChargeCycle = true;
394 if (DEBUG) {
395 Slog.d(TAG, "Severe warning marked as shown this cycle");
Salvador Martinezfd38aa52018-03-28 23:56:59 -0700396 }
Salvador Martinez4387bd52019-02-21 16:16:28 -0800397 } else {
398 Slog.d(TAG, "Low warning marked as shown this cycle");
399 mLowWarningShownThisChargeCycle = true;
Salvador Martinez36307962018-02-08 14:29:08 -0800400 }
Salvador Martinez4387bd52019-02-21 16:16:28 -0800401 } else if (shouldDismissHybridWarning(currentSnapshot)) {
402 if (DEBUG) {
403 Slog.d(TAG, "Dismissing warning");
404 }
405 mWarnings.dismissLowBatteryWarning();
406 } else {
407 if (DEBUG) {
408 Slog.d(TAG, "Updating warning");
409 }
410 mWarnings.updateLowBatteryWarning();
411 }
412 }
413
414 @VisibleForTesting
415 boolean shouldShowHybridWarning(BatteryStateSnapshot snapshot) {
416 if (snapshot.getPlugged()
417 || snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN) {
418 Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged()
419 + " status unknown: "
420 + (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN));
421 return false;
422 }
423
Salvador Martinezd73c5aa2019-03-26 12:38:09 -0700424 // Only show the low warning if enabled once per charge cycle & no battery saver
425 final boolean canShowWarning = snapshot.isLowWarningEnabled()
426 && !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
Salvador Martinez4387bd52019-02-21 16:16:28 -0800427 && (snapshot.getTimeRemainingMillis() < snapshot.getLowThresholdMillis()
428 || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());
429
430 // Only show the severe warning once per charge cycle
431 final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
432 && (snapshot.getTimeRemainingMillis() < snapshot.getSevereThresholdMillis()
433 || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());
434
435 final boolean canShow = canShowWarning || canShowSevereWarning;
Salvador Martinezd73c5aa2019-03-26 12:38:09 -0700436
Salvador Martinez4387bd52019-02-21 16:16:28 -0800437 if (DEBUG) {
438 Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:"
439 + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
440 + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
441 + "\n" + snapshot.toString());
442 }
443 return canShow;
444 }
445
446 @VisibleForTesting
447 boolean shouldDismissHybridWarning(BatteryStateSnapshot snapshot) {
448 return snapshot.getPlugged()
449 || snapshot.getTimeRemainingMillis() > snapshot.getLowThresholdMillis();
450 }
451
452 protected void maybeShowBatteryWarning(
453 BatteryStateSnapshot currentSnapshot,
454 BatteryStateSnapshot lastSnapshot) {
455 final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
456 || lastSnapshot.getPlugged();
457
458 if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {
459 mWarnings.showLowBatteryWarning(playSound);
460 } else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {
Salvador Martinezf9e47502018-01-04 13:45:48 -0800461 mWarnings.dismissLowBatteryWarning();
462 } else {
463 mWarnings.updateLowBatteryWarning();
464 }
465 }
466
467 @VisibleForTesting
Salvador Martinez4387bd52019-02-21 16:16:28 -0800468 boolean shouldShowLowBatteryWarning(
469 BatteryStateSnapshot currentSnapshot,
470 BatteryStateSnapshot lastSnapshot) {
471 return !currentSnapshot.getPlugged()
472 && !currentSnapshot.isPowerSaver()
473 && (((currentSnapshot.getBucket() < lastSnapshot.getBucket()
474 || lastSnapshot.getPlugged())
475 && currentSnapshot.getBucket() < 0))
476 && currentSnapshot.getBatteryStatus() != BatteryManager.BATTERY_STATUS_UNKNOWN;
Salvador Martinezf9e47502018-01-04 13:45:48 -0800477 }
478
Salvador Martinezf9e47502018-01-04 13:45:48 -0800479 @VisibleForTesting
Salvador Martinez4387bd52019-02-21 16:16:28 -0800480 boolean shouldDismissLowBatteryWarning(
481 BatteryStateSnapshot currentSnapshot,
482 BatteryStateSnapshot lastSnapshot) {
483 return currentSnapshot.isPowerSaver()
484 || currentSnapshot.getPlugged()
485 || (currentSnapshot.getBucket() > lastSnapshot.getBucket()
486 && currentSnapshot.getBucket() > 0);
Salvador Martinez36307962018-02-08 14:29:08 -0800487 }
488
Sherry Huang18d27042019-03-19 21:45:28 +0800489 private void initThermalEventListeners() {
490 doSkinThermalEventListenerRegistration();
491 doUsbThermalEventListenerRegistration();
492 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800493
Sherry Huang18d27042019-03-19 21:45:28 +0800494 @VisibleForTesting
495 synchronized void doSkinThermalEventListenerRegistration() {
496 final boolean oldEnableSkinTemperatureWarning = mEnableSkinTemperatureWarning;
497 boolean ret = false;
Adam Lesinski1aab3fa2017-07-31 13:42:00 -0700498
Sherry Huang18d27042019-03-19 21:45:28 +0800499 mEnableSkinTemperatureWarning = Settings.Global.getInt(mContext.getContentResolver(),
500 Settings.Global.SHOW_TEMPERATURE_WARNING,
501 mContext.getResources().getInteger(R.integer.config_showTemperatureWarning)) != 0;
Todd Poynorfb95e122017-10-04 13:00:32 -0700502
Sherry Huang18d27042019-03-19 21:45:28 +0800503 if (mEnableSkinTemperatureWarning != oldEnableSkinTemperatureWarning) {
504 try {
505 if (mSkinThermalEventListener == null) {
506 mSkinThermalEventListener = new SkinThermalEventListener();
507 }
508 if (mThermalService == null) {
509 mThermalService = IThermalService.Stub.asInterface(
510 ServiceManager.getService(Context.THERMAL_SERVICE));
511 }
512 if (mEnableSkinTemperatureWarning) {
513 ret = mThermalService.registerThermalEventListenerWithType(
514 mSkinThermalEventListener, Temperature.TYPE_SKIN);
515 } else {
516 ret = mThermalService.unregisterThermalEventListener(mSkinThermalEventListener);
517 }
518 } catch (RemoteException e) {
519 Slog.e(TAG, "Exception while (un)registering skin thermal event listener.", e);
520 }
521
522 if (!ret) {
523 mEnableSkinTemperatureWarning = !mEnableSkinTemperatureWarning;
524 Slog.e(TAG, "Failed to register or unregister skin thermal event listener.");
Todd Poynorfb95e122017-10-04 13:00:32 -0700525 }
526 }
Sherry Huangce02ed32019-01-17 20:37:29 +0800527 }
Todd Poynorfb95e122017-10-04 13:00:32 -0700528
Sherry Huangce02ed32019-01-17 20:37:29 +0800529 @VisibleForTesting
Sherry Huang18d27042019-03-19 21:45:28 +0800530 synchronized void doUsbThermalEventListenerRegistration() {
531 final boolean oldEnableUsbTemperatureAlarm = mEnableUsbTemperatureAlarm;
532 boolean ret = false;
533
534 mEnableUsbTemperatureAlarm = Settings.Global.getInt(mContext.getContentResolver(),
535 Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
536 mContext.getResources().getInteger(R.integer.config_showUsbPortAlarm)) != 0;
537
538 if (mEnableUsbTemperatureAlarm != oldEnableUsbTemperatureAlarm) {
539 try {
540 if (mUsbThermalEventListener == null) {
541 mUsbThermalEventListener = new UsbThermalEventListener();
542 }
543 if (mThermalService == null) {
544 mThermalService = IThermalService.Stub.asInterface(
545 ServiceManager.getService(Context.THERMAL_SERVICE));
546 }
547 if (mEnableUsbTemperatureAlarm) {
548 ret = mThermalService.registerThermalEventListenerWithType(
549 mUsbThermalEventListener, Temperature.TYPE_USB_PORT);
550 } else {
551 ret = mThermalService.unregisterThermalEventListener(mUsbThermalEventListener);
552 }
553 } catch (RemoteException e) {
554 Slog.e(TAG, "Exception while (un)registering usb thermal event listener.", e);
Sherry Huangce02ed32019-01-17 20:37:29 +0800555 }
Sherry Huang18d27042019-03-19 21:45:28 +0800556
557 if (!ret) {
558 mEnableUsbTemperatureAlarm = !mEnableUsbTemperatureAlarm;
559 Slog.e(TAG, "Failed to register or unregister usb thermal event listener.");
Sherry Huangce02ed32019-01-17 20:37:29 +0800560 }
Sherry Huangce02ed32019-01-17 20:37:29 +0800561 }
Andrew Sappersteinb7caf1d2016-12-14 15:39:20 -0800562 }
563
Sherry Huang9b786732019-08-03 02:36:44 +0800564 private void showWarnOnThermalShutdown() {
565 int bootCount = -1;
566 int lastReboot = mContext.getSharedPreferences(PREFS, 0).getInt(BOOT_COUNT_KEY, -1);
567 try {
568 bootCount = Settings.Global.getInt(mContext.getContentResolver(),
569 Settings.Global.BOOT_COUNT);
570 } catch (Settings.SettingNotFoundException e) {
571 Slog.e(TAG, "Failed to read system boot count from Settings.Global.BOOT_COUNT");
572 }
573 // Only show the thermal shutdown warning when there is a thermal reboot.
574 if (bootCount > lastReboot) {
575 mContext.getSharedPreferences(PREFS, 0).edit().putInt(BOOT_COUNT_KEY,
576 bootCount).apply();
577 if (mPowerManager.getLastShutdownReason()
578 == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
579 mWarnings.showThermalShutdownWarning();
580 }
Salvador Martineza6f7b252017-04-10 10:46:15 -0700581 }
582 }
583
Joe Onorato10523b4d2010-10-25 10:42:46 -0700584 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700585 pw.print("mLowBatteryAlertCloseLevel=");
586 pw.println(mLowBatteryAlertCloseLevel);
587 pw.print("mLowBatteryReminderLevels=");
588 pw.println(Arrays.toString(mLowBatteryReminderLevels));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700589 pw.print("mBatteryLevel=");
590 pw.println(Integer.toString(mBatteryLevel));
591 pw.print("mBatteryStatus=");
592 pw.println(Integer.toString(mBatteryStatus));
593 pw.print("mPlugType=");
594 pw.println(Integer.toString(mPlugType));
595 pw.print("mInvalidCharger=");
596 pw.println(Integer.toString(mInvalidCharger));
Daniel Sandlerdea64622013-09-23 16:05:57 -0400597 pw.print("mScreenOffTime=");
598 pw.print(mScreenOffTime);
599 if (mScreenOffTime >= 0) {
600 pw.print(" (");
601 pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
602 pw.print(" ago)");
603 }
604 pw.println();
605 pw.print("soundTimeout=");
606 pw.println(Settings.Global.getInt(mContext.getContentResolver(),
607 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700608 pw.print("bucket: ");
609 pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
Sherry Huangce02ed32019-01-17 20:37:29 +0800610 pw.print("mEnableSkinTemperatureWarning=");
611 pw.println(mEnableSkinTemperatureWarning);
612 pw.print("mEnableUsbTemperatureAlarm=");
613 pw.println(mEnableUsbTemperatureAlarm);
John Spurlocked452c52014-03-06 12:02:31 -0500614 mWarnings.dump(pw);
615 }
616
Salvador Martinez4387bd52019-02-21 16:16:28 -0800617 /**
618 * The interface to allow PowerUI to communicate with whatever implementation of WarningsUI
619 * is being used by the system.
620 */
John Spurlocked452c52014-03-06 12:02:31 -0500621 public interface WarningsUI {
Salvador Martinez4387bd52019-02-21 16:16:28 -0800622
623 /**
624 * Updates battery and screen info for determining whether to trigger battery warnings or
625 * not.
626 * @param batteryLevel The current battery level
627 * @param bucket The current battery bucket
628 * @param screenOffTime How long the screen has been off in millis
629 */
John Spurlocked452c52014-03-06 12:02:31 -0500630 void update(int batteryLevel, int bucket, long screenOffTime);
Sherry Huangce02ed32019-01-17 20:37:29 +0800631
John Spurlocked452c52014-03-06 12:02:31 -0500632 void dismissLowBatteryWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800633
John Spurlocked452c52014-03-06 12:02:31 -0500634 void showLowBatteryWarning(boolean playSound);
Sherry Huangce02ed32019-01-17 20:37:29 +0800635
John Spurlocked452c52014-03-06 12:02:31 -0500636 void dismissInvalidChargerWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800637
John Spurlocked452c52014-03-06 12:02:31 -0500638 void showInvalidChargerWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800639
John Spurlocked452c52014-03-06 12:02:31 -0500640 void updateLowBatteryWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800641
John Spurlocked452c52014-03-06 12:02:31 -0500642 boolean isInvalidChargerWarningShowing();
Sherry Huangce02ed32019-01-17 20:37:29 +0800643
Salvador Martineza6f7b252017-04-10 10:46:15 -0700644 void dismissHighTemperatureWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800645
Salvador Martineza6f7b252017-04-10 10:46:15 -0700646 void showHighTemperatureWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800647
648 /**
Sherry Huang18d27042019-03-19 21:45:28 +0800649 * Display USB port overheat alarm
Sherry Huangce02ed32019-01-17 20:37:29 +0800650 */
651 void showUsbHighTemperatureAlarm();
652
Salvador Martineza6f7b252017-04-10 10:46:15 -0700653 void showThermalShutdownWarning();
Sherry Huangce02ed32019-01-17 20:37:29 +0800654
John Spurlocked452c52014-03-06 12:02:31 -0500655 void dump(PrintWriter pw);
Sherry Huangce02ed32019-01-17 20:37:29 +0800656
John Spurlockecbc5e82014-10-22 09:05:51 -0400657 void userSwitched();
Salvador Martinez4387bd52019-02-21 16:16:28 -0800658
659 /**
660 * Updates the snapshot of battery state used for evaluating battery warnings
661 * @param snapshot object containing relevant values for making battery warning decisions.
662 */
663 void updateSnapshot(BatteryStateSnapshot snapshot);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700664 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700665
Sherry Huang18d27042019-03-19 21:45:28 +0800666 // Skin thermal event received from thermal service manager subsystem
Sherry Huangce02ed32019-01-17 20:37:29 +0800667 @VisibleForTesting
Sherry Huang18d27042019-03-19 21:45:28 +0800668 final class SkinThermalEventListener extends IThermalEventListener.Stub {
Wei Wangbad7c202018-11-01 11:57:39 -0700669 @Override public void notifyThrottling(Temperature temp) {
Sherry Huangce02ed32019-01-17 20:37:29 +0800670 int status = temp.getStatus();
671
672 if (status >= Temperature.THROTTLING_EMERGENCY) {
Dave Mankoff9a22a2f2019-10-23 16:12:30 -0400673 if (!mStatusBarLazy.get().isDeviceInVrMode()) {
Sherry Huangce02ed32019-01-17 20:37:29 +0800674 mWarnings.showHighTemperatureWarning();
Sherry Huang18d27042019-03-19 21:45:28 +0800675 Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
Sherry Huangce02ed32019-01-17 20:37:29 +0800676 + ", current skin status = " + status
677 + ", temperature = " + temp.getValue());
678 }
679 } else {
680 mWarnings.dismissHighTemperatureWarning();
681 }
682 }
683 }
684
Sherry Huang18d27042019-03-19 21:45:28 +0800685 // Usb thermal event received from thermal service manager subsystem
Sherry Huangce02ed32019-01-17 20:37:29 +0800686 @VisibleForTesting
Sherry Huang18d27042019-03-19 21:45:28 +0800687 final class UsbThermalEventListener extends IThermalEventListener.Stub {
Sherry Huangce02ed32019-01-17 20:37:29 +0800688 @Override public void notifyThrottling(Temperature temp) {
689 int status = temp.getStatus();
690
691 if (status >= Temperature.THROTTLING_EMERGENCY) {
692 mWarnings.showUsbHighTemperatureAlarm();
Sherry Huang18d27042019-03-19 21:45:28 +0800693 Slog.d(TAG, "UsbThermalEventListener: notifyThrottling was called "
Sherry Huangce02ed32019-01-17 20:37:29 +0800694 + ", current usb port status = " + status
695 + ", temperature = " + temp.getValue());
696 }
Todd Poynorfb95e122017-10-04 13:00:32 -0700697 }
698 }
699}