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.ArgumentMatchers.any; 21 import static org.mockito.ArgumentMatchers.eq; 22 import static org.mockito.Matchers.anyInt; 23 import static org.mockito.Mockito.doReturn; 24 import static org.mockito.Mockito.spy; 25 26 import android.content.ContentValues; 27 import android.content.Context; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.PackageInfo; 30 import android.content.pm.PackageManager; 31 import android.graphics.drawable.Drawable; 32 import android.graphics.drawable.Drawable.ConstantState; 33 import android.os.BatteryConsumer; 34 import android.os.UserHandle; 35 import android.os.UserManager; 36 37 import com.android.settings.R; 38 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.mockito.Mock; 43 import org.mockito.MockitoAnnotations; 44 import org.robolectric.RobolectricTestRunner; 45 import org.robolectric.RuntimeEnvironment; 46 import org.robolectric.annotation.Config; 47 import org.robolectric.annotation.Implementation; 48 import org.robolectric.annotation.Implements; 49 import org.robolectric.annotation.Resetter; 50 51 import java.util.ArrayList; 52 import java.util.Collections; 53 import java.util.List; 54 import java.util.Locale; 55 56 @RunWith(RobolectricTestRunner.class) 57 @Config(shadows = {BatteryDiffEntryTest.ShadowUserHandle.class}) 58 public final class BatteryDiffEntryTest { 59 60 private Context mContext; 61 62 @Mock private ApplicationInfo mockAppInfo; 63 @Mock private PackageManager mockPackageManager; 64 @Mock private UserManager mockUserManager; 65 @Mock private Drawable mockDrawable; 66 @Mock private Drawable mockDrawable2; 67 @Mock private Drawable mockBadgedDrawable; 68 @Mock private BatteryHistEntry mBatteryHistEntry; 69 @Mock private PackageInfo mockPackageInfo; 70 @Mock private ConstantState mockConstantState; 71 72 @Before setUp()73 public void setUp() { 74 MockitoAnnotations.initMocks(this); 75 ShadowUserHandle.reset(); 76 mContext = spy(RuntimeEnvironment.application); 77 doReturn(mContext).when(mContext).getApplicationContext(); 78 doReturn(mockUserManager).when(mContext).getSystemService(UserManager.class); 79 doReturn(mockPackageManager).when(mContext).getPackageManager(); 80 BatteryDiffEntry.clearCache(); 81 } 82 83 @Test testSetTotalConsumePower_returnExpectedResult()84 public void testSetTotalConsumePower_returnExpectedResult() { 85 final BatteryDiffEntry entry = 86 new BatteryDiffEntry( 87 mContext, 88 /*foregroundUsageTimeInMs=*/ 10001L, 89 /*backgroundUsageTimeInMs=*/ 20002L, 90 /*consumePower=*/ 22.0, 91 /*batteryHistEntry=*/ null); 92 entry.setTotalConsumePower(100.0); 93 94 assertThat(entry.getPercentOfTotal()).isEqualTo(22.0); 95 } 96 97 @Test testSetTotalConsumePower_setZeroValue_returnsZeroValue()98 public void testSetTotalConsumePower_setZeroValue_returnsZeroValue() { 99 final BatteryDiffEntry entry = 100 new BatteryDiffEntry( 101 mContext, 102 /*foregroundUsageTimeInMs=*/ 10001L, 103 /*backgroundUsageTimeInMs=*/ 20002L, 104 /*consumePower=*/ 22.0, 105 /*batteryHistEntry=*/ null); 106 entry.setTotalConsumePower(0); 107 108 assertThat(entry.getPercentOfTotal()).isEqualTo(0); 109 } 110 111 @Test testComparator_sortCollectionsInDescOrder()112 public void testComparator_sortCollectionsInDescOrder() { 113 final List<BatteryDiffEntry> entryList = new ArrayList<>(); 114 // Generates fake testing data. 115 entryList.add(createBatteryDiffEntry(30, mBatteryHistEntry)); 116 entryList.add(createBatteryDiffEntry(20, mBatteryHistEntry)); 117 entryList.add(createBatteryDiffEntry(10, mBatteryHistEntry)); 118 Collections.sort(entryList, BatteryDiffEntry.COMPARATOR); 119 120 assertThat(entryList.get(0).getPercentOfTotal()).isEqualTo(30); 121 assertThat(entryList.get(1).getPercentOfTotal()).isEqualTo(20); 122 assertThat(entryList.get(2).getPercentOfTotal()).isEqualTo(10); 123 } 124 125 @Test testLoadLabelAndIcon_forSystemBattery_returnExpectedResult()126 public void testLoadLabelAndIcon_forSystemBattery_returnExpectedResult() { 127 final String expectedName = "Ambient display"; 128 // Generates fake testing data. 129 final ContentValues values = getContentValuesWithType( 130 ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY); 131 values.put("drainType", 132 Integer.valueOf(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)); 133 final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values); 134 135 final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry); 136 137 assertThat(entry.getAppLabel()).isEqualTo(expectedName); 138 assertThat(entry.getAppIconId()).isEqualTo(R.drawable.ic_settings_aod); 139 assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); 140 // Verifies the app label in the cache. 141 final BatteryEntry.NameAndIcon nameAndIcon = 142 BatteryDiffEntry.sResourceCache.get(entry.getKey()); 143 assertThat(nameAndIcon.name).isEqualTo(expectedName); 144 assertThat(nameAndIcon.iconId).isEqualTo(R.drawable.ic_settings_aod); 145 // Verifies the restrictable flag in the cache. 146 assertThat(entry.mValidForRestriction).isTrue(); 147 assertThat(BatteryDiffEntry.sValidForRestriction.get(entry.getKey())).isTrue(); 148 } 149 150 @Test testLoadLabelAndIcon_forUserBattery_returnExpectedResult()151 public void testLoadLabelAndIcon_forUserBattery_returnExpectedResult() { 152 final String expectedName = "Removed user"; 153 doReturn(null).when(mockUserManager).getUserInfo(1001); 154 // Generates fake testing data. 155 final ContentValues values = getContentValuesWithType( 156 ConvertUtils.CONSUMER_TYPE_USER_BATTERY); 157 values.put("userId", Integer.valueOf(1001)); 158 final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values); 159 160 final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry); 161 162 assertThat(entry.getAppLabel()).isEqualTo(expectedName); 163 assertThat(entry.getAppIcon()).isNull(); 164 assertThat(entry.getAppIconId()).isEqualTo(0); 165 assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); 166 // Verifies the app label in the cache. 167 final BatteryEntry.NameAndIcon nameAndIcon = 168 BatteryDiffEntry.sResourceCache.get(entry.getKey()); 169 assertThat(nameAndIcon.name).isEqualTo(expectedName); 170 assertThat(nameAndIcon.iconId).isEqualTo(0); 171 // Verifies the restrictable flag in the cache. 172 assertThat(entry.mValidForRestriction).isTrue(); 173 assertThat(BatteryDiffEntry.sValidForRestriction.get(entry.getKey())).isTrue(); 174 } 175 176 @Test testGetAppLabel_loadDataFromApplicationInfo()177 public void testGetAppLabel_loadDataFromApplicationInfo() throws Exception { 178 final String expectedAppLabel = "fake app label"; 179 final String fakePackageName = "com.fake.google.com"; 180 final ContentValues values = getContentValuesWithType( 181 ConvertUtils.CONSUMER_TYPE_UID_BATTERY); 182 values.put("uid", /*invalid uid*/ 10001); 183 values.put("packageName", fakePackageName); 184 doReturn(mockAppInfo).when(mockPackageManager) 185 .getApplicationInfo(fakePackageName, 0); 186 doReturn(expectedAppLabel).when(mockPackageManager) 187 .getApplicationLabel(mockAppInfo); 188 final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values); 189 190 final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry); 191 192 assertThat(entry.getAppLabel()).isEqualTo(expectedAppLabel); 193 assertThat(entry.getAppIconId()).isEqualTo(0); 194 assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); 195 // Verifies the app label in the cache. 196 final BatteryEntry.NameAndIcon nameAndIcon = 197 BatteryDiffEntry.sResourceCache.get(entry.getKey()); 198 assertThat(nameAndIcon.name).isEqualTo(expectedAppLabel); 199 // Verifies the restrictable flag in the cache. 200 assertThat(entry.mValidForRestriction).isFalse(); 201 assertThat(BatteryDiffEntry.sValidForRestriction.get(entry.getKey())).isFalse(); 202 } 203 204 @Test testGetAppLabel_loadDataFromPreDefinedNameAndUid()205 public void testGetAppLabel_loadDataFromPreDefinedNameAndUid() { 206 final String expectedAppLabel = "Android OS"; 207 final ContentValues values = getContentValuesWithType( 208 ConvertUtils.CONSUMER_TYPE_UID_BATTERY); 209 final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values); 210 211 final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry); 212 213 assertThat(entry.getAppLabel()).isEqualTo(expectedAppLabel); 214 assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); 215 // Verifies the app label in the cache. 216 final BatteryEntry.NameAndIcon nameAndIcon = 217 BatteryDiffEntry.sResourceCache.get(entry.getKey()); 218 assertThat(nameAndIcon.name).isEqualTo(expectedAppLabel); 219 } 220 221 @Test testGetAppLabel_nullAppLabel_returnAppLabelInBatteryHistEntry()222 public void testGetAppLabel_nullAppLabel_returnAppLabelInBatteryHistEntry() { 223 final String expectedAppLabel = "fake app label"; 224 final ContentValues values = getContentValuesWithType( 225 ConvertUtils.CONSUMER_TYPE_UID_BATTERY); 226 values.put("appLabel", expectedAppLabel); 227 final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values); 228 229 final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry); 230 231 entry.mIsLoaded = true; 232 assertThat(entry.getAppLabel()).isEqualTo(expectedAppLabel); 233 assertThat(BatteryDiffEntry.sResourceCache).isEmpty(); 234 } 235 236 @Test testGetAppIcon_nonUidConsumer_returnAppIconInBatteryDiffEntry()237 public void testGetAppIcon_nonUidConsumer_returnAppIconInBatteryDiffEntry() { 238 final ContentValues values = getContentValuesWithType( 239 ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY); 240 final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values); 241 mockConstantState(mockDrawable); 242 243 final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry); 244 245 entry.mIsLoaded = true; 246 entry.mAppIcon = mockDrawable; 247 assertThat(entry.getAppIcon()).isEqualTo(mockDrawable); 248 assertThat(BatteryDiffEntry.sResourceCache).isEmpty(); 249 } 250 251 @Test testGetAppIcon_uidConsumerForNonOwner_returnDefaultActivityIconWithBadge()252 public void testGetAppIcon_uidConsumerForNonOwner_returnDefaultActivityIconWithBadge() 253 throws Exception { 254 ShadowUserHandle.setUid(10); 255 final BatteryDiffEntry entry = createBatteryDiffEntry(mockDrawable); 256 mockConstantState(mockDrawable); 257 mockConstantState(mockBadgedDrawable); 258 doReturn(mockBadgedDrawable).when(mockUserManager) 259 .getBadgedIconForUser(eq(mockDrawable), any()); 260 261 entry.mAppIcon = null; 262 assertThat(entry.getAppIcon()).isEqualTo(mockBadgedDrawable); 263 } 264 265 @Test testGetAppIcon_uidConsumerWithNullIcon_returnDefaultActivityIcon()266 public void testGetAppIcon_uidConsumerWithNullIcon_returnDefaultActivityIcon() 267 throws Exception { 268 final BatteryDiffEntry entry = createBatteryDiffEntry(mockDrawable); 269 mockConstantState(mockDrawable); 270 271 entry.mAppIcon = null; 272 assertThat(entry.getAppIcon()).isEqualTo(mockDrawable); 273 assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); 274 // Verifies the app label in the cache. 275 final BatteryEntry.NameAndIcon nameAndIcon = 276 BatteryDiffEntry.sResourceCache.get(entry.getKey()); 277 assertThat(nameAndIcon.icon).isEqualTo(mockDrawable); 278 } 279 280 @Test testClearCache_clearDataForResourcesAndFlags()281 public void testClearCache_clearDataForResourcesAndFlags() { 282 BatteryDiffEntry.sResourceCache.put( 283 "fake application key", 284 new BatteryEntry.NameAndIcon("app label", null, /*iconId=*/ 0)); 285 BatteryDiffEntry.sValidForRestriction.put( 286 "fake application key", Boolean.valueOf(false)); 287 288 BatteryDiffEntry.clearCache(); 289 290 assertThat(BatteryDiffEntry.sResourceCache).isEmpty(); 291 assertThat(BatteryDiffEntry.sValidForRestriction).isEmpty(); 292 } 293 294 @Test testClearCache_switchLocale_clearCacheIconAndLabel()295 public void testClearCache_switchLocale_clearCacheIconAndLabel() throws Exception { 296 final int userId = UserHandle.getUserId(1001); 297 Locale.setDefault(new Locale("en_US")); 298 final BatteryDiffEntry entry1 = createBatteryDiffEntry(mockDrawable); 299 mockConstantState(mockDrawable); 300 assertThat(entry1.getAppIcon()).isEqualTo(mockDrawable); 301 // Switch the locale into another one. 302 Locale.setDefault(new Locale("zh_TW")); 303 304 final BatteryDiffEntry entry2 = createBatteryDiffEntry(mockDrawable2); 305 306 // We should get new drawable without caching. 307 mockConstantState(mockDrawable2); 308 assertThat(entry2.getAppIcon()).isEqualTo(mockDrawable2); 309 // Verifies the cache is updated into the new drawable. 310 final BatteryEntry.NameAndIcon nameAndIcon = 311 BatteryDiffEntry.sResourceCache.get(entry2.getKey()); 312 assertThat(nameAndIcon.icon).isEqualTo(mockDrawable2); 313 } 314 315 @Test testIsSystemEntry_userBattery_returnTrue()316 public void testIsSystemEntry_userBattery_returnTrue() { 317 final BatteryDiffEntry entry = 318 createBatteryDiffEntry( 319 ConvertUtils.CONSUMER_TYPE_USER_BATTERY, 320 /*uid=*/ 0, /*isHidden=*/ false); 321 assertThat(entry.isSystemEntry()).isTrue(); 322 } 323 324 @Test testIsSystemEntry_systemBattery_returnTrue()325 public void testIsSystemEntry_systemBattery_returnTrue() { 326 final BatteryDiffEntry entry = 327 createBatteryDiffEntry( 328 ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, 329 /*uid=*/ 0, /*isHidden=*/ false); 330 assertThat(entry.isSystemEntry()).isTrue(); 331 } 332 333 @Test testIsSystemEntry_uidBattery_returnFalse()334 public void testIsSystemEntry_uidBattery_returnFalse() { 335 final BatteryDiffEntry entry = 336 createBatteryDiffEntry( 337 ConvertUtils.CONSUMER_TYPE_UID_BATTERY, 338 /*uid=*/ 123, /*isHidden=*/ false); 339 assertThat(entry.isSystemEntry()).isFalse(); 340 } 341 342 @Test testIsSystemEntry_uidBatteryWithHiddenState_returnTrue()343 public void testIsSystemEntry_uidBatteryWithHiddenState_returnTrue() { 344 final BatteryDiffEntry entry = 345 createBatteryDiffEntry( 346 ConvertUtils.CONSUMER_TYPE_UID_BATTERY, 347 /*uid=*/ 123, /*isHidden=*/ true); 348 assertThat(entry.isSystemEntry()).isTrue(); 349 } 350 351 @Test testIsSystemEntry_uidBatteryWithSystemProcess_returnTrue()352 public void testIsSystemEntry_uidBatteryWithSystemProcess_returnTrue() { 353 final BatteryDiffEntry entry = 354 createBatteryDiffEntry( 355 ConvertUtils.CONSUMER_TYPE_UID_BATTERY, 356 /*uid=*/ 1230, /*isHidden=*/ false); 357 assertThat(entry.isSystemEntry()).isTrue(); 358 } 359 360 @Test testUpdateRestrictionFlagState_updateFlagAsExpected()361 public void testUpdateRestrictionFlagState_updateFlagAsExpected() throws Exception { 362 final String expectedAppLabel = "fake app label"; 363 final String fakePackageName = "com.fake.google.com"; 364 final ContentValues values = getContentValuesWithType( 365 ConvertUtils.CONSUMER_TYPE_UID_BATTERY); 366 values.put("uid", /*invalid uid*/ 10001); 367 values.put("packageName", fakePackageName); 368 final BatteryDiffEntry entry = 369 createBatteryDiffEntry(10, new BatteryHistEntry(values)); 370 371 entry.updateRestrictionFlagState(); 372 // Sets false if the app entry cannot be found. 373 assertThat(entry.mValidForRestriction).isFalse(); 374 375 doReturn(BatteryUtils.UID_NULL).when(mockPackageManager).getPackageUid( 376 entry.getPackageName(), PackageManager.GET_META_DATA); 377 entry.updateRestrictionFlagState(); 378 // Sets false if the app is invalid package name. 379 assertThat(entry.mValidForRestriction).isFalse(); 380 381 doReturn(1000).when(mockPackageManager).getPackageUid( 382 entry.getPackageName(), PackageManager.GET_META_DATA); 383 entry.updateRestrictionFlagState(); 384 // Sets false if the app PackageInfo cannot be found. 385 assertThat(entry.mValidForRestriction).isFalse(); 386 387 doReturn(mockPackageInfo).when(mockPackageManager).getPackageInfo( 388 eq(entry.getPackageName()), anyInt()); 389 entry.updateRestrictionFlagState(); 390 // Sets true if package is valid and PackageInfo can be found. 391 assertThat(entry.mValidForRestriction).isTrue(); 392 } 393 394 @Test testGetPackageName_returnExpectedResult()395 public void testGetPackageName_returnExpectedResult() { 396 final String expectedPackageName = "com.fake.google.com"; 397 final ContentValues values = getContentValuesWithType( 398 ConvertUtils.CONSUMER_TYPE_UID_BATTERY); 399 values.put("packageName", expectedPackageName); 400 final BatteryDiffEntry entry = 401 createBatteryDiffEntry(10, new BatteryHistEntry(values)); 402 403 assertThat(entry.getPackageName()).isEqualTo(expectedPackageName); 404 } 405 406 @Test testGetPackageName_withProcessName_returnExpectedResult()407 public void testGetPackageName_withProcessName_returnExpectedResult() { 408 final String expectedPackageName = "com.fake.google.com"; 409 final ContentValues values = getContentValuesWithType( 410 ConvertUtils.CONSUMER_TYPE_UID_BATTERY); 411 values.put( 412 "packageName", 413 expectedPackageName + ":privileged_process0"); 414 final BatteryDiffEntry entry = 415 createBatteryDiffEntry(10, new BatteryHistEntry(values)); 416 417 assertThat(entry.getPackageName()).isEqualTo(expectedPackageName); 418 } 419 createBatteryDiffEntry( int consumerType, long uid, boolean isHidden)420 private BatteryDiffEntry createBatteryDiffEntry( 421 int consumerType, long uid, boolean isHidden) { 422 final ContentValues values = getContentValuesWithType(consumerType); 423 values.put("isHidden", isHidden); 424 values.put("uid", uid); 425 return new BatteryDiffEntry( 426 mContext, 427 /*foregroundUsageTimeInMs=*/ 0, 428 /*backgroundUsageTimeInMs=*/ 0, 429 /*consumePower=*/ 0, 430 new BatteryHistEntry(values)); 431 } 432 createBatteryDiffEntry( double consumePower, BatteryHistEntry batteryHistEntry)433 private BatteryDiffEntry createBatteryDiffEntry( 434 double consumePower, BatteryHistEntry batteryHistEntry) { 435 final BatteryDiffEntry entry = new BatteryDiffEntry( 436 mContext, 437 /*foregroundUsageTimeInMs=*/ 0, 438 /*backgroundUsageTimeInMs=*/ 0, 439 consumePower, 440 batteryHistEntry); 441 entry.setTotalConsumePower(100.0); 442 return entry; 443 } 444 getContentValuesWithType(int consumerType)445 private static ContentValues getContentValuesWithType(int consumerType) { 446 final ContentValues values = new ContentValues(); 447 values.put("consumerType", Integer.valueOf(consumerType)); 448 return values; 449 } 450 createBatteryDiffEntry(Drawable drawable)451 private BatteryDiffEntry createBatteryDiffEntry(Drawable drawable) throws Exception { 452 final ContentValues values = getContentValuesWithType( 453 ConvertUtils.CONSUMER_TYPE_UID_BATTERY); 454 values.put("uid", 1001); 455 values.put("packageName", "com.a.b.c"); 456 final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values); 457 doReturn(drawable).when(mockPackageManager).getDefaultActivityIcon(); 458 doReturn(null).when(mockPackageManager).getApplicationInfo("com.a.b.c", 0); 459 doReturn(new String[] {"com.a.b.c"}).when(mockPackageManager) 460 .getPackagesForUid(1001); 461 return createBatteryDiffEntry(10, batteryHistEntry); 462 } 463 mockConstantState(Drawable drawable)464 private void mockConstantState(Drawable drawable) { 465 doReturn(mockConstantState).when(drawable).getConstantState(); 466 doReturn(drawable).when(mockConstantState).newDrawable(); 467 } 468 469 @Implements(UserHandle.class) 470 public static class ShadowUserHandle { 471 // Sets the default as thte OWNER role. 472 private static int sUid = 0; 473 setUid(int uid)474 public static void setUid(int uid) { 475 sUid = uid; 476 } 477 478 @Implementation myUserId()479 public static int myUserId() { 480 return sUid; 481 } 482 483 @Implementation getUserId(int userId)484 public static int getUserId(int userId) { 485 return sUid; 486 } 487 488 @Resetter reset()489 public static void reset() { 490 sUid = 0; 491 } 492 } 493 } 494