• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.car.pm;
18 
19 import static android.Manifest.permission.QUERY_ALL_PACKAGES;
20 import static android.car.content.pm.CarPackageManager.ERROR_CODE_NO_PACKAGE;
21 import static android.car.content.pm.CarPackageManager.MANIFEST_METADATA_TARGET_CAR_VERSION;
22 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
23 
24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
25 
26 import static com.google.common.truth.Truth.assertThat;
27 import static com.google.common.truth.Truth.assertWithMessage;
28 
29 import static org.junit.Assert.assertThrows;
30 import static org.mockito.ArgumentMatchers.any;
31 import static org.mockito.ArgumentMatchers.anyBoolean;
32 import static org.mockito.ArgumentMatchers.anyInt;
33 import static org.mockito.ArgumentMatchers.eq;
34 import static org.mockito.Mockito.doReturn;
35 import static org.mockito.Mockito.mock;
36 import static org.mockito.Mockito.spy;
37 import static org.mockito.Mockito.when;
38 
39 import android.app.PendingIntent;
40 import android.car.CarVersion;
41 import android.car.builtin.app.ActivityManagerHelper;
42 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
43 import android.content.Context;
44 import android.content.pm.ApplicationInfo;
45 import android.content.pm.PackageManager;
46 import android.content.pm.PackageManager.NameNotFoundException;
47 import android.os.Binder;
48 import android.os.Bundle;
49 import android.os.Process;
50 import android.os.ServiceSpecificException;
51 import android.os.UserHandle;
52 
53 import androidx.test.ext.junit.runners.AndroidJUnit4;
54 import androidx.test.platform.app.InstrumentationRegistry;
55 
56 import com.android.car.CarOccupantZoneService;
57 import com.android.car.CarUxRestrictionsManagerService;
58 import com.android.car.am.CarActivityService;
59 
60 import org.junit.Before;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 import org.mockito.Mock;
64 
65 import java.util.ArrayList;
66 import java.util.HashMap;
67 import java.util.Map;
68 import java.util.Set;
69 
70 /**
71  * Unit tests for {@link CarPackageManagerService}.
72  */
73 @RunWith(AndroidJUnit4.class)
74 public class CarPackageManagerServiceUnitTest extends AbstractExtendedMockitoTestCase {
75     CarPackageManagerService mService;
76 
77     private Context mSpiedContext;
78     private PackageManager mSpiedPackageManager;
79 
80     private final UserHandle mUserHandle = UserHandle.of(666);
81 
82     @Mock
83     private Context mUserContext;
84 
85     @Mock
86     private CarUxRestrictionsManagerService mMockUxrService;
87     @Mock
88     private CarActivityService mMockActivityService;
89     @Mock
90     private CarOccupantZoneService mMockCarOccupantZoneService;
91     @Mock
92     private PendingIntent mMockPendingIntent;
93 
CarPackageManagerServiceUnitTest()94     public CarPackageManagerServiceUnitTest() {
95         super(CarPackageManagerService.TAG);
96     }
97 
98     @Override
onSessionBuilder(CustomMockitoSessionBuilder builder)99     protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
100         builder
101             .spyStatic(ActivityManagerHelper.class)
102             .spyStatic(Binder.class)
103             // Need to mock service itself because of getTargetCarVersion() - it doesn't make
104             // sense to test all variations of the methods that call it
105             .spyStatic(CarPackageManagerService.class);
106     }
107 
108     @Before
setUp()109     public void setUp() {
110         mSpiedContext = spy(InstrumentationRegistry.getInstrumentation().getTargetContext());
111 
112         doReturn(mUserContext).when(mSpiedContext).createContextAsUser(mUserHandle, /* flags= */ 0);
113 
114         mSpiedPackageManager = spy(mSpiedContext.getPackageManager());
115         doReturn(mSpiedPackageManager).when(mSpiedContext).getPackageManager();
116 
117         mService = new CarPackageManagerService(mSpiedContext,
118                 mMockUxrService, mMockActivityService, mMockCarOccupantZoneService);
119     }
120 
121     @Test
testParseConfigList_SingleActivity()122     public void testParseConfigList_SingleActivity() {
123         String config = "com.android.test/.TestActivity";
124         Map<String, Set<String>> map = new HashMap<>();
125 
126         mService.parseConfigList(config, map);
127 
128         assertThat(map.get("com.android.test")).containsExactly(".TestActivity");
129     }
130 
131     @Test
testParseConfigList_Package()132     public void testParseConfigList_Package() {
133         String config = "com.android.test";
134         Map<String, Set<String>> map = new HashMap<>();
135 
136         mService.parseConfigList(config, map);
137 
138         assertThat(map.get("com.android.test")).isEmpty();
139     }
140 
141     @Test
testParseConfigList_MultipleActivities()142     public void testParseConfigList_MultipleActivities() {
143         String config = "com.android.test/.TestActivity0,com.android.test/.TestActivity1";
144         Map<String, Set<String>> map = new HashMap<>();
145 
146         mService.parseConfigList(config, map);
147 
148         assertThat(map.get("com.android.test")).containsExactly(".TestActivity0", ".TestActivity1");
149     }
150 
151     @Test
testParseConfigList_PackageAndActivity()152     public void testParseConfigList_PackageAndActivity() {
153         String config = "com.android.test/.TestActivity0,com.android.test";
154         Map<String, Set<String>> map = new HashMap<>();
155 
156         mService.parseConfigList(config, map);
157 
158         assertThat(map.get("com.android.test")).isEmpty();
159     }
160 
161     @Test
test_checkQueryPermission_noPermission()162     public void test_checkQueryPermission_noPermission() {
163         mockQueryPermission(false);
164 
165         assertThat(mService.callerCanQueryPackage("blah")).isFalse();
166     }
167 
168     @Test
test_checkQueryPermission_correctPermission()169     public void test_checkQueryPermission_correctPermission() {
170         mockQueryPermission(true);
171 
172         assertThat(mService.callerCanQueryPackage("blah")).isTrue();
173     }
174 
175     @Test
test_checkQueryPermission_samePackage()176     public void test_checkQueryPermission_samePackage() {
177         mockQueryPermission(false);
178 
179         assertThat(mService.callerCanQueryPackage(
180                 "com.android.car.carservice_unittest")).isTrue();
181     }
182 
183     @Test
testIsPendingIntentDistractionOptimised_withoutActivity()184     public void testIsPendingIntentDistractionOptimised_withoutActivity() {
185         when(mMockPendingIntent.isActivity()).thenReturn(false);
186 
187         assertThat(mService.isPendingIntentDistractionOptimized(mMockPendingIntent)).isFalse();
188     }
189 
190     @Test
testIsPendingIntentDistractionOptimised_noIntentComponents()191     public void testIsPendingIntentDistractionOptimised_noIntentComponents() {
192         when(mMockPendingIntent.isActivity()).thenReturn(true);
193         when(mMockPendingIntent.queryIntentComponents(MATCH_DEFAULT_ONLY)).thenReturn(
194                 new ArrayList<>());
195 
196         assertThat(mService.isPendingIntentDistractionOptimized(mMockPendingIntent)).isFalse();
197     }
198 
199     @Test
testGetTargetCarVersion_ok()200     public void testGetTargetCarVersion_ok() {
201         String pkgName = "dr.evil";
202         CarVersion Version = CarVersion.forMajorAndMinorVersions(66, 6);
203 
204         doReturn(Version)
205                 .when(() -> CarPackageManagerService.getTargetCarVersion(mUserContext, pkgName));
206 
207         mockCallingUser();
208 
209         assertWithMessage("getTargetCarVersion(%s)", pkgName)
210                 .that(mService.getTargetCarVersion(pkgName)).isSameInstanceAs(Version);
211     }
212 
213     @Test
testGetTargetCarVersion_byUser()214     public void testGetTargetCarVersion_byUser() {
215         String pkgName = "dr.evil";
216         CarVersion Version = CarVersion.forMajorAndMinorVersions(66, 6);
217 
218         doReturn(Version)
219                 .when(() -> CarPackageManagerService.getTargetCarVersion(mUserContext, pkgName));
220 
221         mockCallingUser();
222 
223         assertWithMessage("getTargetCarVersion(%s)", pkgName)
224                 .that(mService.getTargetCarVersion(mUserHandle, pkgName))
225                 .isSameInstanceAs(Version);
226     }
227 
228     @Test
testGetTargetCarVersion_self_ok()229     public void testGetTargetCarVersion_self_ok() {
230         String pkgName = "dr.evil";
231         int myUid = Process.myUid();
232         doReturn(new String[] { pkgName }).when(mSpiedPackageManager).getPackagesForUid(myUid);
233         CarVersion Version = CarVersion.forMajorAndMinorVersions(66, 6);
234 
235         doReturn(Version)
236                 .when(() -> CarPackageManagerService.getTargetCarVersion(mUserContext, pkgName));
237 
238         mockCallingUser();
239 
240         assertWithMessage("getTargetCarVersion(%s)", pkgName)
241                 .that(mService.getSelfTargetCarVersion(pkgName)).isSameInstanceAs(Version);
242     }
243 
244     @Test
testGetTargetCarVersion_self_wrongUid()245     public void testGetTargetCarVersion_self_wrongUid() {
246         int myUid = Process.myUid();
247         String pkgName = "dr.evil";
248         CarVersion Version = CarVersion.forMajorAndMinorVersions(66, 6);
249 
250         doReturn(Version)
251                 .when(() -> CarPackageManagerService.getTargetCarVersion(mUserContext, pkgName));
252 
253         mockCallingUser();
254 
255         SecurityException e = assertThrows(SecurityException.class,
256                 () -> mService.getSelfTargetCarVersion(pkgName));
257 
258         String msg = e.getMessage();
259         assertWithMessage("exception message (pkg)").that(msg).contains(pkgName);
260         assertWithMessage("exception message (uid)").that(msg).contains(String.valueOf(myUid));
261     }
262 
263     @Test
testGetTargetCarVersion_static_null()264     public void testGetTargetCarVersion_static_null() {
265         assertThrows(NullPointerException.class,
266                 () -> CarPackageManagerService.getTargetCarVersion(mUserContext, null));
267     }
268 
269     @Test
testGetTargetCarVersion_static_noPermission()270     public void testGetTargetCarVersion_static_noPermission() {
271         mockQueryPermission(/* granted= */ false);
272 
273         assertThrows(SecurityException.class,
274                 () -> CarPackageManagerService.getTargetCarVersion(mUserContext, "d.oh"));
275     }
276 
277 
278     @Test
testGetTargetCarVersion_static_noApp()279     public void testGetTargetCarVersion_static_noApp() throws Exception {
280         mockQueryPermission(/* granted= */ true);
281         String causeMsg = mockGetApplicationInfoThrowsNotFound(mUserContext, "meaning.of.life");
282 
283         ServiceSpecificException e = assertThrows(ServiceSpecificException.class,
284                 () -> CarPackageManagerService.getTargetCarVersion(mUserContext,
285                         "meaning.of.life"));
286         assertWithMessage("exception code").that(e.errorCode).isEqualTo(ERROR_CODE_NO_PACKAGE);
287         assertWithMessage("exception msg").that(e.getMessage()).isEqualTo(causeMsg);
288     }
289 
290     // No need to test all scenarios, as they're tested by CarVersionParserParseMethodTest
291     @Test
testGetTargetCarVersion_static_ok()292     public void testGetTargetCarVersion_static_ok() throws Exception {
293         mockQueryPermission(/* granted= */ true);
294         ApplicationInfo info = mockGetApplicationInfo(mUserContext, "meaning.of.life");
295         info.targetSdkVersion = 666; // Set to make sure it's not used
296         info.metaData = new Bundle();
297         info.metaData.putString(MANIFEST_METADATA_TARGET_CAR_VERSION, "42:108");
298 
299         CarVersion actualVersion = CarPackageManagerService.getTargetCarVersion(
300                 mUserContext, "meaning.of.life");
301 
302         assertWithMessage("static getTargetCarVersion()").that(actualVersion).isNotNull();
303         assertWithMessage("major version").that(actualVersion.getMajorVersion()).isEqualTo(42);
304         assertWithMessage("minor version").that(actualVersion.getMinorVersion()).isEqualTo(108);
305     }
306 
mockQueryPermission(boolean granted)307     private void mockQueryPermission(boolean granted) {
308         int result = android.content.pm.PackageManager.PERMISSION_DENIED;
309         if (granted) {
310             result = android.content.pm.PackageManager.PERMISSION_GRANTED;
311         }
312         doReturn(result).when(() -> ActivityManagerHelper.checkComponentPermission(
313                 eq(QUERY_ALL_PACKAGES), anyInt(), anyInt(), anyBoolean()));
314         when(mUserContext.checkCallingOrSelfPermission(QUERY_ALL_PACKAGES)).thenReturn(result);
315     }
316 
mockCallingUser()317     private void mockCallingUser() {
318         doReturn(mUserHandle).when(() -> Binder.getCallingUserHandle());
319     }
320 
mockGetApplicationInfoThrowsNotFound(Context context, String packageName)321     private static String mockGetApplicationInfoThrowsNotFound(Context context, String packageName)
322             throws NameNotFoundException {
323         String msg = "D'OH!";
324         PackageManager pm = mock(PackageManager.class);
325         when(context.getPackageManager()).thenReturn(pm);
326         when(pm.getApplicationInfo(eq(packageName), any()))
327                 .thenThrow(new NameNotFoundException(msg));
328         return msg;
329     }
330 
mockGetApplicationInfo(Context context, String packageName)331     private static ApplicationInfo mockGetApplicationInfo(Context context, String packageName)
332             throws NameNotFoundException {
333         ApplicationInfo info = new ApplicationInfo();
334         PackageManager pm = mock(PackageManager.class);
335         when(context.getPackageManager()).thenReturn(pm);
336         when(pm.getApplicationInfo(eq(packageName), any())).thenReturn(info);
337         return info;
338     }
339 }
340