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