• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.car.settings.applications.performance;
18 
19 import static android.car.settings.CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE;
20 import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;
21 
22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 import static com.google.common.truth.Truth.assertWithMessage;
27 
28 import static org.mockito.ArgumentMatchers.any;
29 import static org.mockito.ArgumentMatchers.anyInt;
30 import static org.mockito.ArgumentMatchers.anyLong;
31 import static org.mockito.ArgumentMatchers.anyString;
32 import static org.mockito.ArgumentMatchers.eq;
33 import static org.mockito.Mockito.doNothing;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.spy;
36 import static org.mockito.Mockito.verifyNoMoreInteractions;
37 import static org.mockito.Mockito.when;
38 
39 import android.car.Car;
40 import android.car.drivingstate.CarUxRestrictions;
41 import android.car.watchdog.CarWatchdogManager;
42 import android.car.watchdog.PackageKillableState;
43 import android.content.Context;
44 import android.content.DialogInterface;
45 import android.content.Intent;
46 import android.content.pm.ActivityInfo;
47 import android.content.pm.ApplicationInfo;
48 import android.content.pm.PackageManager;
49 import android.content.pm.ResolveInfo;
50 import android.net.Uri;
51 import android.os.UserHandle;
52 import android.provider.Settings;
53 
54 import androidx.lifecycle.LifecycleOwner;
55 import androidx.preference.Preference;
56 import androidx.preference.PreferenceManager;
57 import androidx.preference.PreferenceScreen;
58 import androidx.test.annotation.UiThreadTest;
59 import androidx.test.core.app.ApplicationProvider;
60 import androidx.test.ext.junit.runners.AndroidJUnit4;
61 
62 import com.android.car.settings.common.ConfirmationDialogFragment;
63 import com.android.car.settings.common.FragmentController;
64 import com.android.car.settings.common.LogicalPreferenceGroup;
65 import com.android.car.settings.common.PreferenceControllerTestUtil;
66 import com.android.car.settings.testutils.TestLifecycleOwner;
67 import com.android.car.ui.preference.CarUiTwoActionTextPreference;
68 
69 import org.junit.After;
70 import org.junit.Before;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 import org.mockito.ArgumentCaptor;
74 import org.mockito.Captor;
75 import org.mockito.Mock;
76 import org.mockito.MockitoSession;
77 import org.mockito.quality.Strictness;
78 
79 import java.io.File;
80 import java.util.List;
81 import java.util.stream.Collectors;
82 
83 @RunWith(AndroidJUnit4.class)
84 public final class PerfImpactingAppsPreferenceControllerTest {
85     private static final int TEST_USER_ID = 100;
86     private static final int TEST_PKG_UID = 10000001;
87     private static final String TEST_PKG_NAME = "test.package.name";
88     private static final int TEST_PRIVILEGE_PKG_UID = 10000002;
89     private static final String TEST_PRIVILEGE_PKG_NAME = "test.privilege.package.name";
90     private static final String TEST_DISABLED_PACKAGES_SETTING_STRING =
91             TEST_PKG_NAME + ";" + TEST_PRIVILEGE_PKG_NAME;
92 
93     private final Context mContext = spy(ApplicationProvider.getApplicationContext());
94 
95     private MockitoSession mMockingSession;
96     private LifecycleOwner mLifecycleOwner;
97     private PerfImpactingAppsPreferenceController mController;
98     private LogicalPreferenceGroup mPreferenceGroup;
99 
100     @Captor
101     private ArgumentCaptor<Car.CarServiceLifecycleListener> mCarLifecycleCaptor;
102     @Captor
103     private ArgumentCaptor<ConfirmationDialogFragment> mDialogFragmentCaptor;
104     @Mock
105     private PackageManager mMockPackageManager;
106     @Mock
107     private FragmentController mMockFragmentController;
108     @Mock
109     private Car mMockCar;
110     @Mock
111     private CarWatchdogManager mMockCarWatchdogManager;
112 
113     @Before
114     @UiThreadTest
setUp()115     public void setUp() {
116         mMockingSession = mockitoSession()
117                 .initMocks(this)
118                 .mockStatic(Car.class)
119                 .mockStatic(Settings.Secure.class)
120                 .strictness(Strictness.LENIENT)
121                 .startMocking();
122         mLifecycleOwner = new TestLifecycleOwner();
123 
124         mDialogFragmentCaptor = ArgumentCaptor.forClass(ConfirmationDialogFragment.class);
125 
126         when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
127         when(Car.createCar(any(), any(), anyLong(), mCarLifecycleCaptor.capture())).then(
128                 invocation -> {
129                     Car.CarServiceLifecycleListener listener = mCarLifecycleCaptor.getValue();
130                     listener.onLifecycleChanged(mMockCar, true);
131                     return mMockCar;
132                 });
133         when(mMockCar.getCarManager(Car.CAR_WATCHDOG_SERVICE)).thenReturn(mMockCarWatchdogManager);
134         when(mMockCarWatchdogManager.getPackageKillableStatesAsUser(
135                 UserHandle.getUserHandleForUid(TEST_PKG_UID)))
136                 .thenReturn(List.of(
137                         new PackageKillableState(TEST_PKG_NAME, TEST_USER_ID,
138                                 PackageKillableState.KILLABLE_STATE_YES),
139                         new PackageKillableState(TEST_PRIVILEGE_PKG_NAME, TEST_USER_ID,
140                                 PackageKillableState.KILLABLE_STATE_NEVER)));
141 
142         CarUxRestrictions restrictions = new CarUxRestrictions.Builder(/* reqOpt= */ true,
143                 CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build();
144 
145         PreferenceManager preferenceManager = new PreferenceManager(mContext);
146         PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
147         mPreferenceGroup = new LogicalPreferenceGroup(mContext);
148         screen.addPreference(mPreferenceGroup);
149 
150         mController = new PerfImpactingAppsPreferenceController(mContext,
151                 /* preferenceKey= */ "key", mMockFragmentController, restrictions);
152 
153         PreferenceControllerTestUtil.assignPreference(mController, mPreferenceGroup);
154 
155         initController();
156     }
157 
158     @After
159     @UiThreadTest
tearDown()160     public void tearDown() {
161         (new File(mContext.getDataDir(), TEST_PKG_NAME)).delete();
162         (new File(mContext.getDataDir(), TEST_PRIVILEGE_PKG_NAME)).delete();
163 
164         mMockingSession.finishMocking();
165     }
166 
167     @Test
168     @UiThreadTest
onPreferenceClick_primaryAppAction_sendAppDetailIntent()169     public void onPreferenceClick_primaryAppAction_sendAppDetailIntent() {
170         doNothing().when(mContext).startActivity(any());
171 
172         Preference actualPreference = mPreferenceGroup.getPreference(0);
173 
174         Preference.OnPreferenceClickListener onPreferenceClickListener =
175                 actualPreference.getOnPreferenceClickListener();
176 
177         onPreferenceClickListener.onPreferenceClick(actualPreference);
178 
179         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
180         verify(mContext).startActivity(captor.capture());
181         Intent intent = captor.getValue();
182         assertThat(intent).isNotNull();
183         assertWithMessage("Intent action").that(intent.getAction())
184                 .isEqualTo(ACTION_APPLICATION_DETAILS_SETTINGS);
185         assertWithMessage("Intent data").that(intent.getData())
186                 .isEqualTo(Uri.parse("package:" + TEST_PKG_NAME));
187     }
188 
189     @Test
190     @UiThreadTest
onPreferenceClick_showConfirmationDialog_prioritizeApp()191     public void onPreferenceClick_showConfirmationDialog_prioritizeApp() {
192         CarUiTwoActionTextPreference actualPreference =
193                 (CarUiTwoActionTextPreference) mPreferenceGroup.getPreference(0);
194 
195         actualPreference.performSecondaryActionClick();
196 
197         verify(mMockFragmentController).showDialog(mDialogFragmentCaptor.capture(), anyString());
198         ConfirmationDialogFragment dialogFragment = mDialogFragmentCaptor.getValue();
199 
200         assertThat(dialogFragment).isNotNull();
201 
202         dialogFragment.onClick(dialogFragment.getDialog(), DialogInterface.BUTTON_POSITIVE);
203 
204         verify(mMockCarWatchdogManager).setKillablePackageAsUser(TEST_PKG_NAME,
205                 UserHandle.getUserHandleForUid(TEST_PKG_UID),
206                 /* isKillable= */ false);
207     }
208 
209     @Test
210     @UiThreadTest
onPreferenceClick_showConfirmationDialog_prioritizePrivilegedApp()211     public void onPreferenceClick_showConfirmationDialog_prioritizePrivilegedApp() {
212         CarUiTwoActionTextPreference actualPreference =
213                 (CarUiTwoActionTextPreference) mPreferenceGroup.getPreference(1);
214 
215         actualPreference.performSecondaryActionClick();
216 
217         verify(mMockFragmentController).showDialog(mDialogFragmentCaptor.capture(), anyString());
218         ConfirmationDialogFragment dialogFragment = mDialogFragmentCaptor.getValue();
219 
220         assertThat(dialogFragment).isNotNull();
221 
222         dialogFragment.onClick(dialogFragment.getDialog(), DialogInterface.BUTTON_POSITIVE);
223 
224         verify(mMockPackageManager).setApplicationEnabledSetting(TEST_PRIVILEGE_PKG_NAME,
225                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0);
226     }
227 
228     @Test
229     @UiThreadTest
onPreferenceClick_showConfirmationDialog_cancel()230     public void onPreferenceClick_showConfirmationDialog_cancel() {
231         CarUiTwoActionTextPreference actualPreference =
232                 (CarUiTwoActionTextPreference) mPreferenceGroup.getPreference(1);
233 
234         actualPreference.performSecondaryActionClick();
235 
236         verify(mMockFragmentController).showDialog(mDialogFragmentCaptor.capture(), anyString());
237         ConfirmationDialogFragment dialogFragment = mDialogFragmentCaptor.getValue();
238 
239         assertThat(dialogFragment).isNotNull();
240 
241         dialogFragment.onClick(dialogFragment.getDialog(), DialogInterface.BUTTON_NEGATIVE);
242 
243         verify(mMockPackageManager, never())
244                 .setApplicationEnabledSetting(anyString(), anyInt(), anyInt());
245         verifyNoMoreInteractions(mMockCarWatchdogManager);
246     }
247 
248     @Test
249     @UiThreadTest
onCreate_perfImpactingApps_withNoPackages()250     public void onCreate_perfImpactingApps_withNoPackages() {
251         when(Settings.Secure.getString(any(), eq(KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE)))
252                 .thenReturn("");
253 
254         mController.onDestroy(mLifecycleOwner);
255 
256         mController.onCreate(mLifecycleOwner);
257 
258         assertWithMessage("Preference group count")
259                 .that(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
260     }
261 
initController()262     private void initController() {
263         List<ResolveInfo> expectedResultInfos = getResultInfos();
264 
265         when(Settings.Secure.getString(any(), eq(KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE)))
266                 .thenReturn(TEST_DISABLED_PACKAGES_SETTING_STRING);
267         when(mMockPackageManager.queryIntentActivities(any(), any()))
268                 .thenReturn(expectedResultInfos);
269 
270         mController.onCreate(mLifecycleOwner);
271 
272         List<CarUiTwoActionTextPreference> expectedPreferences =
273                 getPreferences(expectedResultInfos);
274 
275         assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(expectedPreferences.size());
276 
277         for (int idx = 0; idx < expectedPreferences.size(); idx++) {
278             assertThatPreferenceAreEqual(idx,
279                     (CarUiTwoActionTextPreference) mPreferenceGroup.getPreference(idx),
280                     expectedPreferences.get(idx));
281         }
282     }
283 
getPreferences(List<ResolveInfo> resolveInfos)284     private List<CarUiTwoActionTextPreference> getPreferences(List<ResolveInfo> resolveInfos) {
285         return resolveInfos.stream().map(
286                 resolveInfo ->
287                         new PerfImpactingAppsPreferenceController.PerformanceImpactingAppPreference(
288                                 mContext, resolveInfo.activityInfo.applicationInfo))
289                 .collect(Collectors.toList());
290     }
291 
getResultInfos()292     private List<ResolveInfo> getResultInfos() {
293         // Non-privileged Package
294         ResolveInfo resolveInfo1 = new ResolveInfo();
295         resolveInfo1.activityInfo = new ActivityInfo();
296         resolveInfo1.activityInfo.applicationInfo = new ApplicationInfo();
297         resolveInfo1.activityInfo.applicationInfo.uid = TEST_PKG_UID;
298         resolveInfo1.activityInfo.applicationInfo.packageName = TEST_PKG_NAME;
299 
300         File appFile = new File(mContext.getDataDir(), TEST_PKG_NAME);
301         assertWithMessage("%s dir", TEST_PKG_NAME).that(appFile.mkdir()).isTrue();
302 
303         resolveInfo1.activityInfo.applicationInfo.sourceDir = appFile.getAbsolutePath();
304 
305         // Privileged Package
306         ResolveInfo resolveInfo2 = new ResolveInfo();
307         resolveInfo2.activityInfo = new ActivityInfo();
308         resolveInfo2.activityInfo.applicationInfo = new ApplicationInfo();
309         resolveInfo2.activityInfo.applicationInfo.uid = TEST_PRIVILEGE_PKG_UID;
310         resolveInfo2.activityInfo.applicationInfo.packageName = TEST_PRIVILEGE_PKG_NAME;
311 
312         appFile = new File(mContext.getDataDir(), TEST_PRIVILEGE_PKG_NAME);
313         assertWithMessage("%s dir", TEST_PRIVILEGE_PKG_NAME).that(appFile.mkdir()).isTrue();
314 
315         resolveInfo2.activityInfo.applicationInfo.sourceDir = appFile.getAbsolutePath();
316 
317         return List.of(resolveInfo1, resolveInfo2);
318     }
319 
assertThatPreferenceAreEqual(int index, CarUiTwoActionTextPreference p1, CarUiTwoActionTextPreference p2)320     private static void assertThatPreferenceAreEqual(int index, CarUiTwoActionTextPreference p1,
321             CarUiTwoActionTextPreference p2) {
322         assertWithMessage("Preference %s key", index).that(p1.getKey()).isEqualTo(p2.getKey());
323         assertWithMessage("Preference %s title", index).that(p1.getTitle().toString())
324                 .isEqualTo(p2.getTitle().toString());
325         assertWithMessage("Preference %s secondary action text", index)
326                 .that(p2.getSecondaryActionText().toString())
327                 .isEqualTo(p2.getSecondaryActionText().toString());
328     }
329 }
330