1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.power; 16 17 import static android.provider.Settings.Global.SHOW_TEMPERATURE_WARNING; 18 import static android.provider.Settings.Global.SHOW_USB_TEMPERATURE_ALARM; 19 20 import static com.google.common.truth.Truth.assertThat; 21 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.Matchers.eq; 24 import static org.mockito.Mockito.anyObject; 25 import static org.mockito.Mockito.never; 26 import static org.mockito.Mockito.times; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 30 import android.content.BroadcastReceiver; 31 import android.content.IntentFilter; 32 import android.os.BatteryManager; 33 import android.os.Handler; 34 import android.os.IThermalEventListener; 35 import android.os.IThermalService; 36 import android.os.PowerManager; 37 import android.os.Temperature; 38 import android.provider.Settings; 39 import android.test.suitebuilder.annotation.SmallTest; 40 import android.testing.AndroidTestingRunner; 41 import android.testing.TestableLooper; 42 import android.testing.TestableLooper.RunWithLooper; 43 import android.testing.TestableResources; 44 45 import com.android.settingslib.fuelgauge.Estimate; 46 import com.android.systemui.R; 47 import com.android.systemui.SysuiTestCase; 48 import com.android.systemui.broadcast.BroadcastDispatcher; 49 import com.android.systemui.keyguard.WakefulnessLifecycle; 50 import com.android.systemui.power.PowerUI.WarningsUI; 51 import com.android.systemui.settings.UserTracker; 52 import com.android.systemui.statusbar.CommandQueue; 53 import com.android.systemui.statusbar.phone.CentralSurfaces; 54 55 import org.junit.Before; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 import org.mockito.Mock; 59 import org.mockito.MockitoAnnotations; 60 61 import java.time.Duration; 62 import java.util.Optional; 63 import java.util.concurrent.TimeUnit; 64 65 import dagger.Lazy; 66 67 @RunWith(AndroidTestingRunner.class) 68 @RunWithLooper 69 @SmallTest 70 public class PowerUITest extends SysuiTestCase { 71 72 private static final boolean UNPLUGGED = false; 73 private static final boolean POWER_SAVER_OFF = false; 74 private static final int ABOVE_WARNING_BUCKET = 1; 75 private static final long ONE_HOUR_MILLIS = Duration.ofHours(1).toMillis(); 76 public static final int BELOW_WARNING_BUCKET = -1; 77 public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2); 78 public static final long BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(30); 79 public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4); 80 private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis(); 81 private static final int OLD_BATTERY_LEVEL_NINE = 9; 82 private static final int OLD_BATTERY_LEVEL_10 = 10; 83 private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15); 84 public static final int BATTERY_LEVEL_10 = 10; 85 @Mock private WarningsUI mMockWarnings; 86 private PowerUI mPowerUI; 87 @Mock private EnhancedEstimates mEnhancedEstimates; 88 @Mock private PowerManager mPowerManager; 89 @Mock private UserTracker mUserTracker; 90 @Mock private WakefulnessLifecycle mWakefulnessLifecycle; 91 @Mock private IThermalService mThermalServiceMock; 92 private IThermalEventListener mUsbThermalEventListener; 93 private IThermalEventListener mSkinThermalEventListener; 94 @Mock private BroadcastDispatcher mBroadcastDispatcher; 95 @Mock private CommandQueue mCommandQueue; 96 @Mock private Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy; 97 @Mock private CentralSurfaces mCentralSurfaces; 98 99 @Before setup()100 public void setup() { 101 MockitoAnnotations.initMocks(this); 102 103 when(mCentralSurfacesOptionalLazy.get()).thenReturn(Optional.of(mCentralSurfaces)); 104 105 createPowerUi(); 106 mSkinThermalEventListener = mPowerUI.new SkinThermalEventListener(); 107 mUsbThermalEventListener = mPowerUI.new UsbThermalEventListener(); 108 } 109 110 @Test testReceiverIsRegisteredToDispatcherOnStart()111 public void testReceiverIsRegisteredToDispatcherOnStart() { 112 mPowerUI.start(); 113 verify(mBroadcastDispatcher).registerReceiverWithHandler( 114 any(BroadcastReceiver.class), 115 any(IntentFilter.class), 116 any(Handler.class)); //PowerUI does not call with User 117 } 118 119 @Test testSkinWarning_throttlingCritical()120 public void testSkinWarning_throttlingCritical() throws Exception { 121 mPowerUI.start(); 122 123 final Temperature temp = getCriticalStatusTemp(Temperature.TYPE_SKIN, "skin1"); 124 mSkinThermalEventListener.notifyThrottling(temp); 125 126 // dismiss skin high temperature warning when throttling status is critical 127 TestableLooper.get(this).processAllMessages(); 128 verify(mMockWarnings, never()).showHighTemperatureWarning(); 129 verify(mMockWarnings, times(1)).dismissHighTemperatureWarning(); 130 } 131 132 @Test testSkinWarning_throttlingEmergency()133 public void testSkinWarning_throttlingEmergency() throws Exception { 134 mPowerUI.start(); 135 136 final Temperature temp = getEmergencyStatusTemp(Temperature.TYPE_SKIN, "skin2"); 137 mSkinThermalEventListener.notifyThrottling(temp); 138 139 // show skin high temperature warning when throttling status is emergency 140 TestableLooper.get(this).processAllMessages(); 141 verify(mMockWarnings, times(1)).showHighTemperatureWarning(); 142 verify(mMockWarnings, never()).dismissHighTemperatureWarning(); 143 } 144 145 @Test testUsbAlarm_throttlingCritical()146 public void testUsbAlarm_throttlingCritical() throws Exception { 147 mPowerUI.start(); 148 149 final Temperature temp = getCriticalStatusTemp(Temperature.TYPE_USB_PORT, "usb1"); 150 mUsbThermalEventListener.notifyThrottling(temp); 151 152 // not show usb high temperature alarm when throttling status is critical 153 TestableLooper.get(this).processAllMessages(); 154 verify(mMockWarnings, never()).showUsbHighTemperatureAlarm(); 155 } 156 157 @Test testUsbAlarm_throttlingEmergency()158 public void testUsbAlarm_throttlingEmergency() throws Exception { 159 mPowerUI.start(); 160 161 final Temperature temp = getEmergencyStatusTemp(Temperature.TYPE_USB_PORT, "usb2"); 162 mUsbThermalEventListener.notifyThrottling(temp); 163 164 // show usb high temperature alarm when throttling status is emergency 165 TestableLooper.get(this).processAllMessages(); 166 verify(mMockWarnings, times(1)).showUsbHighTemperatureAlarm(); 167 } 168 169 @Test testSettingOverrideConfig_enableSkinTemperatureWarning()170 public void testSettingOverrideConfig_enableSkinTemperatureWarning() throws Exception { 171 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); 172 TestableResources resources = mContext.getOrCreateTestableResources(); 173 resources.addOverride(R.integer.config_showTemperatureWarning, 0); 174 175 mPowerUI.start(); 176 177 TestableLooper.get(this).processAllMessages(); 178 verify(mThermalServiceMock, times(1)) 179 .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); 180 } 181 182 @Test testSettingOverrideConfig_enableUsbTemperatureAlarm()183 public void testSettingOverrideConfig_enableUsbTemperatureAlarm() throws Exception { 184 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); 185 TestableResources resources = mContext.getOrCreateTestableResources(); 186 resources.addOverride(R.integer.config_showUsbPortAlarm, 0); 187 188 mPowerUI.start(); 189 190 TestableLooper.get(this).processAllMessages(); 191 verify(mThermalServiceMock, times(1)) 192 .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); 193 } 194 195 @Test testSettingOverrideConfig_disableSkinTemperatureWarning()196 public void testSettingOverrideConfig_disableSkinTemperatureWarning() throws Exception { 197 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 0); 198 TestableResources resources = mContext.getOrCreateTestableResources(); 199 resources.addOverride(R.integer.config_showTemperatureWarning, 1); 200 201 mPowerUI.start(); 202 203 TestableLooper.get(this).processAllMessages(); 204 verify(mThermalServiceMock, times(0)) 205 .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); 206 } 207 208 @Test testSettingOverrideConfig_disableUsbTemperatureAlarm()209 public void testSettingOverrideConfig_disableUsbTemperatureAlarm() throws Exception { 210 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 0); 211 TestableResources resources = mContext.getOrCreateTestableResources(); 212 resources.addOverride(R.integer.config_showUsbPortAlarm, 1); 213 214 mPowerUI.start(); 215 216 TestableLooper.get(this).processAllMessages(); 217 verify(mThermalServiceMock, times(0)) 218 .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); 219 } 220 221 @Test testThermalEventListenerRegistration_success_skinType()222 public void testThermalEventListenerRegistration_success_skinType() throws Exception { 223 // Settings SHOW_TEMPERATURE_WARNING is set to 1 224 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); 225 226 // success registering skin thermal event listener 227 when(mThermalServiceMock.registerThermalEventListenerWithType( 228 anyObject(), eq(Temperature.TYPE_SKIN))).thenReturn(true); 229 230 mPowerUI.doSkinThermalEventListenerRegistration(); 231 232 // verify registering skin thermal event listener, return true (success) 233 TestableLooper.get(this).processAllMessages(); 234 verify(mThermalServiceMock, times(1)) 235 .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); 236 237 // Settings SHOW_TEMPERATURE_WARNING is set to 0 238 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 0); 239 240 mPowerUI.doSkinThermalEventListenerRegistration(); 241 242 // verify unregistering skin thermal event listener 243 TestableLooper.get(this).processAllMessages(); 244 verify(mThermalServiceMock, times(1)).unregisterThermalEventListener(anyObject()); 245 } 246 247 @Test testThermalEventListenerRegistration_fail_skinType()248 public void testThermalEventListenerRegistration_fail_skinType() throws Exception { 249 // Settings SHOW_TEMPERATURE_WARNING is set to 1 250 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); 251 252 // fail registering skin thermal event listener 253 when(mThermalServiceMock.registerThermalEventListenerWithType( 254 anyObject(), eq(Temperature.TYPE_SKIN))).thenReturn(false); 255 256 mPowerUI.doSkinThermalEventListenerRegistration(); 257 258 // verify registering skin thermal event listener, return false (fail) 259 TestableLooper.get(this).processAllMessages(); 260 verify(mThermalServiceMock, times(1)) 261 .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); 262 263 // Settings SHOW_TEMPERATURE_WARNING is set to 0 264 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 0); 265 266 mPowerUI.doSkinThermalEventListenerRegistration(); 267 268 // verify that cannot unregister listener (current state is unregistered) 269 TestableLooper.get(this).processAllMessages(); 270 verify(mThermalServiceMock, times(0)).unregisterThermalEventListener(anyObject()); 271 272 // Settings SHOW_TEMPERATURE_WARNING is set to 1 273 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); 274 275 mPowerUI.doSkinThermalEventListenerRegistration(); 276 277 // verify that can register listener (current state is unregistered) 278 TestableLooper.get(this).processAllMessages(); 279 verify(mThermalServiceMock, times(2)) 280 .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); 281 } 282 283 @Test testThermalEventListenerRegistration_success_usbType()284 public void testThermalEventListenerRegistration_success_usbType() throws Exception { 285 // Settings SHOW_USB_TEMPERATURE_ALARM is set to 1 286 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); 287 288 // success registering usb thermal event listener 289 when(mThermalServiceMock.registerThermalEventListenerWithType( 290 anyObject(), eq(Temperature.TYPE_USB_PORT))).thenReturn(true); 291 292 mPowerUI.doUsbThermalEventListenerRegistration(); 293 294 // verify registering usb thermal event listener, return true (success) 295 TestableLooper.get(this).processAllMessages(); 296 verify(mThermalServiceMock, times(1)) 297 .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); 298 299 // Settings SHOW_USB_TEMPERATURE_ALARM is set to 0 300 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 0); 301 302 // verify unregistering usb thermal event listener 303 mPowerUI.doUsbThermalEventListenerRegistration(); 304 TestableLooper.get(this).processAllMessages(); 305 verify(mThermalServiceMock, times(1)).unregisterThermalEventListener(anyObject()); 306 } 307 308 @Test testThermalEventListenerRegistration_fail_usbType()309 public void testThermalEventListenerRegistration_fail_usbType() throws Exception { 310 // Settings SHOW_USB_TEMPERATURE_ALARM is set to 1 311 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); 312 313 // fail registering usb thermal event listener 314 when(mThermalServiceMock.registerThermalEventListenerWithType( 315 anyObject(), eq(Temperature.TYPE_USB_PORT))).thenReturn(false); 316 317 mPowerUI.doUsbThermalEventListenerRegistration(); 318 319 // verify registering usb thermal event listener, return false (fail) 320 TestableLooper.get(this).processAllMessages(); 321 verify(mThermalServiceMock, times(1)) 322 .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); 323 324 // Settings SHOW_USB_TEMPERATURE_ALARM is set to 0 325 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 0); 326 327 mPowerUI.doUsbThermalEventListenerRegistration(); 328 329 // verify that cannot unregister listener (current state is unregistered) 330 TestableLooper.get(this).processAllMessages(); 331 verify(mThermalServiceMock, times(0)).unregisterThermalEventListener(anyObject()); 332 333 // Settings SHOW_USB_TEMPERATURE_ALARM is set to 1 334 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); 335 336 mPowerUI.doUsbThermalEventListenerRegistration(); 337 338 // verify that can register listener (current state is unregistered) 339 TestableLooper.get(this).processAllMessages(); 340 verify(mThermalServiceMock, times(2)).registerThermalEventListenerWithType( 341 anyObject(), eq(Temperature.TYPE_USB_PORT)); 342 } 343 344 @Test testMaybeShowHybridWarning()345 public void testMaybeShowHybridWarning() { 346 mPowerUI.start(); 347 348 // verify low warning shown this cycle noticed 349 BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); 350 BatteryStateSnapshot lastState = state.get(); 351 state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); 352 state.mBatteryLevel = 15; 353 354 mPowerUI.maybeShowHybridWarning(state.get(), lastState); 355 356 assertThat(mPowerUI.mLowWarningShownThisChargeCycle).isTrue(); 357 assertThat(mPowerUI.mSevereWarningShownThisChargeCycle).isFalse(); 358 359 // verify severe warning noticed this cycle 360 lastState = state.get(); 361 state.mBatteryLevel = 1; 362 state.mTimeRemainingMillis = Duration.ofMinutes(10).toMillis(); 363 364 mPowerUI.maybeShowHybridWarning(state.get(), lastState); 365 366 assertThat(mPowerUI.mLowWarningShownThisChargeCycle).isTrue(); 367 assertThat(mPowerUI.mSevereWarningShownThisChargeCycle).isTrue(); 368 369 // verify getting past threshold resets values 370 lastState = state.get(); 371 state.mBatteryLevel = 100; 372 state.mTimeRemainingMillis = Duration.ofDays(1).toMillis(); 373 374 mPowerUI.maybeShowHybridWarning(state.get(), lastState); 375 376 assertThat(mPowerUI.mLowWarningShownThisChargeCycle).isFalse(); 377 assertThat(mPowerUI.mSevereWarningShownThisChargeCycle).isFalse(); 378 } 379 380 @Test testShouldShowHybridWarning_lowLevelWarning()381 public void testShouldShowHybridWarning_lowLevelWarning() { 382 mPowerUI.start(); 383 mPowerUI.mLowWarningShownThisChargeCycle = false; 384 mPowerUI.mSevereWarningShownThisChargeCycle = false; 385 BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); 386 387 // readiness check to make sure we can show for a valid config 388 state.mBatteryLevel = 10; 389 state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); 390 boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 391 assertThat(shouldShow).isTrue(); 392 393 // Shouldn't show if plugged in 394 state.mPlugged = true; 395 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 396 assertThat(shouldShow).isFalse(); 397 398 // Shouldn't show if battery is unknown 399 state.mPlugged = false; 400 state.mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; 401 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 402 assertThat(shouldShow).isFalse(); 403 404 state.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD; 405 // Already shown both warnings 406 mPowerUI.mLowWarningShownThisChargeCycle = true; 407 mPowerUI.mSevereWarningShownThisChargeCycle = true; 408 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 409 assertThat(shouldShow).isFalse(); 410 411 // Can show low warning 412 mPowerUI.mLowWarningShownThisChargeCycle = false; 413 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 414 assertThat(shouldShow).isTrue(); 415 416 // Can't show if above the threshold for time & battery 417 state.mTimeRemainingMillis = Duration.ofHours(1000).toMillis(); 418 state.mBatteryLevel = 100; 419 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 420 assertThat(shouldShow).isFalse(); 421 422 // Battery under low percentage threshold but not time 423 state.mBatteryLevel = 10; 424 state.mLowLevelThreshold = 50; 425 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 426 assertThat(shouldShow).isTrue(); 427 428 // Should also trigger if both level and time remaining under low threshold 429 state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); 430 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 431 assertThat(shouldShow).isTrue(); 432 433 // battery saver should block the low level warning though 434 state.mIsPowerSaver = true; 435 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 436 assertThat(shouldShow).isFalse(); 437 } 438 439 @Test testShouldShowHybridWarning_severeLevelWarning()440 public void testShouldShowHybridWarning_severeLevelWarning() { 441 mPowerUI.start(); 442 mPowerUI.mLowWarningShownThisChargeCycle = false; 443 mPowerUI.mSevereWarningShownThisChargeCycle = false; 444 BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); 445 446 // readiness check to make sure we can show for a valid config 447 state.mBatteryLevel = 1; 448 state.mTimeRemainingMillis = Duration.ofMinutes(1).toMillis(); 449 boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 450 assertThat(shouldShow).isTrue(); 451 452 // Shouldn't show if plugged in 453 state.mPlugged = true; 454 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 455 assertThat(shouldShow).isFalse(); 456 457 // Shouldn't show if battery is unknown 458 state.mPlugged = false; 459 state.mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; 460 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 461 assertThat(shouldShow).isFalse(); 462 463 state.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD; 464 // Already shown both warnings 465 mPowerUI.mLowWarningShownThisChargeCycle = true; 466 mPowerUI.mSevereWarningShownThisChargeCycle = true; 467 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 468 assertThat(shouldShow).isFalse(); 469 470 // Can show severe warning 471 mPowerUI.mSevereWarningShownThisChargeCycle = false; 472 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 473 assertThat(shouldShow).isTrue(); 474 475 // Can't show if above the threshold for time & battery 476 state.mTimeRemainingMillis = Duration.ofHours(1000).toMillis(); 477 state.mBatteryLevel = 100; 478 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 479 assertThat(shouldShow).isFalse(); 480 481 // Battery under low percentage threshold but not time 482 state.mBatteryLevel = 1; 483 state.mSevereLevelThreshold = 5; 484 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 485 assertThat(shouldShow).isTrue(); 486 487 // Should also trigger if both level and time remaining under low threshold 488 state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); 489 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 490 assertThat(shouldShow).isTrue(); 491 492 // battery saver should not block the severe level warning though 493 state.mIsPowerSaver = true; 494 shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); 495 assertThat(shouldShow).isTrue(); 496 } 497 498 @Test testShouldDismissHybridWarning()499 public void testShouldDismissHybridWarning() { 500 mPowerUI.start(); 501 BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); 502 503 // We should dismiss if the device is plugged in 504 state.mPlugged = true; 505 state.mBatteryLevel = 19; 506 state.mLowLevelThreshold = 20; 507 boolean shouldDismiss = mPowerUI.shouldDismissHybridWarning(state.get()); 508 assertThat(shouldDismiss).isTrue(); 509 510 // If not plugged in and below the threshold we should not dismiss 511 state.mPlugged = false; 512 shouldDismiss = mPowerUI.shouldDismissHybridWarning(state.get()); 513 assertThat(shouldDismiss).isFalse(); 514 515 // If we go over the low warning threshold we should dismiss 516 state.mBatteryLevel = 21; 517 shouldDismiss = mPowerUI.shouldDismissHybridWarning(state.get()); 518 assertThat(shouldDismiss).isTrue(); 519 } 520 521 @Test testRefreshEstimateIfNeeded_onlyQueriesEstimateOnBatteryLevelChangeOrNull()522 public void testRefreshEstimateIfNeeded_onlyQueriesEstimateOnBatteryLevelChangeOrNull() { 523 mPowerUI.start(); 524 Estimate estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true, 0); 525 when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true); 526 when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS); 527 when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS); 528 when(mEnhancedEstimates.getEstimate()).thenReturn(estimate); 529 mPowerUI.mBatteryLevel = 10; 530 531 // we expect that the first time it will query since there is no last battery snapshot. 532 // However an invalid estimate (-1) is returned. 533 Estimate refreshedEstimate = mPowerUI.refreshEstimateIfNeeded(); 534 assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_HYBRID_THRESHOLD); 535 BatteryStateSnapshot snapshot = new BatteryStateSnapshot( 536 BATTERY_LEVEL_10, false, false, 0, BatteryManager.BATTERY_HEALTH_GOOD, 537 0, 0, -1, 0, 0, 0, false, true); 538 mPowerUI.mLastBatteryStateSnapshot = snapshot; 539 540 // query again since the estimate was -1 541 estimate = new Estimate(BELOW_SEVERE_HYBRID_THRESHOLD, true, 0); 542 when(mEnhancedEstimates.getEstimate()).thenReturn(estimate); 543 refreshedEstimate = mPowerUI.refreshEstimateIfNeeded(); 544 assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_SEVERE_HYBRID_THRESHOLD); 545 snapshot = new BatteryStateSnapshot( 546 BATTERY_LEVEL_10, false, false, 0, BatteryManager.BATTERY_HEALTH_GOOD, 0, 547 0, BELOW_SEVERE_HYBRID_THRESHOLD, 0, 0, 0, false, true); 548 mPowerUI.mLastBatteryStateSnapshot = snapshot; 549 550 // Battery level hasn't changed, so we don't query again 551 estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true, 0); 552 when(mEnhancedEstimates.getEstimate()).thenReturn(estimate); 553 refreshedEstimate = mPowerUI.refreshEstimateIfNeeded(); 554 assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_SEVERE_HYBRID_THRESHOLD); 555 556 // Battery level changes so we update again 557 mPowerUI.mBatteryLevel = 9; 558 refreshedEstimate = mPowerUI.refreshEstimateIfNeeded(); 559 assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_HYBRID_THRESHOLD); 560 } 561 562 @Test testShouldShowStandardWarning()563 public void testShouldShowStandardWarning() { 564 mPowerUI.start(); 565 BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); 566 state.mIsHybrid = false; 567 BatteryStateSnapshot lastState = state.get(); 568 569 // readiness check to make sure we can show for a valid config 570 state.mBatteryLevel = 10; 571 state.mBucket = -1; 572 boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); 573 assertThat(shouldShow).isTrue(); 574 lastState = state.get(); 575 576 // Shouldn't show if plugged in 577 state.mPlugged = true; 578 shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); 579 assertThat(shouldShow).isFalse(); 580 581 state.mPlugged = false; 582 // Shouldn't show if battery saver 583 state.mIsPowerSaver = true; 584 shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); 585 assertThat(shouldShow).isFalse(); 586 587 state.mIsPowerSaver = false; 588 // Shouldn't show if battery is unknown 589 state.mPlugged = false; 590 state.mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; 591 shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); 592 assertThat(shouldShow).isFalse(); 593 594 state.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD; 595 // show if plugged -> unplugged, bucket -1 -> -1 596 state.mPlugged = true; 597 state.mBucket = -1; 598 lastState = state.get(); 599 state.mPlugged = false; 600 state.mBucket = -1; 601 shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); 602 assertThat(shouldShow).isTrue(); 603 604 // don't show if plugged -> unplugged, bucket 0 -> 0 605 state.mPlugged = true; 606 state.mBucket = 0; 607 lastState = state.get(); 608 state.mPlugged = false; 609 state.mBucket = 0; 610 shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); 611 assertThat(shouldShow).isFalse(); 612 613 // show if unplugged -> unplugged, bucket 0 -> -1 614 state.mPlugged = false; 615 state.mBucket = 0; 616 lastState = state.get(); 617 state.mPlugged = false; 618 state.mBucket = -1; 619 shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); 620 assertThat(shouldShow).isTrue(); 621 622 // don't show if unplugged -> unplugged, bucket -1 -> 1 623 state.mPlugged = false; 624 state.mBucket = -1; 625 lastState = state.get(); 626 state.mPlugged = false; 627 state.mBucket = 1; 628 shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); 629 assertThat(shouldShow).isFalse(); 630 } 631 632 @Test testShouldDismissStandardWarning()633 public void testShouldDismissStandardWarning() { 634 mPowerUI.start(); 635 BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); 636 state.mIsHybrid = false; 637 BatteryStateSnapshot lastState = state.get(); 638 639 // should dismiss if battery saver 640 state.mIsPowerSaver = true; 641 boolean shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); 642 assertThat(shouldDismiss).isTrue(); 643 644 state.mIsPowerSaver = false; 645 // should dismiss if plugged 646 state.mPlugged = true; 647 shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); 648 assertThat(shouldDismiss).isTrue(); 649 650 state.mPlugged = false; 651 // should dismiss if bucket 0 -> 1 652 state.mBucket = 0; 653 lastState = state.get(); 654 state.mBucket = 1; 655 shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); 656 assertThat(shouldDismiss).isTrue(); 657 658 // shouldn't dismiss if bucket -1 -> 0 659 state.mBucket = -1; 660 lastState = state.get(); 661 state.mBucket = 0; 662 shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); 663 assertThat(shouldDismiss).isFalse(); 664 665 // should dismiss if powersaver & bucket 0 -> 1 666 state.mIsPowerSaver = true; 667 state.mBucket = 0; 668 lastState = state.get(); 669 state.mBucket = 1; 670 shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); 671 assertThat(shouldDismiss).isTrue(); 672 } 673 getEmergencyStatusTemp(int type, String name)674 private Temperature getEmergencyStatusTemp(int type, String name) { 675 final float value = 65; 676 return new Temperature(value, type, name, Temperature.THROTTLING_EMERGENCY); 677 } 678 getCriticalStatusTemp(int type, String name)679 private Temperature getCriticalStatusTemp(int type, String name) { 680 final float value = 60; 681 return new Temperature(value, type, name, Temperature.THROTTLING_CRITICAL); 682 } 683 createPowerUi()684 private void createPowerUi() { 685 mPowerUI = new PowerUI( 686 mContext, mBroadcastDispatcher, mCommandQueue, mCentralSurfacesOptionalLazy, 687 mMockWarnings, mEnhancedEstimates, mWakefulnessLifecycle, mPowerManager, 688 mUserTracker); 689 mPowerUI.mThermalService = mThermalServiceMock; 690 } 691 692 /** 693 * A simple wrapper class that sets values by default and makes them not final to improve 694 * test clarity. 695 */ 696 private class BatteryStateSnapshotWrapper { 697 public int mBatteryLevel = 100; 698 public boolean mIsPowerSaver = false; 699 public boolean mPlugged = false; 700 public long mSevereThresholdMillis = Duration.ofHours(1).toMillis(); 701 public long mLowThresholdMillis = Duration.ofHours(3).toMillis(); 702 public int mSevereLevelThreshold = 5; 703 public int mLowLevelThreshold = 15; 704 public int mBucket = 1; 705 public int mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD; 706 public long mTimeRemainingMillis = Duration.ofHours(24).toMillis(); 707 public boolean mIsBasedOnUsage = true; 708 public boolean mIsHybrid = true; 709 public boolean mIsLowLevelWarningEnabled = true; 710 private long mAverageTimeToDischargeMillis = Duration.ofHours(24).toMillis(); 711 get()712 public BatteryStateSnapshot get() { 713 if (mIsHybrid) { 714 return new BatteryStateSnapshot(mBatteryLevel, mIsPowerSaver, mPlugged, mBucket, 715 mBatteryStatus, mSevereLevelThreshold, mLowLevelThreshold, 716 mTimeRemainingMillis, mAverageTimeToDischargeMillis, mSevereThresholdMillis, 717 mLowThresholdMillis, mIsBasedOnUsage, mIsLowLevelWarningEnabled); 718 } else { 719 return new BatteryStateSnapshot(mBatteryLevel, mIsPowerSaver, mPlugged, mBucket, 720 mBatteryStatus, mSevereLevelThreshold, mLowLevelThreshold); 721 } 722 } 723 } 724 } 725