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 17 package com.android.settings.fuelgauge; 18 19 import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.ArgumentMatchers.anyString; 26 import static org.mockito.ArgumentMatchers.nullable; 27 import static org.mockito.Mockito.doAnswer; 28 import static org.mockito.Mockito.doNothing; 29 import static org.mockito.Mockito.doReturn; 30 import static org.mockito.Mockito.mock; 31 import static org.mockito.Mockito.spy; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.verifyNoInteractions; 34 import static org.mockito.Mockito.when; 35 36 import android.app.AppOpsManager; 37 import android.app.settings.SettingsEnums; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.pm.ApplicationInfo; 41 import android.content.pm.InstallSourceInfo; 42 import android.content.pm.PackageManager; 43 import android.graphics.drawable.ColorDrawable; 44 import android.graphics.drawable.Drawable; 45 import android.os.Bundle; 46 import android.os.UserHandle; 47 48 import androidx.fragment.app.FragmentActivity; 49 import androidx.loader.app.LoaderManager; 50 import androidx.test.core.app.ApplicationProvider; 51 52 import com.android.settings.R; 53 import com.android.settings.SettingsActivity; 54 import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; 55 import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry; 56 import com.android.settings.fuelgauge.batteryusage.BatteryEntry; 57 import com.android.settings.fuelgauge.batteryusage.ConvertUtils; 58 import com.android.settings.testutils.FakeFeatureFactory; 59 import com.android.settings.testutils.shadow.ShadowEntityHeaderController; 60 import com.android.settingslib.Utils; 61 import com.android.settingslib.applications.AppUtils; 62 import com.android.settingslib.applications.ApplicationsState; 63 import com.android.settingslib.applications.instantapps.InstantAppDataProvider; 64 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 65 import com.android.settingslib.widget.IntroPreference; 66 67 import org.junit.After; 68 import org.junit.Before; 69 import org.junit.Rule; 70 import org.junit.Test; 71 import org.junit.runner.RunWith; 72 import org.mockito.Answers; 73 import org.mockito.ArgumentCaptor; 74 import org.mockito.Mock; 75 import org.mockito.junit.MockitoJUnit; 76 import org.mockito.junit.MockitoRule; 77 import org.mockito.stubbing.Answer; 78 import org.robolectric.RobolectricTestRunner; 79 import org.robolectric.annotation.Config; 80 import org.robolectric.annotation.Implementation; 81 import org.robolectric.annotation.Implements; 82 import org.robolectric.util.ReflectionHelpers; 83 84 import java.util.concurrent.TimeUnit; 85 86 @RunWith(RobolectricTestRunner.class) 87 @Config( 88 shadows = { 89 com.android.settings.testutils.shadow.ShadowFragment.class, 90 }) 91 public class AdvancedPowerUsageDetailTest { 92 93 @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 94 95 private static final String APP_LABEL = "app label"; 96 private static final String APP_ENTRY_LABEL = "app entry label"; 97 private static final String SUMMARY = "summary"; 98 private static final String PACKAGE_NAME = "com.android.app"; 99 private static final String INITIATING_PACKAGE_NAME = "com.android.vending"; 100 private static final String USAGE_PERCENT = "16%"; 101 private static final int ICON_ID = 123; 102 private static final int UID = 1; 103 private static final long FOREGROUND_TIME_MS = 444; 104 private static final long FOREGROUND_SERVICE_TIME_MS = 123; 105 private static final long BACKGROUND_TIME_MS = 100; 106 private static final long SCREEN_ON_TIME_MS = 321; 107 private static final Drawable TEST_DRAWABLE = new ColorDrawable(0); 108 109 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 110 private FragmentActivity mActivity; 111 112 @Mock private ApplicationsState mState; 113 @Mock private ApplicationsState.AppEntry mAppEntry; 114 @Mock private BatteryEntry mBatteryEntry; 115 @Mock private PackageManager mPackageManager; 116 @Mock private InstallSourceInfo mInstallSourceInfo; 117 @Mock private AppOpsManager mAppOpsManager; 118 @Mock private LoaderManager mLoaderManager; 119 120 private int mTestMode; 121 private Context mContext; 122 private AdvancedPowerUsageDetail mFragment; 123 private SettingsActivity mTestActivity; 124 private FakeFeatureFactory mFeatureFactory; 125 private MetricsFeatureProvider mMetricsFeatureProvider; 126 private BatteryDiffEntry mBatteryDiffEntry; 127 private Bundle mBundle; 128 private BatteryOptimizeUtils mBatteryOptimizeUtils; 129 private IntroPreference mIntroPreference; 130 131 @Implements(Utils.class) 132 private static class ShadowUtils { 133 @Implementation getBadgedIcon(Context context, ApplicationInfo appInfo)134 public static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) { 135 return AdvancedPowerUsageDetailTest.TEST_DRAWABLE; 136 } 137 } 138 139 @Before setUp()140 public void setUp() throws Exception { 141 mContext = spy(ApplicationProvider.getApplicationContext()); 142 when(mContext.getPackageName()).thenReturn(PACKAGE_NAME); 143 when(mContext.getPackageManager()).thenReturn(mPackageManager); 144 when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); 145 mFeatureFactory = FakeFeatureFactory.setupForTest(); 146 mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; 147 148 prepareTestBatteryOptimizationUtils(); 149 mFragment = spy(new AdvancedPowerUsageDetail()); 150 mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; 151 mIntroPreference = new IntroPreference(mContext); 152 doReturn(mIntroPreference).when(mFragment).findPreference(any()); 153 mBundle = spy(new Bundle()); 154 doReturn(mContext).when(mFragment).getContext(); 155 doReturn(mActivity).when(mFragment).getActivity(); 156 doReturn(SUMMARY).when(mFragment).getString(anyInt()); 157 doReturn(APP_LABEL).when(mBundle).getString(nullable(String.class)); 158 when(mFragment.getArguments()).thenReturn(mBundle); 159 doReturn(mLoaderManager).when(mFragment).getLoaderManager(); 160 161 when(mBatteryEntry.getUid()).thenReturn(UID); 162 when(mBatteryEntry.getLabel()).thenReturn(APP_LABEL); 163 when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(FOREGROUND_TIME_MS); 164 when(mBatteryEntry.getTimeInForegroundServiceMs()).thenReturn(FOREGROUND_SERVICE_TIME_MS); 165 when(mBatteryEntry.getTimeInBackgroundMs()).thenReturn(BACKGROUND_TIME_MS); 166 mBatteryEntry.mIconId = ICON_ID; 167 168 mBatteryDiffEntry = 169 spy( 170 new BatteryDiffEntry( 171 mContext, 172 /* uid= */ UID, 173 /* userId= */ 0, 174 /* key= */ "key", 175 /* isHidden= */ false, 176 /* componentId= */ -1, 177 /* legacyPackageName= */ null, 178 /* legacyLabel= */ null, 179 /*consumerType*/ ConvertUtils.CONSUMER_TYPE_USER_BATTERY, 180 /* foregroundUsageTimeInMs= */ FOREGROUND_TIME_MS, 181 /* foregroundSerUsageTimeInMs= */ FOREGROUND_SERVICE_TIME_MS, 182 /* backgroundUsageTimeInMs= */ BACKGROUND_TIME_MS, 183 /* screenOnTimeInMs= */ SCREEN_ON_TIME_MS, 184 /* consumePower= */ 0, 185 /* foregroundUsageConsumePower= */ 0, 186 /* foregroundServiceUsageConsumePower= */ 0, 187 /* backgroundUsageConsumePower= */ 0, 188 /* cachedUsageConsumePower= */ 0)); 189 when(mBatteryDiffEntry.getAppLabel()).thenReturn(APP_LABEL); 190 when(mBatteryDiffEntry.getAppIconId()).thenReturn(ICON_ID); 191 192 mFragment.mState = mState; 193 mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; 194 mFragment.mLogStringBuilder = new StringBuilder(); 195 doNothing().when(mState).ensureIcon(mAppEntry); 196 mAppEntry.info = mock(ApplicationInfo.class); 197 mAppEntry.label = APP_ENTRY_LABEL; 198 199 mTestActivity = spy(new SettingsActivity()); 200 doReturn(mPackageManager).when(mTestActivity).getPackageManager(); 201 doReturn(mPackageManager).when(mActivity).getPackageManager(); 202 doReturn(mAppOpsManager).when(mTestActivity).getSystemService(Context.APP_OPS_SERVICE); 203 204 final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); 205 206 Answer<Void> callable = 207 invocation -> { 208 mBundle = captor.getValue().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); 209 System.out.println("mBundle = " + mBundle); 210 return null; 211 }; 212 doAnswer(callable) 213 .when(mActivity) 214 .startActivityAsUser(captor.capture(), nullable(UserHandle.class)); 215 doAnswer(callable).when(mActivity).startActivity(captor.capture()); 216 doAnswer(callable).when(mContext).startActivity(captor.capture()); 217 } 218 219 @After reset()220 public void reset() { 221 ShadowEntityHeaderController.reset(); 222 } 223 224 @Test setPreferenceScreenResId_returnNewLayout()225 public void setPreferenceScreenResId_returnNewLayout() { 226 assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(R.xml.power_usage_detail); 227 } 228 229 @Test 230 @Config(shadows = ShadowUtils.class) initHeader_NoAppEntry_BuildByBundle()231 public void initHeader_NoAppEntry_BuildByBundle() { 232 mFragment.mAppEntry = null; 233 mFragment.initHeader(); 234 235 assertThat(mIntroPreference.getIcon()).isNotEqualTo(TEST_DRAWABLE); 236 assertThat(mIntroPreference.getTitle()).isEqualTo(APP_LABEL); 237 } 238 239 @Test 240 @Config(shadows = ShadowUtils.class) initHeader_HasAppEntry_BuildByAppEntry()241 public void initHeader_HasAppEntry_BuildByAppEntry() { 242 mFragment.mAppEntry = mAppEntry; 243 mFragment.initHeader(); 244 245 assertThat(mIntroPreference.getIcon()).isEqualTo(TEST_DRAWABLE); 246 assertThat(mIntroPreference.getTitle()).isEqualTo(mAppEntry.label); 247 } 248 249 @Test 250 @Config(shadows = ShadowUtils.class) initHeader_HasAppEntry_InstantApp()251 public void initHeader_HasAppEntry_InstantApp() { 252 ReflectionHelpers.setStaticField( 253 AppUtils.class, 254 "sInstantAppDataProvider", 255 new InstantAppDataProvider() { 256 @Override 257 public boolean isInstantApp(ApplicationInfo info) { 258 return true; 259 } 260 }); 261 mFragment.mAppEntry = mAppEntry; 262 mFragment.initHeader(); 263 264 assertThat(mIntroPreference.getIcon()).isEqualTo(TEST_DRAWABLE); 265 assertThat(mIntroPreference.getTitle()).isEqualTo(mAppEntry.label); 266 } 267 268 @Test startBatteryDetailPage_invalidToShowSummary_noFGBDData()269 public void startBatteryDetailPage_invalidToShowSummary_noFGBDData() { 270 mBundle.clear(); 271 AdvancedPowerUsageDetail.startBatteryDetailPage( 272 mActivity, mFragment, mBatteryEntry, USAGE_PERCENT); 273 274 assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID); 275 assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME)).isEqualTo(0); 276 assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME)).isEqualTo(0); 277 assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_SCREEN_ON_TIME)).isEqualTo(0); 278 assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT)) 279 .isEqualTo(USAGE_PERCENT); 280 } 281 282 @Test startBatteryDetailPage_showSummary_hasFGBDData()283 public void startBatteryDetailPage_showSummary_hasFGBDData() { 284 final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); 285 mBundle.clear(); 286 AdvancedPowerUsageDetail.startBatteryDetailPage( 287 mContext, 288 mFragment.getMetricsCategory(), 289 mBatteryDiffEntry, 290 USAGE_PERCENT, 291 /* slotInformation= */ null, 292 /* showTimeInformation= */ true, 293 /* anomalyHintPrefKey= */ null, 294 /* anomalyHintText= */ null); 295 296 verify(mContext).startActivity(captor.capture()); 297 assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID); 298 assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME)) 299 .isEqualTo(BACKGROUND_TIME_MS + FOREGROUND_SERVICE_TIME_MS); 300 assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME)) 301 .isEqualTo(FOREGROUND_TIME_MS); 302 assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_SCREEN_ON_TIME)) 303 .isEqualTo(SCREEN_ON_TIME_MS); 304 assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT)) 305 .isEqualTo(USAGE_PERCENT); 306 assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_SLOT_TIME)).isNull(); 307 } 308 309 @Test startBatteryDetailPage_noBatteryUsage_hasBasicData()310 public void startBatteryDetailPage_noBatteryUsage_hasBasicData() { 311 final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); 312 313 AdvancedPowerUsageDetail.startBatteryDetailPage( 314 mActivity, mFragment, PACKAGE_NAME, UserHandle.OWNER); 315 316 verify(mActivity).startActivity(captor.capture()); 317 318 assertThat( 319 captor.getValue() 320 .getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS) 321 .getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME)) 322 .isEqualTo(PACKAGE_NAME); 323 324 assertThat( 325 captor.getValue() 326 .getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS) 327 .getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT)) 328 .isEqualTo("0%"); 329 } 330 331 @Test startBatteryDetailPage_batteryEntryNotExisted_extractUidFromPackageName()332 public void startBatteryDetailPage_batteryEntryNotExisted_extractUidFromPackageName() 333 throws PackageManager.NameNotFoundException { 334 mBundle.clear(); 335 doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME, 0 /* no flag */); 336 337 AdvancedPowerUsageDetail.startBatteryDetailPage( 338 mActivity, mFragment, PACKAGE_NAME, UserHandle.OWNER); 339 340 assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID); 341 } 342 343 @Test onPause_optimizationModeIsChanged_logPreference()344 public void onPause_optimizationModeIsChanged_logPreference() throws Exception { 345 mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_RESTRICTED; 346 when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); 347 when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); 348 349 mBatteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED, Action.APPLY); 350 mFragment.onPause(); 351 352 TimeUnit.SECONDS.sleep(1); 353 verify(mMetricsFeatureProvider) 354 .action( 355 SettingsEnums.LEAVE_APP_BATTERY_USAGE, 356 SettingsEnums.ACTION_APP_BATTERY_USAGE_ALLOW_BACKGROUND, 357 SettingsEnums.FUELGAUGE_POWER_USAGE_DETAIL, 358 PACKAGE_NAME, 359 /* consumed battery */ 0); 360 } 361 362 @Test onPause_optimizationModeIsNotChanged_notInvokeLogging()363 public void onPause_optimizationModeIsNotChanged_notInvokeLogging() throws Exception { 364 mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_RESTRICTED; 365 when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); 366 when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); 367 368 mBatteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED, Action.APPLY); 369 mBatteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_RESTRICTED, Action.APPLY); 370 mFragment.onPause(); 371 372 TimeUnit.SECONDS.sleep(1); 373 verifyNoInteractions(mMetricsFeatureProvider); 374 } 375 376 @Test shouldSkipForInitialSUW_returnTrue()377 public void shouldSkipForInitialSUW_returnTrue() { 378 assertThat(mFragment.shouldSkipForInitialSUW()).isTrue(); 379 } 380 prepareTestBatteryOptimizationUtils()381 private void prepareTestBatteryOptimizationUtils() { 382 mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); 383 Answer<Void> setTestMode = 384 invocation -> { 385 mTestMode = invocation.getArgument(0); 386 return null; 387 }; 388 doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); 389 Answer<Integer> getTestMode = invocation -> mTestMode; 390 doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); 391 } 392 } 393