1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * 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 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 * 15 * 16 */ 17 18 package com.android.internal.os; 19 20 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.Matchers.anyInt; 25 import static org.mockito.Matchers.anyLong; 26 import static org.mockito.Matchers.eq; 27 import static org.mockito.Mockito.RETURNS_DEEP_STUBS; 28 import static org.mockito.Mockito.doReturn; 29 import static org.mockito.Mockito.mock; 30 import static org.mockito.Mockito.spy; 31 import static org.mockito.Mockito.when; 32 33 import android.content.Context; 34 import android.content.pm.PackageManager; 35 import android.os.BatteryStats; 36 import android.os.Process; 37 import android.text.format.DateUtils; 38 39 import androidx.test.InstrumentationRegistry; 40 import androidx.test.filters.SmallTest; 41 import androidx.test.runner.AndroidJUnit4; 42 43 import com.android.internal.util.FrameworkStatsLog; 44 45 import junit.framework.TestCase; 46 47 import org.junit.Before; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 import org.mockito.Mock; 51 import org.mockito.MockitoAnnotations; 52 53 import java.util.ArrayList; 54 import java.util.List; 55 56 @RunWith(AndroidJUnit4.class) 57 @SmallTest 58 public class BatteryStatsHelperTest extends TestCase { 59 private static final long TIME_FOREGROUND_ACTIVITY_ZERO = 0; 60 private static final long TIME_FOREGROUND_ACTIVITY = 100 * DateUtils.MINUTE_IN_MILLIS * 1000; 61 private static final long TIME_STATE_FOREGROUND_MS = 10 * DateUtils.MINUTE_IN_MILLIS; 62 private static final long TIME_STATE_FOREGROUND_US = TIME_STATE_FOREGROUND_MS * 1000; 63 64 private static final int UID = 123456; 65 private static final double BATTERY_SCREEN_USAGE = 300; 66 private static final double BATTERY_SYSTEM_USAGE = 600; 67 private static final double BATTERY_WIFI_USAGE = 200; 68 private static final double BATTERY_IDLE_USAGE = 600; 69 private static final double BATTERY_BLUETOOTH_USAGE = 300; 70 private static final double BATTERY_OVERACCOUNTED_USAGE = 500; 71 private static final double BATTERY_UNACCOUNTED_USAGE = 700; 72 private static final double BATTERY_APP_USAGE = 100; 73 private static final double TOTAL_BATTERY_USAGE = 1000; 74 private static final double PRECISION = 0.001; 75 76 @Mock 77 private BatteryStats.Uid mUid; 78 @Mock 79 private BatterySipper mWifiBatterySipper; 80 @Mock 81 private BatterySipper mBluetoothBatterySipper; 82 @Mock 83 private BatterySipper mIdleBatterySipper; 84 @Mock 85 private BatterySipper mNormalBatterySipper; 86 @Mock 87 private BatterySipper mScreenBatterySipper; 88 @Mock 89 private BatterySipper mOvercountedBatterySipper; 90 @Mock 91 private BatterySipper mUnaccountedBatterySipper; 92 @Mock 93 private BatterySipper mSystemBatterySipper; 94 @Mock 95 private BatterySipper mCellBatterySipper; 96 @Mock 97 private PackageManager mPackageManager; 98 99 private BatteryStatsHelper mBatteryStatsHelper; 100 private Context mContext; 101 102 @Before setUp()103 public void setUp() { 104 MockitoAnnotations.initMocks(this); 105 106 mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; 107 mNormalBatterySipper.totalPowerMah = TOTAL_BATTERY_USAGE; 108 when(mNormalBatterySipper.getUid()).thenReturn(UID); 109 mNormalBatterySipper.uidObj = mUid; 110 111 112 mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN; 113 mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE; 114 115 mSystemBatterySipper.drainType = BatterySipper.DrainType.APP; 116 mSystemBatterySipper.totalPowerMah = BATTERY_SYSTEM_USAGE; 117 mSystemBatterySipper.uidObj = mUid; 118 when(mSystemBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID); 119 120 mOvercountedBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED; 121 mOvercountedBatterySipper.totalPowerMah = BATTERY_OVERACCOUNTED_USAGE; 122 123 mUnaccountedBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED; 124 mUnaccountedBatterySipper.totalPowerMah = BATTERY_UNACCOUNTED_USAGE; 125 126 mWifiBatterySipper.drainType = BatterySipper.DrainType.WIFI; 127 mWifiBatterySipper.totalPowerMah = BATTERY_WIFI_USAGE; 128 129 mBluetoothBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH; 130 mBluetoothBatterySipper.totalPowerMah = BATTERY_BLUETOOTH_USAGE; 131 132 mIdleBatterySipper.drainType = BatterySipper.DrainType.IDLE; 133 mIdleBatterySipper.totalPowerMah = BATTERY_IDLE_USAGE; 134 135 mContext = InstrumentationRegistry.getContext(); 136 mBatteryStatsHelper = spy(new BatteryStatsHelper(mContext)); 137 mBatteryStatsHelper.setPackageManager(mPackageManager); 138 } 139 140 @Test testShouldHideSipper_TypeUnAccounted_ReturnTrue()141 public void testShouldHideSipper_TypeUnAccounted_ReturnTrue() { 142 mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED; 143 assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue(); 144 } 145 146 @Test testShouldHideSipper_TypeOverAccounted_ReturnTrue()147 public void testShouldHideSipper_TypeOverAccounted_ReturnTrue() { 148 mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED; 149 assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue(); 150 } 151 152 @Test testShouldHideSipper_TypeIdle_ReturnTrue()153 public void testShouldHideSipper_TypeIdle_ReturnTrue() { 154 mNormalBatterySipper.drainType = BatterySipper.DrainType.IDLE; 155 assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue(); 156 } 157 158 @Test testShouldHideSipper_TypeCell_ReturnTrue()159 public void testShouldHideSipper_TypeCell_ReturnTrue() { 160 mNormalBatterySipper.drainType = BatterySipper.DrainType.CELL; 161 assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue(); 162 } 163 164 @Test testShouldHideSipper_TypeScreen_ReturnTrue()165 public void testShouldHideSipper_TypeScreen_ReturnTrue() { 166 mNormalBatterySipper.drainType = BatterySipper.DrainType.SCREEN; 167 assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue(); 168 } 169 170 @Test testShouldHideSipper_TypeSystem_ReturnTrue()171 public void testShouldHideSipper_TypeSystem_ReturnTrue() { 172 mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; 173 when(mNormalBatterySipper.getUid()).thenReturn(Process.ROOT_UID); 174 assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue(); 175 } 176 177 @Test testShouldHideSipper_UidNormal_ReturnFalse()178 public void testShouldHideSipper_UidNormal_ReturnFalse() { 179 mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; 180 assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isFalse(); 181 } 182 183 @Test testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue()184 public void testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue() { 185 final List<BatterySipper> sippers = new ArrayList<>(); 186 sippers.add(mNormalBatterySipper); 187 sippers.add(mScreenBatterySipper); 188 sippers.add(mSystemBatterySipper); 189 sippers.add(mOvercountedBatterySipper); 190 sippers.add(mUnaccountedBatterySipper); 191 sippers.add(mWifiBatterySipper); 192 sippers.add(mBluetoothBatterySipper); 193 sippers.add(mIdleBatterySipper); 194 doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper); 195 196 final double totalUsage = mBatteryStatsHelper.removeHiddenBatterySippers(sippers); 197 198 assertThat(mNormalBatterySipper.shouldHide).isFalse(); 199 assertThat(mScreenBatterySipper.shouldHide).isTrue(); 200 assertThat(mSystemBatterySipper.shouldHide).isTrue(); 201 assertThat(mOvercountedBatterySipper.shouldHide).isTrue(); 202 assertThat(mUnaccountedBatterySipper.shouldHide).isTrue(); 203 assertThat(totalUsage).isWithin(PRECISION).of(BATTERY_SYSTEM_USAGE); 204 } 205 206 @Test testSmearScreenBatterySipper()207 public void testSmearScreenBatterySipper() { 208 final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class); 209 final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, 210 BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */, spc); 211 final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, 212 BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */, spc); 213 final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY, 214 BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */, spc); 215 216 final List<BatterySipper> sippers = new ArrayList<>(); 217 sippers.add(sipperNull); 218 sippers.add(sipperBg); 219 sippers.add(sipperFg); 220 221 spc.smearScreenBatterySipper(sippers, mScreenBatterySipper, 0); 222 223 assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0); 224 assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0); 225 assertThat(sipperFg.screenPowerMah).isWithin(PRECISION).of(BATTERY_SCREEN_USAGE); 226 } 227 228 @Test testIsTypeSystem_systemPackage_returnTrue()229 public void testIsTypeSystem_systemPackage_returnTrue() { 230 final String[] systemPackages = {"com.android.system"}; 231 mBatteryStatsHelper.setSystemPackageArray(systemPackages); 232 doReturn(UID).when(mNormalBatterySipper).getUid(); 233 doReturn(systemPackages).when(mPackageManager).getPackagesForUid(UID); 234 235 assertThat(mBatteryStatsHelper.isTypeSystem(mNormalBatterySipper)).isTrue(); 236 } 237 238 @Test testIsTypeService_servicePackage_returnTrue()239 public void testIsTypeService_servicePackage_returnTrue() { 240 final String[] servicePackages = {"com.android.service"}; 241 mBatteryStatsHelper.setServicePackageArray(servicePackages); 242 doReturn(UID).when(mNormalBatterySipper).getUid(); 243 doReturn(servicePackages).when(mPackageManager).getPackagesForUid(UID); 244 245 assertThat(mBatteryStatsHelper.isTypeService(mNormalBatterySipper)).isTrue(); 246 } 247 248 @Test testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime()249 public void testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime() { 250 final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class); 251 doReturn(TIME_STATE_FOREGROUND_US + 500).when(spc) 252 .getForegroundActivityTotalTimeUs(eq(mUid), anyLong()); 253 doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP), 254 anyLong(), anyInt()); 255 256 final long time = spc.getProcessForegroundTimeMs(mUid, 1000); 257 258 assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS); 259 } 260 261 @Test testDrainTypesSyncedWithProto()262 public void testDrainTypesSyncedWithProto() { 263 assertEquals(BatterySipper.DrainType.AMBIENT_DISPLAY.ordinal(), 264 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__AMBIENT_DISPLAY); 265 // AtomsProto has no "APP" 266 assertEquals(BatterySipper.DrainType.BLUETOOTH.ordinal(), 267 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__BLUETOOTH); 268 assertEquals(BatterySipper.DrainType.CAMERA.ordinal(), 269 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CAMERA); 270 assertEquals(BatterySipper.DrainType.CELL.ordinal(), 271 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CELL); 272 assertEquals(BatterySipper.DrainType.FLASHLIGHT.ordinal(), 273 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__FLASHLIGHT); 274 assertEquals(BatterySipper.DrainType.IDLE.ordinal(), 275 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__IDLE); 276 assertEquals(BatterySipper.DrainType.MEMORY.ordinal(), 277 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__MEMORY); 278 assertEquals(BatterySipper.DrainType.OVERCOUNTED.ordinal(), 279 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__OVERCOUNTED); 280 assertEquals(BatterySipper.DrainType.PHONE.ordinal(), 281 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__PHONE); 282 assertEquals(BatterySipper.DrainType.SCREEN.ordinal(), 283 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__SCREEN); 284 assertEquals(BatterySipper.DrainType.UNACCOUNTED.ordinal(), 285 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__UNACCOUNTED); 286 // AtomsProto has no "USER" 287 assertEquals(BatterySipper.DrainType.WIFI.ordinal(), 288 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__WIFI); 289 } 290 createTestSmearBatterySipper(long activityTime, double totalPowerMah, int uidCode, boolean isUidNull, ScreenPowerCalculator spc)291 private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah, 292 int uidCode, boolean isUidNull, ScreenPowerCalculator spc) { 293 final BatterySipper sipper = mock(BatterySipper.class); 294 sipper.drainType = BatterySipper.DrainType.APP; 295 sipper.totalPowerMah = totalPowerMah; 296 doReturn(uidCode).when(sipper).getUid(); 297 if (!isUidNull) { 298 final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS); 299 doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid), anyLong()); 300 doReturn(uidCode).when(uid).getUid(); 301 sipper.uidObj = uid; 302 } 303 304 return sipper; 305 } 306 307 } 308