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