• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.power;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.ActivityInfo;
25 import android.content.res.Configuration;
26 import android.database.ContentObserver;
27 import android.os.BatteryManager;
28 import android.os.Handler;
29 import android.os.IThermalEventListener;
30 import android.os.IThermalService;
31 import android.os.PowerManager;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.os.SystemClock;
35 import android.os.Temperature;
36 import android.os.UserHandle;
37 import android.provider.Settings;
38 import android.text.format.DateUtils;
39 import android.util.Log;
40 import android.util.Slog;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.settingslib.fuelgauge.Estimate;
44 import com.android.settingslib.utils.ThreadUtils;
45 import com.android.systemui.Dependency;
46 import com.android.systemui.R;
47 import com.android.systemui.SystemUI;
48 import com.android.systemui.statusbar.phone.StatusBar;
49 
50 import java.io.FileDescriptor;
51 import java.io.PrintWriter;
52 import java.time.Duration;
53 import java.util.Arrays;
54 import java.util.concurrent.Future;
55 
56 public class PowerUI extends SystemUI {
57 
58     static final String TAG = "PowerUI";
59     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
60     private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
61     private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
62     private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
63     static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
64     private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
65     private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
66     public static final int NO_ESTIMATE_AVAILABLE = -1;
67 
68     private final Handler mHandler = new Handler();
69     @VisibleForTesting
70     final Receiver mReceiver = new Receiver();
71 
72     private PowerManager mPowerManager;
73     private WarningsUI mWarnings;
74     private final Configuration mLastConfiguration = new Configuration();
75     private int mPlugType = 0;
76     private int mInvalidCharger = 0;
77     private EnhancedEstimates mEnhancedEstimates;
78     private Future mLastShowWarningTask;
79     private boolean mEnableSkinTemperatureWarning;
80     private boolean mEnableUsbTemperatureAlarm;
81 
82     private int mLowBatteryAlertCloseLevel;
83     private final int[] mLowBatteryReminderLevels = new int[2];
84 
85     private long mScreenOffTime = -1;
86 
87     @VisibleForTesting boolean mLowWarningShownThisChargeCycle;
88     @VisibleForTesting boolean mSevereWarningShownThisChargeCycle;
89     @VisibleForTesting BatteryStateSnapshot mCurrentBatteryStateSnapshot;
90     @VisibleForTesting BatteryStateSnapshot mLastBatteryStateSnapshot;
91     @VisibleForTesting IThermalService mThermalService;
92 
93     @VisibleForTesting int mBatteryLevel = 100;
94     @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
95 
96     private IThermalEventListener mSkinThermalEventListener;
97     private IThermalEventListener mUsbThermalEventListener;
98 
start()99     public void start() {
100         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
101         mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
102         mWarnings = Dependency.get(WarningsUI.class);
103         mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
104         mLastConfiguration.setTo(mContext.getResources().getConfiguration());
105 
106         ContentObserver obs = new ContentObserver(mHandler) {
107             @Override
108             public void onChange(boolean selfChange) {
109                 updateBatteryWarningLevels();
110             }
111         };
112         final ContentResolver resolver = mContext.getContentResolver();
113         resolver.registerContentObserver(Settings.Global.getUriFor(
114                 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
115                 false, obs, UserHandle.USER_ALL);
116         updateBatteryWarningLevels();
117         mReceiver.init();
118 
119         // Check to see if we need to let the user know that the phone previously shut down due
120         // to the temperature being too high.
121         showThermalShutdownDialog();
122 
123         // Register an observer to configure mEnableSkinTemperatureWarning and perform the
124         // registration of skin thermal event listener upon Settings change.
125         resolver.registerContentObserver(
126                 Settings.Global.getUriFor(Settings.Global.SHOW_TEMPERATURE_WARNING),
127                 false /*notifyForDescendants*/,
128                 new ContentObserver(mHandler) {
129                     @Override
130                     public void onChange(boolean selfChange) {
131                         doSkinThermalEventListenerRegistration();
132                     }
133                 });
134         // Register an observer to configure mEnableUsbTemperatureAlarm and perform the
135         // registration of usb thermal event listener upon Settings change.
136         resolver.registerContentObserver(
137                 Settings.Global.getUriFor(Settings.Global.SHOW_USB_TEMPERATURE_ALARM),
138                 false /*notifyForDescendants*/,
139                 new ContentObserver(mHandler) {
140                     @Override
141                     public void onChange(boolean selfChange) {
142                         doUsbThermalEventListenerRegistration();
143                     }
144                 });
145         initThermalEventListeners();
146     }
147 
148     @Override
onConfigurationChanged(Configuration newConfig)149     protected void onConfigurationChanged(Configuration newConfig) {
150         final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
151 
152         // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
153         if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
154             mHandler.post(this::initThermalEventListeners);
155         }
156     }
157 
updateBatteryWarningLevels()158     void updateBatteryWarningLevels() {
159         int critLevel = mContext.getResources().getInteger(
160                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
161         int warnLevel = mContext.getResources().getInteger(
162                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
163 
164         if (warnLevel < critLevel) {
165             warnLevel = critLevel;
166         }
167 
168         mLowBatteryReminderLevels[0] = warnLevel;
169         mLowBatteryReminderLevels[1] = critLevel;
170         mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
171                 + mContext.getResources().getInteger(
172                         com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
173     }
174 
175     /**
176      * Buckets the battery level.
177      *
178      * The code in this function is a little weird because I couldn't comprehend
179      * the bucket going up when the battery level was going down. --joeo
180      *
181      * 1 means that the battery is "ok"
182      * 0 means that the battery is between "ok" and what we should warn about.
183      * less than 0 means that the battery is low
184      */
findBatteryLevelBucket(int level)185     private int findBatteryLevelBucket(int level) {
186         if (level >= mLowBatteryAlertCloseLevel) {
187             return 1;
188         }
189         if (level > mLowBatteryReminderLevels[0]) {
190             return 0;
191         }
192         final int N = mLowBatteryReminderLevels.length;
193         for (int i=N-1; i>=0; i--) {
194             if (level <= mLowBatteryReminderLevels[i]) {
195                 return -1-i;
196             }
197         }
198         throw new RuntimeException("not possible!");
199     }
200 
201     @VisibleForTesting
202     final class Receiver extends BroadcastReceiver {
203 
init()204         public void init() {
205             // Register for Intent broadcasts for...
206             IntentFilter filter = new IntentFilter();
207             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
208             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
209             filter.addAction(Intent.ACTION_SCREEN_OFF);
210             filter.addAction(Intent.ACTION_SCREEN_ON);
211             filter.addAction(Intent.ACTION_USER_SWITCHED);
212             mContext.registerReceiver(this, filter, null, mHandler);
213         }
214 
215         @Override
onReceive(Context context, Intent intent)216         public void onReceive(Context context, Intent intent) {
217             String action = intent.getAction();
218             if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
219                 ThreadUtils.postOnBackgroundThread(() -> {
220                     if (mPowerManager.isPowerSaveMode()) {
221                         mWarnings.dismissLowBatteryWarning();
222                     }
223                 });
224             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
225                 final int oldBatteryLevel = mBatteryLevel;
226                 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
227                 final int oldBatteryStatus = mBatteryStatus;
228                 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
229                         BatteryManager.BATTERY_STATUS_UNKNOWN);
230                 final int oldPlugType = mPlugType;
231                 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
232                 final int oldInvalidCharger = mInvalidCharger;
233                 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
234                 mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;
235 
236                 final boolean plugged = mPlugType != 0;
237                 final boolean oldPlugged = oldPlugType != 0;
238 
239                 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
240                 int bucket = findBatteryLevelBucket(mBatteryLevel);
241 
242                 if (DEBUG) {
243                     Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
244                             + " .. " + mLowBatteryReminderLevels[0]
245                             + " .. " + mLowBatteryReminderLevels[1]);
246                     Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
247                     Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
248                     Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
249                     Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
250                     Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
251                     Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
252                 }
253 
254                 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
255                 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
256                     Slog.d(TAG, "showing invalid charger warning");
257                     mWarnings.showInvalidChargerWarning();
258                     return;
259                 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
260                     mWarnings.dismissInvalidChargerWarning();
261                 } else if (mWarnings.isInvalidChargerWarningShowing()) {
262                     // if invalid charger is showing, don't show low battery
263                     if (DEBUG) {
264                         Slog.d(TAG, "Bad Charger");
265                     }
266                     return;
267                 }
268 
269                 // Show the correct version of low battery warning if needed
270                 if (mLastShowWarningTask != null) {
271                     mLastShowWarningTask.cancel(true);
272                     if (DEBUG) {
273                         Slog.d(TAG, "cancelled task");
274                     }
275                 }
276                 mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
277                     maybeShowBatteryWarningV2(
278                             plugged, bucket);
279                 });
280 
281             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
282                 mScreenOffTime = SystemClock.elapsedRealtime();
283             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
284                 mScreenOffTime = -1;
285             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
286                 mWarnings.userSwitched();
287             } else {
288                 Slog.w(TAG, "unknown intent: " + intent);
289             }
290         }
291     }
292 
maybeShowBatteryWarningV2(boolean plugged, int bucket)293     protected void maybeShowBatteryWarningV2(boolean plugged, int bucket) {
294         final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
295         final boolean isPowerSaverMode = mPowerManager.isPowerSaveMode();
296 
297         // Stick current battery state into an immutable container to determine if we should show
298         // a warning.
299         if (DEBUG) {
300             Slog.d(TAG, "evaluating which notification to show");
301         }
302         if (hybridEnabled) {
303             if (DEBUG) {
304                 Slog.d(TAG, "using hybrid");
305             }
306             Estimate estimate = refreshEstimateIfNeeded();
307             mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
308                     plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
309                     mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
310                     estimate.getAverageDischargeTime(),
311                     mEnhancedEstimates.getSevereWarningThreshold(),
312                     mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage(),
313                     mEnhancedEstimates.getLowWarningEnabled());
314         } else {
315             if (DEBUG) {
316                 Slog.d(TAG, "using standard");
317             }
318             mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
319                     plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
320                     mLowBatteryReminderLevels[0]);
321         }
322 
323         mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
324         if (mCurrentBatteryStateSnapshot.isHybrid()) {
325             maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
326         } else {
327             maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
328         }
329     }
330 
331     // updates the time estimate if we don't have one or battery level has changed.
332     @VisibleForTesting
refreshEstimateIfNeeded()333     Estimate refreshEstimateIfNeeded() {
334         if (mLastBatteryStateSnapshot == null
335                 || mLastBatteryStateSnapshot.getTimeRemainingMillis() == NO_ESTIMATE_AVAILABLE
336                 || mBatteryLevel != mLastBatteryStateSnapshot.getBatteryLevel()) {
337             final Estimate estimate = mEnhancedEstimates.getEstimate();
338             if (DEBUG) {
339                 Slog.d(TAG, "updated estimate: " + estimate.getEstimateMillis());
340             }
341             return estimate;
342         }
343         return new Estimate(mLastBatteryStateSnapshot.getTimeRemainingMillis(),
344                 mLastBatteryStateSnapshot.isBasedOnUsage(),
345                 mLastBatteryStateSnapshot.getAverageTimeToDischargeMillis());
346     }
347 
348     @VisibleForTesting
maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot, BatteryStateSnapshot lastSnapshot)349     void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
350             BatteryStateSnapshot lastSnapshot) {
351         // if we are now over 45% battery & 6 hours remaining so we can trigger hybrid
352         // notification again
353         if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET
354                 && currentSnapshot.getTimeRemainingMillis() > SIX_HOURS_MILLIS) {
355             mLowWarningShownThisChargeCycle = false;
356             mSevereWarningShownThisChargeCycle = false;
357             if (DEBUG) {
358                 Slog.d(TAG, "Charge cycle reset! Can show warnings again");
359             }
360         }
361 
362         final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
363                 || lastSnapshot.getPlugged();
364 
365         if (shouldShowHybridWarning(currentSnapshot)) {
366             mWarnings.showLowBatteryWarning(playSound);
367             // mark if we've already shown a warning this cycle. This will prevent the notification
368             // trigger from spamming users by only showing low/critical warnings once per cycle
369             if (currentSnapshot.getTimeRemainingMillis()
370                     <= currentSnapshot.getSevereThresholdMillis()
371                     || currentSnapshot.getBatteryLevel()
372                     <= currentSnapshot.getSevereLevelThreshold()) {
373                 mSevereWarningShownThisChargeCycle = true;
374                 mLowWarningShownThisChargeCycle = true;
375                 if (DEBUG) {
376                     Slog.d(TAG, "Severe warning marked as shown this cycle");
377                 }
378             } else {
379                 Slog.d(TAG, "Low warning marked as shown this cycle");
380                 mLowWarningShownThisChargeCycle = true;
381             }
382         } else if (shouldDismissHybridWarning(currentSnapshot)) {
383             if (DEBUG) {
384                 Slog.d(TAG, "Dismissing warning");
385             }
386             mWarnings.dismissLowBatteryWarning();
387         } else {
388             if (DEBUG) {
389                 Slog.d(TAG, "Updating warning");
390             }
391             mWarnings.updateLowBatteryWarning();
392         }
393     }
394 
395     @VisibleForTesting
shouldShowHybridWarning(BatteryStateSnapshot snapshot)396     boolean shouldShowHybridWarning(BatteryStateSnapshot snapshot) {
397         if (snapshot.getPlugged()
398                 || snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN) {
399             Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged()
400                     + " status unknown: "
401                     + (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN));
402             return false;
403         }
404 
405         // Only show the low warning if enabled once per charge cycle & no battery saver
406         final boolean canShowWarning = snapshot.isLowWarningEnabled()
407                 && !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
408                 && (snapshot.getTimeRemainingMillis() < snapshot.getLowThresholdMillis()
409                 || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());
410 
411         // Only show the severe warning once per charge cycle
412         final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
413                 && (snapshot.getTimeRemainingMillis() < snapshot.getSevereThresholdMillis()
414                 || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());
415 
416         final boolean canShow = canShowWarning || canShowSevereWarning;
417 
418         if (DEBUG) {
419             Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:"
420                     + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
421                     + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
422                     + "\n" + snapshot.toString());
423         }
424         return canShow;
425     }
426 
427     @VisibleForTesting
shouldDismissHybridWarning(BatteryStateSnapshot snapshot)428     boolean shouldDismissHybridWarning(BatteryStateSnapshot snapshot) {
429         return snapshot.getPlugged()
430                 || snapshot.getTimeRemainingMillis() > snapshot.getLowThresholdMillis();
431     }
432 
maybeShowBatteryWarning( BatteryStateSnapshot currentSnapshot, BatteryStateSnapshot lastSnapshot)433     protected void maybeShowBatteryWarning(
434             BatteryStateSnapshot currentSnapshot,
435             BatteryStateSnapshot lastSnapshot) {
436         final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
437                 || lastSnapshot.getPlugged();
438 
439         if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {
440             mWarnings.showLowBatteryWarning(playSound);
441         } else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {
442             mWarnings.dismissLowBatteryWarning();
443         } else {
444             mWarnings.updateLowBatteryWarning();
445         }
446     }
447 
448     @VisibleForTesting
shouldShowLowBatteryWarning( BatteryStateSnapshot currentSnapshot, BatteryStateSnapshot lastSnapshot)449     boolean shouldShowLowBatteryWarning(
450             BatteryStateSnapshot currentSnapshot,
451             BatteryStateSnapshot lastSnapshot) {
452         return !currentSnapshot.getPlugged()
453                 && !currentSnapshot.isPowerSaver()
454                 && (((currentSnapshot.getBucket() < lastSnapshot.getBucket()
455                         || lastSnapshot.getPlugged())
456                 && currentSnapshot.getBucket() < 0))
457                 && currentSnapshot.getBatteryStatus() != BatteryManager.BATTERY_STATUS_UNKNOWN;
458     }
459 
460     @VisibleForTesting
shouldDismissLowBatteryWarning( BatteryStateSnapshot currentSnapshot, BatteryStateSnapshot lastSnapshot)461     boolean shouldDismissLowBatteryWarning(
462             BatteryStateSnapshot currentSnapshot,
463             BatteryStateSnapshot lastSnapshot) {
464         return currentSnapshot.isPowerSaver()
465                 || currentSnapshot.getPlugged()
466                 || (currentSnapshot.getBucket() > lastSnapshot.getBucket()
467                         && currentSnapshot.getBucket() > 0);
468     }
469 
initThermalEventListeners()470     private void initThermalEventListeners() {
471         doSkinThermalEventListenerRegistration();
472         doUsbThermalEventListenerRegistration();
473     }
474 
475     @VisibleForTesting
doSkinThermalEventListenerRegistration()476     synchronized void doSkinThermalEventListenerRegistration() {
477         final boolean oldEnableSkinTemperatureWarning = mEnableSkinTemperatureWarning;
478         boolean ret = false;
479 
480         mEnableSkinTemperatureWarning = Settings.Global.getInt(mContext.getContentResolver(),
481             Settings.Global.SHOW_TEMPERATURE_WARNING,
482             mContext.getResources().getInteger(R.integer.config_showTemperatureWarning)) != 0;
483 
484         if (mEnableSkinTemperatureWarning != oldEnableSkinTemperatureWarning) {
485             try {
486                 if (mSkinThermalEventListener == null) {
487                     mSkinThermalEventListener = new SkinThermalEventListener();
488                 }
489                 if (mThermalService == null) {
490                     mThermalService = IThermalService.Stub.asInterface(
491                         ServiceManager.getService(Context.THERMAL_SERVICE));
492                 }
493                 if (mEnableSkinTemperatureWarning) {
494                     ret = mThermalService.registerThermalEventListenerWithType(
495                             mSkinThermalEventListener, Temperature.TYPE_SKIN);
496                 } else {
497                     ret = mThermalService.unregisterThermalEventListener(mSkinThermalEventListener);
498                 }
499             } catch (RemoteException e) {
500                 Slog.e(TAG, "Exception while (un)registering skin thermal event listener.", e);
501             }
502 
503             if (!ret) {
504                 mEnableSkinTemperatureWarning = !mEnableSkinTemperatureWarning;
505                 Slog.e(TAG, "Failed to register or unregister skin thermal event listener.");
506             }
507         }
508     }
509 
510     @VisibleForTesting
doUsbThermalEventListenerRegistration()511     synchronized void doUsbThermalEventListenerRegistration() {
512         final boolean oldEnableUsbTemperatureAlarm = mEnableUsbTemperatureAlarm;
513         boolean ret = false;
514 
515         mEnableUsbTemperatureAlarm = Settings.Global.getInt(mContext.getContentResolver(),
516             Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
517             mContext.getResources().getInteger(R.integer.config_showUsbPortAlarm)) != 0;
518 
519         if (mEnableUsbTemperatureAlarm != oldEnableUsbTemperatureAlarm) {
520             try {
521                 if (mUsbThermalEventListener == null) {
522                     mUsbThermalEventListener = new UsbThermalEventListener();
523                 }
524                 if (mThermalService == null) {
525                     mThermalService = IThermalService.Stub.asInterface(
526                         ServiceManager.getService(Context.THERMAL_SERVICE));
527                 }
528                 if (mEnableUsbTemperatureAlarm) {
529                     ret = mThermalService.registerThermalEventListenerWithType(
530                             mUsbThermalEventListener, Temperature.TYPE_USB_PORT);
531                 } else {
532                     ret = mThermalService.unregisterThermalEventListener(mUsbThermalEventListener);
533                 }
534             } catch (RemoteException e) {
535                 Slog.e(TAG, "Exception while (un)registering usb thermal event listener.", e);
536             }
537 
538             if (!ret) {
539                 mEnableUsbTemperatureAlarm = !mEnableUsbTemperatureAlarm;
540                 Slog.e(TAG, "Failed to register or unregister usb thermal event listener.");
541             }
542         }
543     }
544 
showThermalShutdownDialog()545     private void showThermalShutdownDialog() {
546         if (mPowerManager.getLastShutdownReason()
547                 == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
548             mWarnings.showThermalShutdownWarning();
549         }
550     }
551 
dump(FileDescriptor fd, PrintWriter pw, String[] args)552     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
553         pw.print("mLowBatteryAlertCloseLevel=");
554         pw.println(mLowBatteryAlertCloseLevel);
555         pw.print("mLowBatteryReminderLevels=");
556         pw.println(Arrays.toString(mLowBatteryReminderLevels));
557         pw.print("mBatteryLevel=");
558         pw.println(Integer.toString(mBatteryLevel));
559         pw.print("mBatteryStatus=");
560         pw.println(Integer.toString(mBatteryStatus));
561         pw.print("mPlugType=");
562         pw.println(Integer.toString(mPlugType));
563         pw.print("mInvalidCharger=");
564         pw.println(Integer.toString(mInvalidCharger));
565         pw.print("mScreenOffTime=");
566         pw.print(mScreenOffTime);
567         if (mScreenOffTime >= 0) {
568             pw.print(" (");
569             pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
570             pw.print(" ago)");
571         }
572         pw.println();
573         pw.print("soundTimeout=");
574         pw.println(Settings.Global.getInt(mContext.getContentResolver(),
575                 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
576         pw.print("bucket: ");
577         pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
578         pw.print("mEnableSkinTemperatureWarning=");
579         pw.println(mEnableSkinTemperatureWarning);
580         pw.print("mEnableUsbTemperatureAlarm=");
581         pw.println(mEnableUsbTemperatureAlarm);
582         mWarnings.dump(pw);
583     }
584 
585     /**
586      * The interface to allow PowerUI to communicate with whatever implementation of WarningsUI
587      * is being used by the system.
588      */
589     public interface WarningsUI {
590 
591         /**
592          * Updates battery and screen info for determining whether to trigger battery warnings or
593          * not.
594          * @param batteryLevel The current battery level
595          * @param bucket The current battery bucket
596          * @param screenOffTime How long the screen has been off in millis
597          */
update(int batteryLevel, int bucket, long screenOffTime)598         void update(int batteryLevel, int bucket, long screenOffTime);
599 
dismissLowBatteryWarning()600         void dismissLowBatteryWarning();
601 
showLowBatteryWarning(boolean playSound)602         void showLowBatteryWarning(boolean playSound);
603 
dismissInvalidChargerWarning()604         void dismissInvalidChargerWarning();
605 
showInvalidChargerWarning()606         void showInvalidChargerWarning();
607 
updateLowBatteryWarning()608         void updateLowBatteryWarning();
609 
isInvalidChargerWarningShowing()610         boolean isInvalidChargerWarningShowing();
611 
dismissHighTemperatureWarning()612         void dismissHighTemperatureWarning();
613 
showHighTemperatureWarning()614         void showHighTemperatureWarning();
615 
616         /**
617          * Display USB port overheat alarm
618          */
showUsbHighTemperatureAlarm()619         void showUsbHighTemperatureAlarm();
620 
showThermalShutdownWarning()621         void showThermalShutdownWarning();
622 
dump(PrintWriter pw)623         void dump(PrintWriter pw);
624 
userSwitched()625         void userSwitched();
626 
627         /**
628          * Updates the snapshot of battery state used for evaluating battery warnings
629          * @param snapshot object containing relevant values for making battery warning decisions.
630          */
updateSnapshot(BatteryStateSnapshot snapshot)631         void updateSnapshot(BatteryStateSnapshot snapshot);
632     }
633 
634     // Skin thermal event received from thermal service manager subsystem
635     @VisibleForTesting
636     final class SkinThermalEventListener extends IThermalEventListener.Stub {
notifyThrottling(Temperature temp)637         @Override public void notifyThrottling(Temperature temp) {
638             int status = temp.getStatus();
639 
640             if (status >= Temperature.THROTTLING_EMERGENCY) {
641                 StatusBar statusBar = getComponent(StatusBar.class);
642                 if (statusBar != null && !statusBar.isDeviceInVrMode()) {
643                     mWarnings.showHighTemperatureWarning();
644                     Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
645                             + ", current skin status = " + status
646                             + ", temperature = " + temp.getValue());
647                 }
648             } else {
649                 mWarnings.dismissHighTemperatureWarning();
650             }
651         }
652     }
653 
654     // Usb thermal event received from thermal service manager subsystem
655     @VisibleForTesting
656     final class UsbThermalEventListener extends IThermalEventListener.Stub {
notifyThrottling(Temperature temp)657         @Override public void notifyThrottling(Temperature temp) {
658             int status = temp.getStatus();
659 
660             if (status >= Temperature.THROTTLING_EMERGENCY) {
661                 mWarnings.showUsbHighTemperatureAlarm();
662                 Slog.d(TAG, "UsbThermalEventListener: notifyThrottling was called "
663                         + ", current usb port status = " + status
664                         + ", temperature = " + temp.getValue());
665             }
666         }
667     }
668 }
669