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