1 /* 2 * Copyright (C) 2017 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 package com.android.settings.fuelgauge; 17 18 import android.content.Context; 19 import android.os.BatteryStats; 20 import android.os.Process; 21 import android.text.format.DateUtils; 22 23 import com.android.internal.os.BatterySipper; 24 import com.android.internal.os.BatteryStatsHelper; 25 import com.android.settings.SettingsRobolectricTestRunner; 26 import com.android.settings.TestConfig; 27 import com.android.settings.testutils.FakeFeatureFactory; 28 29 import org.junit.Before; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.mockito.Answers; 33 import org.mockito.Mock; 34 import org.mockito.MockitoAnnotations; 35 import org.robolectric.RuntimeEnvironment; 36 import org.robolectric.annotation.Config; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 41 import static android.os.BatteryStats.Uid.PROCESS_STATE_BACKGROUND; 42 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND; 43 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE; 44 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP; 45 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING; 46 47 import static com.google.common.truth.Truth.assertThat; 48 49 import static org.mockito.Matchers.any; 50 import static org.mockito.Matchers.anyInt; 51 import static org.mockito.Matchers.anyLong; 52 import static org.mockito.Mockito.RETURNS_DEEP_STUBS; 53 import static org.mockito.Mockito.doNothing; 54 import static org.mockito.Mockito.doReturn; 55 import static org.mockito.Matchers.eq; 56 import static org.mockito.Mockito.mock; 57 import static org.mockito.Mockito.when; 58 import static org.mockito.Mockito.spy; 59 60 @RunWith(SettingsRobolectricTestRunner.class) 61 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) 62 public class BatteryUtilsTest { 63 // unit that used to converted ms to us 64 private static final long UNIT = 1000; 65 private static final long TIME_STATE_TOP = 1500 * UNIT; 66 private static final long TIME_STATE_FOREGROUND_SERVICE = 2000 * UNIT; 67 private static final long TIME_STATE_TOP_SLEEPING = 2500 * UNIT; 68 private static final long TIME_STATE_FOREGROUND = 3000 * UNIT; 69 private static final long TIME_STATE_BACKGROUND = 6000 * UNIT; 70 private static final long TIME_FOREGROUND_ACTIVITY_ZERO = 0; 71 private static final long TIME_FOREGROUND_ACTIVITY = 100 * DateUtils.MINUTE_IN_MILLIS; 72 private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000; 73 74 private static final int UID = 123; 75 private static final long TIME_EXPECTED_FOREGROUND = 1500; 76 private static final long TIME_EXPECTED_BACKGROUND = 6000; 77 private static final long TIME_EXPECTED_ALL = 7500; 78 private static final double BATTERY_SCREEN_USAGE = 300; 79 private static final double BATTERY_SYSTEM_USAGE = 600; 80 private static final double BATTERY_OVERACCOUNTED_USAGE = 500; 81 private static final double BATTERY_UNACCOUNTED_USAGE = 700; 82 private static final double BATTERY_APP_USAGE = 100; 83 private static final double BATTERY_WIFI_USAGE = 200; 84 private static final double BATTERY_BLUETOOTH_USAGE = 300; 85 private static final double TOTAL_BATTERY_USAGE = 1000; 86 private static final double HIDDEN_USAGE = 200; 87 private static final int DISCHARGE_AMOUNT = 80; 88 private static final double PERCENT_SYSTEM_USAGE = 60; 89 private static final double PRECISION = 0.001; 90 91 @Mock 92 private BatteryStats.Uid mUid; 93 @Mock 94 private BatterySipper mNormalBatterySipper; 95 @Mock 96 private BatterySipper mWifiBatterySipper; 97 @Mock 98 private BatterySipper mBluetoothBatterySipper; 99 @Mock 100 private BatterySipper mScreenBatterySipper; 101 @Mock 102 private BatterySipper mOvercountedBatterySipper; 103 @Mock 104 private BatterySipper mUnaccountedBatterySipper; 105 @Mock 106 private BatterySipper mSystemBatterySipper; 107 @Mock 108 private BatterySipper mCellBatterySipper; 109 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 110 private Context mContext; 111 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 112 private BatteryStatsHelper mBatteryStatsHelper; 113 private BatteryUtils mBatteryUtils; 114 private FakeFeatureFactory mFeatureFactory; 115 private PowerUsageFeatureProvider mProvider; 116 117 @Before setUp()118 public void setUp() { 119 MockitoAnnotations.initMocks(this); 120 121 FakeFeatureFactory.setupForTest(mContext); 122 mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); 123 mProvider = mFeatureFactory.powerUsageFeatureProvider; 124 125 doReturn(TIME_STATE_TOP).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP), anyLong(), 126 anyInt()); 127 doReturn(TIME_STATE_FOREGROUND_SERVICE).when(mUid).getProcessStateTime( 128 eq(PROCESS_STATE_FOREGROUND_SERVICE), anyLong(), anyInt()); 129 doReturn(TIME_STATE_TOP_SLEEPING).when(mUid).getProcessStateTime( 130 eq(PROCESS_STATE_TOP_SLEEPING), anyLong(), anyInt()); 131 doReturn(TIME_STATE_FOREGROUND).when(mUid).getProcessStateTime(eq(PROCESS_STATE_FOREGROUND), 132 anyLong(), anyInt()); 133 doReturn(TIME_STATE_BACKGROUND).when(mUid).getProcessStateTime(eq(PROCESS_STATE_BACKGROUND), 134 anyLong(), anyInt()); 135 136 mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; 137 mNormalBatterySipper.totalPowerMah = TOTAL_BATTERY_USAGE; 138 139 mWifiBatterySipper.drainType = BatterySipper.DrainType.WIFI; 140 mWifiBatterySipper.totalPowerMah = BATTERY_WIFI_USAGE; 141 142 mBluetoothBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH; 143 mBluetoothBatterySipper.totalPowerMah = BATTERY_BLUETOOTH_USAGE; 144 145 mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN; 146 mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE; 147 148 mSystemBatterySipper.drainType = BatterySipper.DrainType.APP; 149 mSystemBatterySipper.totalPowerMah = BATTERY_SYSTEM_USAGE; 150 when(mSystemBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID); 151 152 mOvercountedBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED; 153 mOvercountedBatterySipper.totalPowerMah = BATTERY_OVERACCOUNTED_USAGE; 154 155 mUnaccountedBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED; 156 mUnaccountedBatterySipper.totalPowerMah = BATTERY_UNACCOUNTED_USAGE; 157 158 mBatteryUtils = BatteryUtils.getInstance(RuntimeEnvironment.application); 159 mBatteryUtils.mPowerUsageFeatureProvider = mProvider; 160 161 mBatteryUtils = spy(new BatteryUtils(RuntimeEnvironment.application)); 162 } 163 164 @Test testGetProcessTimeMs_typeForeground_timeCorrect()165 public void testGetProcessTimeMs_typeForeground_timeCorrect() { 166 final long time = mBatteryUtils.getProcessTimeMs(BatteryUtils.StatusType.FOREGROUND, mUid, 167 BatteryStats.STATS_SINCE_CHARGED); 168 169 assertThat(time).isEqualTo(TIME_EXPECTED_FOREGROUND); 170 } 171 172 @Test testGetProcessTimeMs_typeBackground_timeCorrect()173 public void testGetProcessTimeMs_typeBackground_timeCorrect() { 174 final long time = mBatteryUtils.getProcessTimeMs(BatteryUtils.StatusType.BACKGROUND, mUid, 175 BatteryStats.STATS_SINCE_CHARGED); 176 177 assertThat(time).isEqualTo(TIME_EXPECTED_BACKGROUND); 178 } 179 180 @Test testGetProcessTimeMs_typeAll_timeCorrect()181 public void testGetProcessTimeMs_typeAll_timeCorrect() { 182 final long time = mBatteryUtils.getProcessTimeMs(BatteryUtils.StatusType.ALL, mUid, 183 BatteryStats.STATS_SINCE_CHARGED); 184 185 assertThat(time).isEqualTo(TIME_EXPECTED_ALL); 186 } 187 188 @Test testGetProcessTimeMs_uidNull_returnZero()189 public void testGetProcessTimeMs_uidNull_returnZero() { 190 final long time = mBatteryUtils.getProcessTimeMs(BatteryUtils.StatusType.ALL, null, 191 BatteryStats.STATS_SINCE_CHARGED); 192 193 assertThat(time).isEqualTo(0); 194 } 195 196 @Test testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue()197 public void testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue() { 198 final List<BatterySipper> sippers = new ArrayList<>(); 199 sippers.add(mNormalBatterySipper); 200 sippers.add(mScreenBatterySipper); 201 sippers.add(mSystemBatterySipper); 202 sippers.add(mOvercountedBatterySipper); 203 sippers.add(mUnaccountedBatterySipper); 204 sippers.add(mWifiBatterySipper); 205 sippers.add(mBluetoothBatterySipper); 206 when(mProvider.isTypeSystem(mSystemBatterySipper)) 207 .thenReturn(true); 208 doNothing().when(mBatteryUtils).smearScreenBatterySipper(any(), any()); 209 210 final double totalUsage = mBatteryUtils.removeHiddenBatterySippers(sippers); 211 212 assertThat(sippers).containsExactly(mNormalBatterySipper); 213 assertThat(totalUsage).isWithin(PRECISION).of(BATTERY_SYSTEM_USAGE); 214 } 215 216 @Test testShouldHideSipper_TypeUnAccounted_ReturnTrue()217 public void testShouldHideSipper_TypeUnAccounted_ReturnTrue() { 218 mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED; 219 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); 220 } 221 222 @Test testShouldHideSipper_TypeOverAccounted_ReturnTrue()223 public void testShouldHideSipper_TypeOverAccounted_ReturnTrue() { 224 mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED; 225 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); 226 } 227 228 @Test testShouldHideSipper_TypeIdle_ReturnTrue()229 public void testShouldHideSipper_TypeIdle_ReturnTrue() { 230 mNormalBatterySipper.drainType = BatterySipper.DrainType.IDLE; 231 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); 232 } 233 234 @Test testShouldHideSipper_TypeCell_ReturnTrue()235 public void testShouldHideSipper_TypeCell_ReturnTrue() { 236 mNormalBatterySipper.drainType = BatterySipper.DrainType.CELL; 237 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); 238 } 239 240 @Test testShouldHideSipper_TypeScreen_ReturnTrue()241 public void testShouldHideSipper_TypeScreen_ReturnTrue() { 242 mNormalBatterySipper.drainType = BatterySipper.DrainType.SCREEN; 243 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); 244 } 245 246 @Test testShouldHideSipper_TypeWifi_ReturnTrue()247 public void testShouldHideSipper_TypeWifi_ReturnTrue() { 248 mNormalBatterySipper.drainType = BatterySipper.DrainType.WIFI; 249 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); 250 } 251 252 @Test testShouldHideSipper_TypeBluetooth_ReturnTrue()253 public void testShouldHideSipper_TypeBluetooth_ReturnTrue() { 254 mNormalBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH; 255 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); 256 } 257 258 @Test testShouldHideSipper_TypeSystem_ReturnTrue()259 public void testShouldHideSipper_TypeSystem_ReturnTrue() { 260 mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; 261 when(mNormalBatterySipper.getUid()).thenReturn(Process.ROOT_UID); 262 when(mProvider.isTypeSystem(any())).thenReturn(true); 263 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); 264 } 265 266 @Test testShouldHideSipper_UidNormal_ReturnFalse()267 public void testShouldHideSipper_UidNormal_ReturnFalse() { 268 mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; 269 when(mNormalBatterySipper.getUid()).thenReturn(UID); 270 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isFalse(); 271 } 272 273 @Test testShouldHideSipper_TypeService_ReturnTrue()274 public void testShouldHideSipper_TypeService_ReturnTrue() { 275 mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; 276 when(mNormalBatterySipper.getUid()).thenReturn(UID); 277 when(mProvider.isTypeService(any())).thenReturn(true); 278 279 assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); 280 } 281 282 @Test testCalculateBatteryPercent()283 public void testCalculateBatteryPercent() { 284 assertThat(mBatteryUtils.calculateBatteryPercent(BATTERY_SYSTEM_USAGE, TOTAL_BATTERY_USAGE, 285 HIDDEN_USAGE, DISCHARGE_AMOUNT)) 286 .isWithin(PRECISION).of(PERCENT_SYSTEM_USAGE); 287 } 288 289 @Test testSmearScreenBatterySipper()290 public void testSmearScreenBatterySipper() { 291 final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, 292 BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */); 293 final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, 294 BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */); 295 final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY, 296 BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */); 297 298 final List<BatterySipper> sippers = new ArrayList<>(); 299 sippers.add(sipperNull); 300 sippers.add(sipperBg); 301 sippers.add(sipperFg); 302 303 mBatteryUtils.smearScreenBatterySipper(sippers, mScreenBatterySipper); 304 305 assertThat(sipperNull.totalPowerMah).isWithin(PRECISION).of(BATTERY_APP_USAGE); 306 assertThat(sipperBg.totalPowerMah).isWithin(PRECISION).of(BATTERY_APP_USAGE); 307 assertThat(sipperFg.totalPowerMah).isWithin(PRECISION).of( 308 BATTERY_APP_USAGE + BATTERY_SCREEN_USAGE); 309 } 310 311 @Test testSortUsageList()312 public void testSortUsageList() { 313 final List<BatterySipper> sippers = new ArrayList<>(); 314 sippers.add(mNormalBatterySipper); 315 sippers.add(mScreenBatterySipper); 316 sippers.add(mSystemBatterySipper); 317 318 mBatteryUtils.sortUsageList(sippers); 319 320 assertThat(sippers).containsExactly(mNormalBatterySipper, mSystemBatterySipper, 321 mScreenBatterySipper); 322 } 323 324 @Test testCalculateLastFullChargeTime()325 public void testCalculateLastFullChargeTime() { 326 final long currentTimeMs = System.currentTimeMillis(); 327 when(mBatteryStatsHelper.getStats().getStartClockTime()).thenReturn( 328 currentTimeMs - TIME_SINCE_LAST_FULL_CHARGE_MS); 329 330 assertThat(mBatteryUtils.calculateLastFullChargeTime( 331 mBatteryStatsHelper, currentTimeMs)).isEqualTo(TIME_SINCE_LAST_FULL_CHARGE_MS); 332 } 333 createTestSmearBatterySipper(long activityTime, double totalPowerMah, int uidCode, boolean isUidNull)334 private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah, 335 int uidCode, boolean isUidNull) { 336 final BatterySipper sipper = mock(BatterySipper.class); 337 sipper.drainType = BatterySipper.DrainType.APP; 338 sipper.totalPowerMah = totalPowerMah; 339 doReturn(uidCode).when(sipper).getUid(); 340 if (!isUidNull) { 341 final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS); 342 doReturn(activityTime).when(mBatteryUtils).getForegroundActivityTotalTimeMs(eq(uid), 343 anyLong()); 344 doReturn(uidCode).when(uid).getUid(); 345 sipper.uidObj = uid; 346 } 347 348 return sipper; 349 } 350 } 351