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