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