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