1 /* 2 * Copyright (C) 2021 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 static com.google.common.truth.Truth.assertThat; 19 20 import static org.mockito.Mockito.spy; 21 import static org.mockito.Mockito.when; 22 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.os.BatteryManager; 26 import android.os.BatteryUsageStats; 27 import android.os.LocaleList; 28 import android.os.UserHandle; 29 30 import com.android.settings.testutils.FakeFeatureFactory; 31 32 import org.junit.Before; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.mockito.Mock; 36 import org.mockito.MockitoAnnotations; 37 import org.robolectric.RobolectricTestRunner; 38 import org.robolectric.RuntimeEnvironment; 39 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.HashMap; 43 import java.util.HashSet; 44 import java.util.List; 45 import java.util.Locale; 46 import java.util.Map; 47 import java.util.TimeZone; 48 49 @RunWith(RobolectricTestRunner.class) 50 public final class ConvertUtilsTest { 51 52 private Context mContext; 53 54 @Mock 55 private BatteryUsageStats mBatteryUsageStats; 56 @Mock 57 private BatteryEntry mockBatteryEntry; 58 59 private FakeFeatureFactory mFeatureFactory; 60 private PowerUsageFeatureProvider mPowerUsageFeatureProvider; 61 62 @Before setUp()63 public void setUp() { 64 MockitoAnnotations.initMocks(this); 65 mContext = spy(RuntimeEnvironment.application); 66 mFeatureFactory = FakeFeatureFactory.setupForTest(); 67 mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider; 68 } 69 70 @Test convert_returnsExpectedContentValues()71 public void convert_returnsExpectedContentValues() { 72 final int expectedType = 3; 73 when(mockBatteryEntry.getUid()).thenReturn(1001); 74 when(mockBatteryEntry.getLabel()).thenReturn("Settings"); 75 when(mockBatteryEntry.getDefaultPackageName()) 76 .thenReturn("com.google.android.settings.battery"); 77 when(mockBatteryEntry.isHidden()).thenReturn(true); 78 when(mBatteryUsageStats.getConsumedPower()).thenReturn(5.1); 79 when(mockBatteryEntry.getConsumedPower()).thenReturn(1.1); 80 mockBatteryEntry.percent = 0.3; 81 when(mockBatteryEntry.getTimeInForegroundMs()).thenReturn(1234L); 82 when(mockBatteryEntry.getTimeInBackgroundMs()).thenReturn(5689L); 83 when(mockBatteryEntry.getPowerComponentId()).thenReturn(expectedType); 84 when(mockBatteryEntry.getConsumerType()) 85 .thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY); 86 87 final ContentValues values = 88 ConvertUtils.convert( 89 mockBatteryEntry, 90 mBatteryUsageStats, 91 /*batteryLevel=*/ 12, 92 /*batteryStatus=*/ BatteryManager.BATTERY_STATUS_FULL, 93 /*batteryHealth=*/ BatteryManager.BATTERY_HEALTH_COLD, 94 /*bootTimestamp=*/ 101L, 95 /*timestamp=*/ 10001L); 96 97 assertThat(values.getAsLong(BatteryHistEntry.KEY_UID)).isEqualTo(1001L); 98 assertThat(values.getAsLong(BatteryHistEntry.KEY_USER_ID)) 99 .isEqualTo(UserHandle.getUserId(1001)); 100 assertThat(values.getAsString(BatteryHistEntry.KEY_APP_LABEL)) 101 .isEqualTo("Settings"); 102 assertThat(values.getAsString(BatteryHistEntry.KEY_PACKAGE_NAME)) 103 .isEqualTo("com.google.android.settings.battery"); 104 assertThat(values.getAsBoolean(BatteryHistEntry.KEY_IS_HIDDEN)).isTrue(); 105 assertThat(values.getAsLong(BatteryHistEntry.KEY_BOOT_TIMESTAMP)) 106 .isEqualTo(101L); 107 assertThat(values.getAsLong(BatteryHistEntry.KEY_TIMESTAMP)).isEqualTo(10001L); 108 assertThat(values.getAsString(BatteryHistEntry.KEY_ZONE_ID)) 109 .isEqualTo(TimeZone.getDefault().getID()); 110 assertThat(values.getAsDouble(BatteryHistEntry.KEY_TOTAL_POWER)).isEqualTo(5.1); 111 assertThat(values.getAsDouble(BatteryHistEntry.KEY_CONSUME_POWER)).isEqualTo(1.1); 112 assertThat(values.getAsDouble(BatteryHistEntry.KEY_PERCENT_OF_TOTAL)).isEqualTo(0.3); 113 assertThat(values.getAsLong(BatteryHistEntry.KEY_FOREGROUND_USAGE_TIME)) 114 .isEqualTo(1234L); 115 assertThat(values.getAsLong(BatteryHistEntry.KEY_BACKGROUND_USAGE_TIME)) 116 .isEqualTo(5689L); 117 assertThat(values.getAsInteger(BatteryHistEntry.KEY_DRAIN_TYPE)).isEqualTo(expectedType); 118 assertThat(values.getAsInteger(BatteryHistEntry.KEY_CONSUMER_TYPE)) 119 .isEqualTo(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY); 120 assertThat(values.getAsInteger(BatteryHistEntry.KEY_BATTERY_LEVEL)).isEqualTo(12); 121 assertThat(values.getAsInteger(BatteryHistEntry.KEY_BATTERY_STATUS)) 122 .isEqualTo(BatteryManager.BATTERY_STATUS_FULL); 123 assertThat(values.getAsInteger(BatteryHistEntry.KEY_BATTERY_HEALTH)) 124 .isEqualTo(BatteryManager.BATTERY_HEALTH_COLD); 125 } 126 127 @Test convert_nullBatteryEntry_returnsExpectedContentValues()128 public void convert_nullBatteryEntry_returnsExpectedContentValues() { 129 final ContentValues values = 130 ConvertUtils.convert( 131 /*entry=*/ null, 132 /*batteryUsageStats=*/ null, 133 /*batteryLevel=*/ 12, 134 /*batteryStatus=*/ BatteryManager.BATTERY_STATUS_FULL, 135 /*batteryHealth=*/ BatteryManager.BATTERY_HEALTH_COLD, 136 /*bootTimestamp=*/ 101L, 137 /*timestamp=*/ 10001L); 138 139 assertThat(values.getAsLong(BatteryHistEntry.KEY_BOOT_TIMESTAMP)) 140 .isEqualTo(101L); 141 assertThat(values.getAsLong(BatteryHistEntry.KEY_TIMESTAMP)) 142 .isEqualTo(10001L); 143 assertThat(values.getAsString(BatteryHistEntry.KEY_ZONE_ID)) 144 .isEqualTo(TimeZone.getDefault().getID()); 145 assertThat(values.getAsInteger(BatteryHistEntry.KEY_BATTERY_LEVEL)).isEqualTo(12); 146 assertThat(values.getAsInteger(BatteryHistEntry.KEY_BATTERY_STATUS)) 147 .isEqualTo(BatteryManager.BATTERY_STATUS_FULL); 148 assertThat(values.getAsInteger(BatteryHistEntry.KEY_BATTERY_HEALTH)) 149 .isEqualTo(BatteryManager.BATTERY_HEALTH_COLD); 150 assertThat(values.getAsString(BatteryHistEntry.KEY_PACKAGE_NAME)) 151 .isEqualTo(ConvertUtils.FAKE_PACKAGE_NAME); 152 } 153 154 @Test getIndexedUsageMap_nullOrEmptyHistoryMap_returnEmptyCollection()155 public void getIndexedUsageMap_nullOrEmptyHistoryMap_returnEmptyCollection() { 156 final int timeSlotSize = 2; 157 final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L, 104L, 105L}; 158 159 assertThat(ConvertUtils.getIndexedUsageMap( 160 mContext, timeSlotSize, batteryHistoryKeys, 161 /*batteryHistoryMap=*/ null, /*purgeLowPercentageAndFakeData=*/ true)) 162 .isEmpty(); 163 assertThat(ConvertUtils.getIndexedUsageMap( 164 mContext, timeSlotSize, batteryHistoryKeys, 165 new HashMap<Long, Map<String, BatteryHistEntry>>(), 166 /*purgeLowPercentageAndFakeData=*/ true)) 167 .isEmpty(); 168 } 169 @Test getIndexedUsageMap_returnsExpectedResult()170 public void getIndexedUsageMap_returnsExpectedResult() { 171 // Creates the fake testing data. 172 final int timeSlotSize = 2; 173 final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L, 104L, 105L}; 174 final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = 175 new HashMap<>(); 176 final BatteryHistEntry fakeEntry = createBatteryHistEntry( 177 ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", 0, 0L, 0L, 0L); 178 // Adds the index = 0 data. 179 Map<String, BatteryHistEntry> entryMap = new HashMap<>(); 180 BatteryHistEntry entry = createBatteryHistEntry( 181 "package1", "label1", 5.0, 1L, 10L, 20L); 182 entryMap.put(entry.getKey(), entry); 183 entryMap.put(fakeEntry.getKey(), fakeEntry); 184 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[0]), entryMap); 185 // Adds the index = 1 data. 186 entryMap = new HashMap<>(); 187 entryMap.put(fakeEntry.getKey(), fakeEntry); 188 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[1]), entryMap); 189 // Adds the index = 2 data. 190 entryMap = new HashMap<>(); 191 entry = createBatteryHistEntry( 192 "package2", "label2", 10.0, 2L, 15L, 25L); 193 entryMap.put(entry.getKey(), entry); 194 entryMap.put(fakeEntry.getKey(), fakeEntry); 195 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[2]), entryMap); 196 // Adds the index = 3 data. 197 entryMap = new HashMap<>(); 198 entry = createBatteryHistEntry( 199 "package2", "label2", 15.0, 2L, 25L, 35L); 200 entryMap.put(entry.getKey(), entry); 201 entry = createBatteryHistEntry( 202 "package3", "label3", 5.0, 3L, 5L, 5L); 203 entryMap.put(entry.getKey(), entry); 204 entryMap.put(fakeEntry.getKey(), fakeEntry); 205 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[3]), entryMap); 206 // Adds the index = 4 data. 207 entryMap = new HashMap<>(); 208 entry = createBatteryHistEntry( 209 "package2", "label2", 30.0, 2L, 30L, 40L); 210 entryMap.put(entry.getKey(), entry); 211 entry = createBatteryHistEntry( 212 "package2", "label2", 75.0, 4L, 40L, 50L); 213 entryMap.put(entry.getKey(), entry); 214 entry = createBatteryHistEntry( 215 "package3", "label3", 5.0, 3L, 5L, 5L); 216 entryMap.put(entry.getKey(), entry); 217 entryMap.put(fakeEntry.getKey(), fakeEntry); 218 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[4]), entryMap); 219 220 final Map<Integer, List<BatteryDiffEntry>> resultMap = 221 ConvertUtils.getIndexedUsageMap( 222 mContext, timeSlotSize, batteryHistoryKeys, batteryHistoryMap, 223 /*purgeLowPercentageAndFakeData=*/ false); 224 225 assertThat(resultMap).hasSize(3); 226 // Verifies the first timestamp result. 227 List<BatteryDiffEntry> entryList = resultMap.get(Integer.valueOf(0)); 228 assertThat(entryList).hasSize(1); 229 assertBatteryDiffEntry(entryList.get(0), 100, 15L, 25L); 230 // Verifies the second timestamp result. 231 entryList = resultMap.get(Integer.valueOf(1)); 232 assertThat(entryList).hasSize(3); 233 assertBatteryDiffEntry(entryList.get(1), 5, 5L, 5L); 234 assertBatteryDiffEntry(entryList.get(2), 75, 40L, 50L); 235 assertBatteryDiffEntry(entryList.get(0), 20, 15L, 15L); 236 // Verifies the last 24 hours aggregate result. 237 entryList = resultMap.get(Integer.valueOf(-1)); 238 assertThat(entryList).hasSize(3); 239 assertBatteryDiffEntry(entryList.get(1), 4, 5L, 5L); 240 assertBatteryDiffEntry(entryList.get(2), 68, 40L, 50L); 241 assertBatteryDiffEntry(entryList.get(0), 27, 30L, 40L); 242 243 // Test getIndexedUsageMap() with purged data. 244 ConvertUtils.PERCENTAGE_OF_TOTAL_THRESHOLD = 50; 245 final Map<Integer, List<BatteryDiffEntry>> purgedResultMap = 246 ConvertUtils.getIndexedUsageMap( 247 mContext, timeSlotSize, batteryHistoryKeys, batteryHistoryMap, 248 /*purgeLowPercentageAndFakeData=*/ true); 249 250 assertThat(purgedResultMap).hasSize(3); 251 // Verifies the first timestamp result. 252 entryList = purgedResultMap.get(Integer.valueOf(0)); 253 assertThat(entryList).hasSize(1); 254 // Verifies the second timestamp result. 255 entryList = purgedResultMap.get(Integer.valueOf(1)); 256 assertThat(entryList).hasSize(1); 257 assertBatteryDiffEntry(entryList.get(0), 75, 40L, 50L); 258 // Verifies the last 24 hours aggregate result. 259 entryList = purgedResultMap.get(Integer.valueOf(-1)); 260 assertThat(entryList).hasSize(1); 261 // Verifies the fake data is cleared out. 262 assertThat(entryList.get(0).getPackageName()) 263 .isNotEqualTo(ConvertUtils.FAKE_PACKAGE_NAME); 264 265 // Adds lacked data into the battery history map. 266 final int remainingSize = 25 - batteryHistoryKeys.length; 267 for (int index = 0; index < remainingSize; index++) { 268 batteryHistoryMap.put(105L + index + 1, new HashMap<>()); 269 } 270 when(mPowerUsageFeatureProvider.getBatteryHistory(mContext)) 271 .thenReturn(batteryHistoryMap); 272 273 final List<BatteryDiffEntry> batteryDiffEntryList = 274 BatteryChartPreferenceController.getBatteryLast24HrUsageData(mContext); 275 276 assertThat(batteryDiffEntryList).isNotEmpty(); 277 final BatteryDiffEntry resultEntry = batteryDiffEntryList.get(0); 278 assertThat(resultEntry.getPackageName()).isEqualTo("package2"); 279 } 280 281 @Test getIndexedUsageMap_usageTimeExceed_returnsExpectedResult()282 public void getIndexedUsageMap_usageTimeExceed_returnsExpectedResult() { 283 final int timeSlotSize = 1; 284 final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L}; 285 final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = 286 new HashMap<>(); 287 final BatteryHistEntry fakeEntry = createBatteryHistEntry( 288 ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", 0, 0L, 0L, 0L); 289 // Adds the index = 0 data. 290 Map<String, BatteryHistEntry> entryMap = new HashMap<>(); 291 entryMap.put(fakeEntry.getKey(), fakeEntry); 292 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[0]), entryMap); 293 // Adds the index = 1 data. 294 entryMap = new HashMap<>(); 295 entryMap.put(fakeEntry.getKey(), fakeEntry); 296 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[1]), entryMap); 297 // Adds the index = 2 data. 298 entryMap = new HashMap<>(); 299 final BatteryHistEntry entry = createBatteryHistEntry( 300 "package3", "label3", 500, 5L, 3600000L, 7200000L); 301 entryMap.put(entry.getKey(), entry); 302 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[2]), entryMap); 303 304 final Map<Integer, List<BatteryDiffEntry>> purgedResultMap = 305 ConvertUtils.getIndexedUsageMap( 306 mContext, timeSlotSize, batteryHistoryKeys, batteryHistoryMap, 307 /*purgeLowPercentageAndFakeData=*/ true); 308 309 assertThat(purgedResultMap).hasSize(2); 310 final List<BatteryDiffEntry> entryList = purgedResultMap.get(0); 311 assertThat(entryList).hasSize(1); 312 // Verifies the clipped usage time. 313 final float ratio = (float) (7200) / (float) (3600 + 7200); 314 final BatteryDiffEntry resultEntry = entryList.get(0); 315 assertThat(resultEntry.mForegroundUsageTimeInMs) 316 .isEqualTo(Math.round(entry.mForegroundUsageTimeInMs * ratio)); 317 assertThat(resultEntry.mBackgroundUsageTimeInMs) 318 .isEqualTo(Math.round(entry.mBackgroundUsageTimeInMs * ratio)); 319 assertThat(resultEntry.mConsumePower) 320 .isEqualTo(entry.mConsumePower * ratio); 321 } 322 323 @Test getIndexedUsageMap_hideBackgroundUsageTime_returnsExpectedResult()324 public void getIndexedUsageMap_hideBackgroundUsageTime_returnsExpectedResult() { 325 final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L}; 326 final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>(); 327 final BatteryHistEntry fakeEntry = createBatteryHistEntry( 328 ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", 0, 0L, 0L, 0L); 329 // Adds the index = 0 data. 330 Map<String, BatteryHistEntry> entryMap = new HashMap<>(); 331 entryMap.put(fakeEntry.getKey(), fakeEntry); 332 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[0]), entryMap); 333 // Adds the index = 1 data. 334 entryMap = new HashMap<>(); 335 entryMap.put(fakeEntry.getKey(), fakeEntry); 336 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[1]), entryMap); 337 // Adds the index = 2 data. 338 entryMap = new HashMap<>(); 339 final BatteryHistEntry entry = createBatteryHistEntry( 340 "package3", "label3", 500, 5L, 3600000L, 7200000L); 341 entryMap.put(entry.getKey(), entry); 342 batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[2]), entryMap); 343 when(mPowerUsageFeatureProvider.getHideBackgroundUsageTimeSet(mContext)) 344 .thenReturn(new HashSet(Arrays.asList((CharSequence) "package3"))); 345 346 final Map<Integer, List<BatteryDiffEntry>> purgedResultMap = 347 ConvertUtils.getIndexedUsageMap( 348 mContext, /*timeSlotSize=*/ 1, batteryHistoryKeys, batteryHistoryMap, 349 /*purgeLowPercentageAndFakeData=*/ true); 350 351 final BatteryDiffEntry resultEntry = purgedResultMap.get(0).get(0); 352 assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(0); 353 } 354 355 @Test getLocale_nullContext_returnDefaultLocale()356 public void getLocale_nullContext_returnDefaultLocale() { 357 assertThat(ConvertUtils.getLocale(/*context=*/ null)) 358 .isEqualTo(Locale.getDefault()); 359 } 360 361 @Test getLocale_nullLocaleList_returnDefaultLocale()362 public void getLocale_nullLocaleList_returnDefaultLocale() { 363 mContext.getResources().getConfiguration().setLocales(null); 364 assertThat(ConvertUtils.getLocale(mContext)).isEqualTo(Locale.getDefault()); 365 } 366 367 @Test getLocale_emptyLocaleList_returnDefaultLocale()368 public void getLocale_emptyLocaleList_returnDefaultLocale() { 369 mContext.getResources().getConfiguration().setLocales(new LocaleList()); 370 assertThat(ConvertUtils.getLocale(mContext)).isEqualTo(Locale.getDefault()); 371 } 372 createBatteryHistEntry( String packageName, String appLabel, double consumePower, long uid, long foregroundUsageTimeInMs, long backgroundUsageTimeInMs)373 private static BatteryHistEntry createBatteryHistEntry( 374 String packageName, String appLabel, double consumePower, 375 long uid, long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) { 376 // Only insert required fields. 377 final ContentValues values = new ContentValues(); 378 values.put(BatteryHistEntry.KEY_PACKAGE_NAME, packageName); 379 values.put(BatteryHistEntry.KEY_APP_LABEL, appLabel); 380 values.put(BatteryHistEntry.KEY_UID, Long.valueOf(uid)); 381 values.put(BatteryHistEntry.KEY_CONSUMER_TYPE, 382 Integer.valueOf(ConvertUtils.CONSUMER_TYPE_UID_BATTERY)); 383 values.put(BatteryHistEntry.KEY_CONSUME_POWER, consumePower); 384 values.put(BatteryHistEntry.KEY_FOREGROUND_USAGE_TIME, 385 Long.valueOf(foregroundUsageTimeInMs)); 386 values.put(BatteryHistEntry.KEY_BACKGROUND_USAGE_TIME, 387 Long.valueOf(backgroundUsageTimeInMs)); 388 return new BatteryHistEntry(values); 389 } 390 assertBatteryDiffEntry( BatteryDiffEntry entry, int percentOfTotal, long foregroundUsageTimeInMs, long backgroundUsageTimeInMs)391 private static void assertBatteryDiffEntry( 392 BatteryDiffEntry entry, int percentOfTotal, 393 long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) { 394 assertThat((int) entry.getPercentOfTotal()).isEqualTo(percentOfTotal); 395 assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs); 396 assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs); 397 } 398 } 399