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