1 /* 2 * Copyright (C) 2023 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.batteryusage.BatteryEntry; 55 import com.android.settings.testutils.FakeFeatureFactory; 56 import com.android.settings.testutils.shadow.ShadowHelpUtils; 57 import com.android.settingslib.Utils; 58 import com.android.settingslib.applications.ApplicationsState; 59 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 60 import com.android.settingslib.widget.FooterPreference; 61 import com.android.settingslib.widget.IntroPreference; 62 63 import org.junit.After; 64 import org.junit.Before; 65 import org.junit.Rule; 66 import org.junit.Test; 67 import org.junit.runner.RunWith; 68 import org.mockito.Answers; 69 import org.mockito.ArgumentCaptor; 70 import org.mockito.Mock; 71 import org.mockito.junit.MockitoJUnit; 72 import org.mockito.junit.MockitoRule; 73 import org.mockito.stubbing.Answer; 74 import org.robolectric.RobolectricTestRunner; 75 import org.robolectric.annotation.Config; 76 import org.robolectric.annotation.Implementation; 77 import org.robolectric.annotation.Implements; 78 79 import java.util.concurrent.TimeUnit; 80 81 @RunWith(RobolectricTestRunner.class) 82 @Config( 83 shadows = { 84 ShadowHelpUtils.class, 85 com.android.settings.testutils.shadow.ShadowFragment.class, 86 }) 87 public class PowerBackgroundUsageDetailTest { 88 89 @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 90 91 private static final String APP_LABEL = "app label"; 92 private static final String APP_ENTRY_LABEL = "app entry label"; 93 private static final String SUMMARY = "summary"; 94 private static final int ICON_ID = 123; 95 private static final int UID = 1; 96 private static final String PACKAGE_NAME = "com.android.app"; 97 private static final String KEY_PREF_HEADER = "header_view"; 98 private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference"; 99 private static final String INITIATING_PACKAGE_NAME = "com.android.vending"; 100 private static final Drawable TEST_DRAWABLE = new ColorDrawable(0); 101 102 private int mTestMode; 103 private Context mContext; 104 private PowerBackgroundUsageDetail mFragment; 105 private MetricsFeatureProvider mMetricsFeatureProvider; 106 private SettingsActivity mTestActivity; 107 private BatteryOptimizeUtils mBatteryOptimizeUtils; 108 private IntroPreference mIntroPreference; 109 110 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 111 private FragmentActivity mActivity; 112 113 @Mock private ApplicationsState mState; 114 @Mock private Bundle mBundle; 115 @Mock private LoaderManager mLoaderManager; 116 @Mock private ApplicationsState.AppEntry mAppEntry; 117 @Mock private BatteryEntry mBatteryEntry; 118 @Mock private PackageManager mPackageManager; 119 @Mock private AppOpsManager mAppOpsManager; 120 @Mock private InstallSourceInfo mInstallSourceInfo; 121 @Mock private FooterPreference mFooterPreference; 122 123 @Implements(Utils.class) 124 private static class ShadowUtils { 125 @Implementation getBadgedIcon(Context context, ApplicationInfo appInfo)126 public static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) { 127 return PowerBackgroundUsageDetailTest.TEST_DRAWABLE; 128 } 129 } 130 131 @Before setUp()132 public void setUp() throws Exception { 133 mContext = spy(ApplicationProvider.getApplicationContext()); 134 when(mContext.getPackageName()).thenReturn(PACKAGE_NAME); 135 when(mContext.getPackageManager()).thenReturn(mPackageManager); 136 when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); 137 138 final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest(); 139 mMetricsFeatureProvider = fakeFeatureFactory.metricsFeatureProvider; 140 141 prepareTestBatteryOptimizationUtils(); 142 mFragment = spy(new PowerBackgroundUsageDetail()); 143 mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; 144 mFragment.mLogStringBuilder = new StringBuilder(); 145 mIntroPreference = new IntroPreference(mContext); 146 doReturn(mIntroPreference).when(mFragment).findPreference(KEY_PREF_HEADER); 147 doReturn(mFooterPreference).when(mFragment).findPreference(KEY_FOOTER_PREFERENCE); 148 doReturn(mContext).when(mFragment).getContext(); 149 doReturn(mActivity).when(mFragment).getActivity(); 150 doReturn(SUMMARY).when(mFragment).getString(anyInt()); 151 doReturn(APP_LABEL).when(mBundle).getString(nullable(String.class)); 152 when(mFragment.getArguments()).thenReturn(mBundle); 153 doReturn(mLoaderManager).when(mFragment).getLoaderManager(); 154 155 when(mBatteryEntry.getUid()).thenReturn(UID); 156 when(mBatteryEntry.getLabel()).thenReturn(APP_LABEL); 157 mBatteryEntry.mIconId = ICON_ID; 158 159 mFragment.mState = mState; 160 doNothing().when(mState).ensureIcon(mAppEntry); 161 mAppEntry.info = mock(ApplicationInfo.class); 162 mAppEntry.label = APP_ENTRY_LABEL; 163 164 mTestActivity = spy(new SettingsActivity()); 165 doReturn(mPackageManager).when(mTestActivity).getPackageManager(); 166 doReturn(mPackageManager).when(mActivity).getPackageManager(); 167 doReturn(mAppOpsManager).when(mTestActivity).getSystemService(Context.APP_OPS_SERVICE); 168 169 final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); 170 171 Answer<Void> callable = 172 invocation -> { 173 mBundle = captor.getValue().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); 174 System.out.println("mBundle = " + mBundle); 175 return null; 176 }; 177 doAnswer(callable) 178 .when(mActivity) 179 .startActivityAsUser(captor.capture(), nullable(UserHandle.class)); 180 doAnswer(callable).when(mActivity).startActivity(captor.capture()); 181 } 182 183 @After reset()184 public void reset() { 185 ShadowHelpUtils.reset(); 186 } 187 188 @Test 189 @Config(shadows = ShadowUtils.class) initHeader_NoAppEntry_BuildByBundle()190 public void initHeader_NoAppEntry_BuildByBundle() { 191 mFragment.mAppEntry = null; 192 mFragment.initHeader(); 193 194 assertThat(mIntroPreference.getIcon()).isNotEqualTo(TEST_DRAWABLE); 195 assertThat(mIntroPreference.getTitle()).isEqualTo(APP_LABEL); 196 } 197 198 @Test 199 @Config(shadows = ShadowUtils.class) initHeader_HasAppEntry_BuildByAppEntry()200 public void initHeader_HasAppEntry_BuildByAppEntry() { 201 mFragment.mAppEntry = mAppEntry; 202 mFragment.initHeader(); 203 204 assertThat(mIntroPreference.getIcon()).isEqualTo(TEST_DRAWABLE); 205 assertThat(mIntroPreference.getTitle()).isEqualTo(mAppEntry.label); 206 } 207 208 @Test 209 @Config(shadows = ShadowUtils.class) initHeader_HasAppEntry_InstantApp()210 public void initHeader_HasAppEntry_InstantApp() { 211 mFragment.mAppEntry = mAppEntry; 212 mFragment.initHeader(); 213 214 assertThat(mIntroPreference.getIcon()).isEqualTo(TEST_DRAWABLE); 215 assertThat(mIntroPreference.getTitle()).isEqualTo(mAppEntry.label); 216 } 217 218 @Test initFooter_setExpectedFooterContent()219 public void initFooter_setExpectedFooterContent() { 220 mFragment.initFooter(); 221 222 verify(mFooterPreference) 223 .setTitle(mContext.getString(R.string.manager_battery_usage_footer)); 224 verify(mFooterPreference).setLearnMoreAction(any()); 225 verify(mFooterPreference) 226 .setLearnMoreText(mContext.getString(R.string.manager_battery_usage_link_a11y)); 227 } 228 229 @Test onPause_optimizationModeIsChanged_logPreference()230 public void onPause_optimizationModeIsChanged_logPreference() throws Exception { 231 mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_OPTIMIZED; 232 when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); 233 when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); 234 235 mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; 236 assertThat(mBatteryOptimizeUtils.getAppOptimizationMode()) 237 .isEqualTo(BatteryOptimizeUtils.MODE_UNRESTRICTED); 238 mFragment.onPause(); 239 240 TimeUnit.SECONDS.sleep(1); 241 verify(mMetricsFeatureProvider) 242 .action( 243 SettingsEnums.LEAVE_POWER_USAGE_MANAGE_BACKGROUND, 244 SettingsEnums.ACTION_APP_BATTERY_USAGE_UNRESTRICTED, 245 SettingsEnums.FUELGAUGE_POWER_USAGE_MANAGE_BACKGROUND, 246 PACKAGE_NAME, 247 /* consumed battery */ 0); 248 } 249 250 @Test onPause_optimizationModeIsNotChanged_notInvokeLogging()251 public void onPause_optimizationModeIsNotChanged_notInvokeLogging() throws Exception { 252 mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_OPTIMIZED; 253 when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); 254 when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); 255 256 mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; 257 assertThat(mBatteryOptimizeUtils.getAppOptimizationMode()) 258 .isEqualTo(BatteryOptimizeUtils.MODE_UNRESTRICTED); 259 mTestMode = BatteryOptimizeUtils.MODE_OPTIMIZED; 260 assertThat(mBatteryOptimizeUtils.getAppOptimizationMode()) 261 .isEqualTo(BatteryOptimizeUtils.MODE_OPTIMIZED); 262 mFragment.onPause(); 263 264 TimeUnit.SECONDS.sleep(1); 265 verifyNoInteractions(mMetricsFeatureProvider); 266 } 267 prepareTestBatteryOptimizationUtils()268 private void prepareTestBatteryOptimizationUtils() { 269 mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); 270 Answer<Void> setTestMode = 271 invocation -> { 272 mTestMode = invocation.getArgument(0); 273 return null; 274 }; 275 doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); 276 Answer<Integer> getTestMode = invocation -> mTestMode; 277 doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); 278 } 279 } 280