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