• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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