| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the |
| * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the specific language governing |
| * permissions and limitations under the License. |
| */ |
| |
| package com.android.systemui.power; |
| |
| import static android.provider.Settings.Global.SHOW_TEMPERATURE_WARNING; |
| import static android.provider.Settings.Global.SHOW_USB_TEMPERATURE_ALARM; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.Matchers.eq; |
| import static org.mockito.Mockito.anyObject; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.IntentFilter; |
| import android.os.BatteryManager; |
| import android.os.Handler; |
| import android.os.IThermalEventListener; |
| import android.os.IThermalService; |
| import android.os.PowerManager; |
| import android.os.Temperature; |
| import android.provider.Settings; |
| import android.test.suitebuilder.annotation.SmallTest; |
| import android.testing.AndroidTestingRunner; |
| import android.testing.TestableLooper; |
| import android.testing.TestableLooper.RunWithLooper; |
| import android.testing.TestableResources; |
| |
| import com.android.settingslib.fuelgauge.Estimate; |
| import com.android.systemui.R; |
| import com.android.systemui.SysuiTestCase; |
| import com.android.systemui.broadcast.BroadcastDispatcher; |
| import com.android.systemui.power.PowerUI.WarningsUI; |
| import com.android.systemui.statusbar.phone.StatusBar; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.time.Duration; |
| import java.util.concurrent.TimeUnit; |
| |
| @RunWith(AndroidTestingRunner.class) |
| @RunWithLooper |
| @SmallTest |
| public class PowerUITest extends SysuiTestCase { |
| |
| private static final boolean UNPLUGGED = false; |
| private static final boolean POWER_SAVER_OFF = false; |
| private static final int ABOVE_WARNING_BUCKET = 1; |
| private static final long ONE_HOUR_MILLIS = Duration.ofHours(1).toMillis(); |
| public static final int BELOW_WARNING_BUCKET = -1; |
| public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2); |
| public static final long BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(30); |
| public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4); |
| private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis(); |
| private static final int OLD_BATTERY_LEVEL_NINE = 9; |
| private static final int OLD_BATTERY_LEVEL_10 = 10; |
| private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15); |
| public static final int BATTERY_LEVEL_10 = 10; |
| private WarningsUI mMockWarnings; |
| private PowerUI mPowerUI; |
| private EnhancedEstimates mEnhancedEstimates; |
| @Mock private PowerManager mPowerManager; |
| @Mock private IThermalService mThermalServiceMock; |
| private IThermalEventListener mUsbThermalEventListener; |
| private IThermalEventListener mSkinThermalEventListener; |
| @Mock private BroadcastDispatcher mBroadcastDispatcher; |
| |
| @Before |
| public void setup() { |
| MockitoAnnotations.initMocks(this); |
| mMockWarnings = mDependency.injectMockDependency(WarningsUI.class); |
| mEnhancedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class); |
| |
| mContext.putComponent(StatusBar.class, mock(StatusBar.class)); |
| mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager); |
| |
| createPowerUi(); |
| mSkinThermalEventListener = mPowerUI.new SkinThermalEventListener(); |
| mUsbThermalEventListener = mPowerUI.new UsbThermalEventListener(); |
| } |
| |
| @Test |
| public void testReceiverIsRegisteredToDispatcherOnStart() { |
| mPowerUI.start(); |
| verify(mBroadcastDispatcher).registerReceiver( |
| any(BroadcastReceiver.class), |
| any(IntentFilter.class), |
| any(Handler.class)); //PowerUI does not call with User |
| } |
| |
| @Test |
| public void testSkinWarning_throttlingCritical() throws Exception { |
| mPowerUI.start(); |
| |
| final Temperature temp = getCriticalStatusTemp(Temperature.TYPE_SKIN, "skin1"); |
| mSkinThermalEventListener.notifyThrottling(temp); |
| |
| // dismiss skin high temperature warning when throttling status is critical |
| TestableLooper.get(this).processAllMessages(); |
| verify(mMockWarnings, never()).showHighTemperatureWarning(); |
| verify(mMockWarnings, times(1)).dismissHighTemperatureWarning(); |
| } |
| |
| @Test |
| public void testSkinWarning_throttlingEmergency() throws Exception { |
| mPowerUI.start(); |
| |
| final Temperature temp = getEmergencyStatusTemp(Temperature.TYPE_SKIN, "skin2"); |
| mSkinThermalEventListener.notifyThrottling(temp); |
| |
| // show skin high temperature warning when throttling status is emergency |
| TestableLooper.get(this).processAllMessages(); |
| verify(mMockWarnings, times(1)).showHighTemperatureWarning(); |
| verify(mMockWarnings, never()).dismissHighTemperatureWarning(); |
| } |
| |
| @Test |
| public void testUsbAlarm_throttlingCritical() throws Exception { |
| mPowerUI.start(); |
| |
| final Temperature temp = getCriticalStatusTemp(Temperature.TYPE_USB_PORT, "usb1"); |
| mUsbThermalEventListener.notifyThrottling(temp); |
| |
| // not show usb high temperature alarm when throttling status is critical |
| TestableLooper.get(this).processAllMessages(); |
| verify(mMockWarnings, never()).showUsbHighTemperatureAlarm(); |
| } |
| |
| @Test |
| public void testUsbAlarm_throttlingEmergency() throws Exception { |
| mPowerUI.start(); |
| |
| final Temperature temp = getEmergencyStatusTemp(Temperature.TYPE_USB_PORT, "usb2"); |
| mUsbThermalEventListener.notifyThrottling(temp); |
| |
| // show usb high temperature alarm when throttling status is emergency |
| TestableLooper.get(this).processAllMessages(); |
| verify(mMockWarnings, times(1)).showUsbHighTemperatureAlarm(); |
| } |
| |
| @Test |
| public void testSettingOverrideConfig_enableSkinTemperatureWarning() throws Exception { |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); |
| TestableResources resources = mContext.getOrCreateTestableResources(); |
| resources.addOverride(R.integer.config_showTemperatureWarning, 0); |
| |
| mPowerUI.start(); |
| |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(1)) |
| .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); |
| } |
| |
| @Test |
| public void testSettingOverrideConfig_enableUsbTemperatureAlarm() throws Exception { |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); |
| TestableResources resources = mContext.getOrCreateTestableResources(); |
| resources.addOverride(R.integer.config_showUsbPortAlarm, 0); |
| |
| mPowerUI.start(); |
| |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(1)) |
| .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); |
| } |
| |
| @Test |
| public void testSettingOverrideConfig_disableSkinTemperatureWarning() throws Exception { |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 0); |
| TestableResources resources = mContext.getOrCreateTestableResources(); |
| resources.addOverride(R.integer.config_showTemperatureWarning, 1); |
| |
| mPowerUI.start(); |
| |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(0)) |
| .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); |
| } |
| |
| @Test |
| public void testSettingOverrideConfig_disableUsbTemperatureAlarm() throws Exception { |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 0); |
| TestableResources resources = mContext.getOrCreateTestableResources(); |
| resources.addOverride(R.integer.config_showUsbPortAlarm, 1); |
| |
| mPowerUI.start(); |
| |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(0)) |
| .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); |
| } |
| |
| @Test |
| public void testThermalEventListenerRegistration_success_skinType() throws Exception { |
| // Settings SHOW_TEMPERATURE_WARNING is set to 1 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); |
| |
| // success registering skin thermal event listener |
| when(mThermalServiceMock.registerThermalEventListenerWithType( |
| anyObject(), eq(Temperature.TYPE_SKIN))).thenReturn(true); |
| |
| mPowerUI.doSkinThermalEventListenerRegistration(); |
| |
| // verify registering skin thermal event listener, return true (success) |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(1)) |
| .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); |
| |
| // Settings SHOW_TEMPERATURE_WARNING is set to 0 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 0); |
| |
| mPowerUI.doSkinThermalEventListenerRegistration(); |
| |
| // verify unregistering skin thermal event listener |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(1)).unregisterThermalEventListener(anyObject()); |
| } |
| |
| @Test |
| public void testThermalEventListenerRegistration_fail_skinType() throws Exception { |
| // Settings SHOW_TEMPERATURE_WARNING is set to 1 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); |
| |
| // fail registering skin thermal event listener |
| when(mThermalServiceMock.registerThermalEventListenerWithType( |
| anyObject(), eq(Temperature.TYPE_SKIN))).thenReturn(false); |
| |
| mPowerUI.doSkinThermalEventListenerRegistration(); |
| |
| // verify registering skin thermal event listener, return false (fail) |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(1)) |
| .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); |
| |
| // Settings SHOW_TEMPERATURE_WARNING is set to 0 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 0); |
| |
| mPowerUI.doSkinThermalEventListenerRegistration(); |
| |
| // verify that cannot unregister listener (current state is unregistered) |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(0)).unregisterThermalEventListener(anyObject()); |
| |
| // Settings SHOW_TEMPERATURE_WARNING is set to 1 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); |
| |
| mPowerUI.doSkinThermalEventListenerRegistration(); |
| |
| // verify that can register listener (current state is unregistered) |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(2)) |
| .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); |
| } |
| |
| @Test |
| public void testThermalEventListenerRegistration_success_usbType() throws Exception { |
| // Settings SHOW_USB_TEMPERATURE_ALARM is set to 1 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); |
| |
| // success registering usb thermal event listener |
| when(mThermalServiceMock.registerThermalEventListenerWithType( |
| anyObject(), eq(Temperature.TYPE_USB_PORT))).thenReturn(true); |
| |
| mPowerUI.doUsbThermalEventListenerRegistration(); |
| |
| // verify registering usb thermal event listener, return true (success) |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(1)) |
| .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); |
| |
| // Settings SHOW_USB_TEMPERATURE_ALARM is set to 0 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 0); |
| |
| // verify unregistering usb thermal event listener |
| mPowerUI.doUsbThermalEventListenerRegistration(); |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(1)).unregisterThermalEventListener(anyObject()); |
| } |
| |
| @Test |
| public void testThermalEventListenerRegistration_fail_usbType() throws Exception { |
| // Settings SHOW_USB_TEMPERATURE_ALARM is set to 1 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); |
| |
| // fail registering usb thermal event listener |
| when(mThermalServiceMock.registerThermalEventListenerWithType( |
| anyObject(), eq(Temperature.TYPE_USB_PORT))).thenReturn(false); |
| |
| mPowerUI.doUsbThermalEventListenerRegistration(); |
| |
| // verify registering usb thermal event listener, return false (fail) |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(1)) |
| .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); |
| |
| // Settings SHOW_USB_TEMPERATURE_ALARM is set to 0 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 0); |
| |
| mPowerUI.doUsbThermalEventListenerRegistration(); |
| |
| // verify that cannot unregister listener (current state is unregistered) |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(0)).unregisterThermalEventListener(anyObject()); |
| |
| // Settings SHOW_USB_TEMPERATURE_ALARM is set to 1 |
| Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); |
| |
| mPowerUI.doUsbThermalEventListenerRegistration(); |
| |
| // verify that can register listener (current state is unregistered) |
| TestableLooper.get(this).processAllMessages(); |
| verify(mThermalServiceMock, times(2)).registerThermalEventListenerWithType( |
| anyObject(), eq(Temperature.TYPE_USB_PORT)); |
| } |
| |
| @Test |
| public void testMaybeShowHybridWarning() { |
| mPowerUI.start(); |
| |
| // verify low warning shown this cycle noticed |
| BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); |
| BatteryStateSnapshot lastState = state.get(); |
| state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); |
| state.mBatteryLevel = 15; |
| |
| mPowerUI.maybeShowHybridWarning(state.get(), lastState); |
| |
| assertThat(mPowerUI.mLowWarningShownThisChargeCycle).isTrue(); |
| assertThat(mPowerUI.mSevereWarningShownThisChargeCycle).isFalse(); |
| |
| // verify severe warning noticed this cycle |
| lastState = state.get(); |
| state.mBatteryLevel = 1; |
| state.mTimeRemainingMillis = Duration.ofMinutes(10).toMillis(); |
| |
| mPowerUI.maybeShowHybridWarning(state.get(), lastState); |
| |
| assertThat(mPowerUI.mLowWarningShownThisChargeCycle).isTrue(); |
| assertThat(mPowerUI.mSevereWarningShownThisChargeCycle).isTrue(); |
| |
| // verify getting past threshold resets values |
| lastState = state.get(); |
| state.mBatteryLevel = 100; |
| state.mTimeRemainingMillis = Duration.ofDays(1).toMillis(); |
| |
| mPowerUI.maybeShowHybridWarning(state.get(), lastState); |
| |
| assertThat(mPowerUI.mLowWarningShownThisChargeCycle).isFalse(); |
| assertThat(mPowerUI.mSevereWarningShownThisChargeCycle).isFalse(); |
| } |
| |
| @Test |
| public void testShouldShowHybridWarning_lowLevelWarning() { |
| mPowerUI.start(); |
| mPowerUI.mLowWarningShownThisChargeCycle = false; |
| mPowerUI.mSevereWarningShownThisChargeCycle = false; |
| BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); |
| |
| // sanity check to make sure we can show for a valid config |
| state.mBatteryLevel = 10; |
| state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); |
| boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isTrue(); |
| |
| // Shouldn't show if plugged in |
| state.mPlugged = true; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| |
| // Shouldn't show if battery is unknown |
| state.mPlugged = false; |
| state.mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| |
| state.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD; |
| // Already shown both warnings |
| mPowerUI.mLowWarningShownThisChargeCycle = true; |
| mPowerUI.mSevereWarningShownThisChargeCycle = true; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| |
| // Can show low warning |
| mPowerUI.mLowWarningShownThisChargeCycle = false; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isTrue(); |
| |
| // Can't show if above the threshold for time & battery |
| state.mTimeRemainingMillis = Duration.ofHours(1000).toMillis(); |
| state.mBatteryLevel = 100; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| |
| // Battery under low percentage threshold but not time |
| state.mBatteryLevel = 10; |
| state.mLowLevelThreshold = 50; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isTrue(); |
| |
| // Should also trigger if both level and time remaining under low threshold |
| state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isTrue(); |
| |
| // battery saver should block the low level warning though |
| state.mIsPowerSaver = true; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| |
| state.mIsPowerSaver = false; |
| // if disabled we should not show the low warning. |
| state.mIsLowLevelWarningEnabled = false; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| } |
| |
| @Test |
| public void testShouldShowHybridWarning_severeLevelWarning() { |
| mPowerUI.start(); |
| mPowerUI.mLowWarningShownThisChargeCycle = false; |
| mPowerUI.mSevereWarningShownThisChargeCycle = false; |
| BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); |
| |
| // sanity check to make sure we can show for a valid config |
| state.mBatteryLevel = 1; |
| state.mTimeRemainingMillis = Duration.ofMinutes(1).toMillis(); |
| boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isTrue(); |
| |
| // Shouldn't show if plugged in |
| state.mPlugged = true; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| |
| // Shouldn't show if battery is unknown |
| state.mPlugged = false; |
| state.mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| |
| state.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD; |
| // Already shown both warnings |
| mPowerUI.mLowWarningShownThisChargeCycle = true; |
| mPowerUI.mSevereWarningShownThisChargeCycle = true; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| |
| // Can show severe warning |
| mPowerUI.mSevereWarningShownThisChargeCycle = false; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isTrue(); |
| |
| // Can't show if above the threshold for time & battery |
| state.mTimeRemainingMillis = Duration.ofHours(1000).toMillis(); |
| state.mBatteryLevel = 100; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isFalse(); |
| |
| // Battery under low percentage threshold but not time |
| state.mBatteryLevel = 1; |
| state.mSevereLevelThreshold = 5; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isTrue(); |
| |
| // Should also trigger if both level and time remaining under low threshold |
| state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isTrue(); |
| |
| // battery saver should not block the severe level warning though |
| state.mIsPowerSaver = true; |
| shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); |
| assertThat(shouldShow).isTrue(); |
| } |
| |
| @Test |
| public void testShouldDismissHybridWarning() { |
| mPowerUI.start(); |
| BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); |
| |
| // We should dismiss if the device is plugged in |
| state.mPlugged = true; |
| state.mTimeRemainingMillis = Duration.ofHours(1).toMillis(); |
| state.mLowThresholdMillis = Duration.ofHours(2).toMillis(); |
| boolean shouldDismiss = mPowerUI.shouldDismissHybridWarning(state.get()); |
| assertThat(shouldDismiss).isTrue(); |
| |
| // If not plugged in and below the threshold we should not dismiss |
| state.mPlugged = false; |
| shouldDismiss = mPowerUI.shouldDismissHybridWarning(state.get()); |
| assertThat(shouldDismiss).isFalse(); |
| |
| // If we go over the low warning threshold we should dismiss |
| state.mTimeRemainingMillis = Duration.ofHours(3).toMillis(); |
| shouldDismiss = mPowerUI.shouldDismissHybridWarning(state.get()); |
| assertThat(shouldDismiss).isTrue(); |
| } |
| |
| @Test |
| public void testRefreshEstimateIfNeeded_onlyQueriesEstimateOnBatteryLevelChangeOrNull() { |
| mPowerUI.start(); |
| Estimate estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true, 0); |
| when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true); |
| when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS); |
| when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS); |
| when(mEnhancedEstimates.getEstimate()).thenReturn(estimate); |
| mPowerUI.mBatteryLevel = 10; |
| |
| // we expect that the first time it will query since there is no last battery snapshot. |
| // However an invalid estimate (-1) is returned. |
| Estimate refreshedEstimate = mPowerUI.refreshEstimateIfNeeded(); |
| assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_HYBRID_THRESHOLD); |
| BatteryStateSnapshot snapshot = new BatteryStateSnapshot( |
| BATTERY_LEVEL_10, false, false, 0, BatteryManager.BATTERY_HEALTH_GOOD, |
| 0, 0, -1, 0, 0, 0, false, true); |
| mPowerUI.mLastBatteryStateSnapshot = snapshot; |
| |
| // query again since the estimate was -1 |
| estimate = new Estimate(BELOW_SEVERE_HYBRID_THRESHOLD, true, 0); |
| when(mEnhancedEstimates.getEstimate()).thenReturn(estimate); |
| refreshedEstimate = mPowerUI.refreshEstimateIfNeeded(); |
| assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_SEVERE_HYBRID_THRESHOLD); |
| snapshot = new BatteryStateSnapshot( |
| BATTERY_LEVEL_10, false, false, 0, BatteryManager.BATTERY_HEALTH_GOOD, 0, |
| 0, BELOW_SEVERE_HYBRID_THRESHOLD, 0, 0, 0, false, true); |
| mPowerUI.mLastBatteryStateSnapshot = snapshot; |
| |
| // Battery level hasn't changed, so we don't query again |
| estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true, 0); |
| when(mEnhancedEstimates.getEstimate()).thenReturn(estimate); |
| refreshedEstimate = mPowerUI.refreshEstimateIfNeeded(); |
| assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_SEVERE_HYBRID_THRESHOLD); |
| |
| // Battery level changes so we update again |
| mPowerUI.mBatteryLevel = 9; |
| refreshedEstimate = mPowerUI.refreshEstimateIfNeeded(); |
| assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_HYBRID_THRESHOLD); |
| } |
| |
| @Test |
| public void testShouldShowStandardWarning() { |
| mPowerUI.start(); |
| BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); |
| state.mIsHybrid = false; |
| BatteryStateSnapshot lastState = state.get(); |
| |
| // sanity check to make sure we can show for a valid config |
| state.mBatteryLevel = 10; |
| state.mBucket = -1; |
| boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldShow).isTrue(); |
| lastState = state.get(); |
| |
| // Shouldn't show if plugged in |
| state.mPlugged = true; |
| shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldShow).isFalse(); |
| |
| state.mPlugged = false; |
| // Shouldn't show if battery saver |
| state.mIsPowerSaver = true; |
| shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldShow).isFalse(); |
| |
| state.mIsPowerSaver = false; |
| // Shouldn't show if battery is unknown |
| state.mPlugged = false; |
| state.mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; |
| shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldShow).isFalse(); |
| |
| state.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD; |
| // show if plugged -> unplugged, bucket -1 -> -1 |
| state.mPlugged = true; |
| state.mBucket = -1; |
| lastState = state.get(); |
| state.mPlugged = false; |
| state.mBucket = -1; |
| shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldShow).isTrue(); |
| |
| // don't show if plugged -> unplugged, bucket 0 -> 0 |
| state.mPlugged = true; |
| state.mBucket = 0; |
| lastState = state.get(); |
| state.mPlugged = false; |
| state.mBucket = 0; |
| shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldShow).isFalse(); |
| |
| // show if unplugged -> unplugged, bucket 0 -> -1 |
| state.mPlugged = false; |
| state.mBucket = 0; |
| lastState = state.get(); |
| state.mPlugged = false; |
| state.mBucket = -1; |
| shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldShow).isTrue(); |
| |
| // don't show if unplugged -> unplugged, bucket -1 -> 1 |
| state.mPlugged = false; |
| state.mBucket = -1; |
| lastState = state.get(); |
| state.mPlugged = false; |
| state.mBucket = 1; |
| shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldShow).isFalse(); |
| } |
| |
| @Test |
| public void testShouldDismissStandardWarning() { |
| mPowerUI.start(); |
| BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); |
| state.mIsHybrid = false; |
| BatteryStateSnapshot lastState = state.get(); |
| |
| // should dismiss if battery saver |
| state.mIsPowerSaver = true; |
| boolean shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldDismiss).isTrue(); |
| |
| state.mIsPowerSaver = false; |
| // should dismiss if plugged |
| state.mPlugged = true; |
| shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldDismiss).isTrue(); |
| |
| state.mPlugged = false; |
| // should dismiss if bucket 0 -> 1 |
| state.mBucket = 0; |
| lastState = state.get(); |
| state.mBucket = 1; |
| shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldDismiss).isTrue(); |
| |
| // shouldn't dismiss if bucket -1 -> 0 |
| state.mBucket = -1; |
| lastState = state.get(); |
| state.mBucket = 0; |
| shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldDismiss).isFalse(); |
| |
| // should dismiss if powersaver & bucket 0 -> 1 |
| state.mIsPowerSaver = true; |
| state.mBucket = 0; |
| lastState = state.get(); |
| state.mBucket = 1; |
| shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState); |
| assertThat(shouldDismiss).isTrue(); |
| } |
| |
| private Temperature getEmergencyStatusTemp(int type, String name) { |
| final float value = 65; |
| return new Temperature(value, type, name, Temperature.THROTTLING_EMERGENCY); |
| } |
| |
| private Temperature getCriticalStatusTemp(int type, String name) { |
| final float value = 60; |
| return new Temperature(value, type, name, Temperature.THROTTLING_CRITICAL); |
| } |
| |
| private void createPowerUi() { |
| mPowerUI = new PowerUI(mContext, mBroadcastDispatcher); |
| mPowerUI.mComponents = mContext.getComponents(); |
| mPowerUI.mThermalService = mThermalServiceMock; |
| } |
| |
| /** |
| * A simple wrapper class that sets values by default and makes them not final to improve |
| * test clarity. |
| */ |
| private class BatteryStateSnapshotWrapper { |
| public int mBatteryLevel = 100; |
| public boolean mIsPowerSaver = false; |
| public boolean mPlugged = false; |
| public long mSevereThresholdMillis = Duration.ofHours(1).toMillis(); |
| public long mLowThresholdMillis = Duration.ofHours(3).toMillis(); |
| public int mSevereLevelThreshold = 5; |
| public int mLowLevelThreshold = 15; |
| public int mBucket = 1; |
| public int mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD; |
| public long mTimeRemainingMillis = Duration.ofHours(24).toMillis(); |
| public boolean mIsBasedOnUsage = true; |
| public boolean mIsHybrid = true; |
| public boolean mIsLowLevelWarningEnabled = true; |
| private long mAverageTimeToDischargeMillis = Duration.ofHours(24).toMillis(); |
| |
| public BatteryStateSnapshot get() { |
| if (mIsHybrid) { |
| return new BatteryStateSnapshot(mBatteryLevel, mIsPowerSaver, mPlugged, mBucket, |
| mBatteryStatus, mSevereLevelThreshold, mLowLevelThreshold, |
| mTimeRemainingMillis, mAverageTimeToDischargeMillis, mSevereThresholdMillis, |
| mLowThresholdMillis, mIsBasedOnUsage, mIsLowLevelWarningEnabled); |
| } else { |
| return new BatteryStateSnapshot(mBatteryLevel, mIsPowerSaver, mPlugged, mBucket, |
| mBatteryStatus, mSevereLevelThreshold, mLowLevelThreshold); |
| } |
| } |
| } |
| } |