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.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY; 21 import static android.car.content.pm.CarPackageManager.ERROR_CODE_NO_PACKAGE; 22 import static android.car.content.pm.CarPackageManager.MANIFEST_METADATA_TARGET_CAR_VERSION; 23 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; 24 import static android.os.Process.INVALID_UID; 25 import static android.view.Display.DEFAULT_DISPLAY; 26 27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 28 29 import static com.google.common.truth.Truth.assertThat; 30 import static com.google.common.truth.Truth.assertWithMessage; 31 32 import static org.junit.Assert.assertThrows; 33 import static org.mockito.ArgumentMatchers.any; 34 import static org.mockito.ArgumentMatchers.anyBoolean; 35 import static org.mockito.ArgumentMatchers.anyInt; 36 import static org.mockito.ArgumentMatchers.eq; 37 import static org.mockito.Mockito.mock; 38 import static org.mockito.Mockito.spy; 39 import static org.mockito.Mockito.times; 40 import static org.mockito.Mockito.verify; 41 import static org.mockito.Mockito.when; 42 43 import android.app.ActivityManager; 44 import android.app.PendingIntent; 45 import android.car.CarVersion; 46 import android.car.builtin.app.ActivityManagerHelper; 47 import android.car.content.pm.ICarBlockingUiCommandListener; 48 import android.car.test.mocks.AbstractExtendedMockitoTestCase; 49 import android.content.Context; 50 import android.content.pm.ApplicationInfo; 51 import android.content.pm.PackageManager; 52 import android.content.pm.PackageManager.NameNotFoundException; 53 import android.os.Binder; 54 import android.os.Bundle; 55 import android.os.IBinder; 56 import android.os.Process; 57 import android.os.RemoteException; 58 import android.os.ServiceSpecificException; 59 import android.os.UserHandle; 60 61 import androidx.test.ext.junit.runners.AndroidJUnit4; 62 import androidx.test.platform.app.InstrumentationRegistry; 63 64 import com.android.car.CarOccupantZoneService; 65 import com.android.car.CarUxRestrictionsManagerService; 66 import com.android.car.am.CarActivityService; 67 68 import org.junit.Before; 69 import org.junit.Test; 70 import org.junit.runner.RunWith; 71 import org.mockito.Mock; 72 73 import java.util.ArrayList; 74 import java.util.HashMap; 75 import java.util.Map; 76 import java.util.Set; 77 78 /** 79 * Unit tests for {@link CarPackageManagerService}. 80 */ 81 @RunWith(AndroidJUnit4.class) 82 public class CarPackageManagerServiceUnitTest extends AbstractExtendedMockitoTestCase { 83 CarPackageManagerService mService; 84 85 private Context mSpiedContext; 86 private PackageManager mSpiedPackageManager; 87 88 private final UserHandle mUserHandle = UserHandle.of(666); 89 90 @Mock 91 private Context mUserContext; 92 93 @Mock 94 private CarUxRestrictionsManagerService mMockUxrService; 95 @Mock 96 private CarActivityService mMockActivityService; 97 @Mock 98 private CarOccupantZoneService mMockCarOccupantZoneService; 99 @Mock 100 private PendingIntent mMockPendingIntent; 101 CarPackageManagerServiceUnitTest()102 public CarPackageManagerServiceUnitTest() { 103 super(CarPackageManagerService.TAG); 104 } 105 106 @Override onSessionBuilder(CustomMockitoSessionBuilder builder)107 protected void onSessionBuilder(CustomMockitoSessionBuilder builder) { 108 builder 109 .spyStatic(ActivityManagerHelper.class) 110 .spyStatic(Binder.class) 111 // Need to mock service itself because of getTargetCarVersion() - it doesn't make 112 // sense to test all variations of the methods that call it 113 .spyStatic(CarPackageManagerService.class); 114 } 115 116 @Before setUp()117 public void setUp() { 118 mSpiedContext = spy(InstrumentationRegistry.getInstrumentation().getContext()); 119 120 doReturn(mUserContext).when(mSpiedContext).createContextAsUser(mUserHandle, /* flags= */ 0); 121 122 mSpiedPackageManager = spy(mSpiedContext.getPackageManager()); 123 doReturn(mSpiedPackageManager).when(mSpiedContext).getPackageManager(); 124 125 mService = new CarPackageManagerService(mSpiedContext, 126 mMockUxrService, mMockActivityService, mMockCarOccupantZoneService); 127 } 128 129 @Test testParseConfigList_SingleActivity()130 public void testParseConfigList_SingleActivity() { 131 String config = "com.android.test/.TestActivity"; 132 Map<String, Set<String>> map = new HashMap<>(); 133 134 mService.parseConfigList(config, map); 135 136 assertThat(map.get("com.android.test")).containsExactly(".TestActivity"); 137 } 138 139 @Test testParseConfigList_Package()140 public void testParseConfigList_Package() { 141 String config = "com.android.test"; 142 Map<String, Set<String>> map = new HashMap<>(); 143 144 mService.parseConfigList(config, map); 145 146 assertThat(map.get("com.android.test")).isEmpty(); 147 } 148 149 @Test testParseConfigList_MultipleActivities()150 public void testParseConfigList_MultipleActivities() { 151 String config = "com.android.test/.TestActivity0,com.android.test/.TestActivity1"; 152 Map<String, Set<String>> map = new HashMap<>(); 153 154 mService.parseConfigList(config, map); 155 156 assertThat(map.get("com.android.test")).containsExactly(".TestActivity0", ".TestActivity1"); 157 } 158 159 @Test testParseConfigList_PackageAndActivity()160 public void testParseConfigList_PackageAndActivity() { 161 String config = "com.android.test/.TestActivity0,com.android.test"; 162 Map<String, Set<String>> map = new HashMap<>(); 163 164 mService.parseConfigList(config, map); 165 166 assertThat(map.get("com.android.test")).isEmpty(); 167 } 168 169 @Test test_checkQueryPermission_noPermission()170 public void test_checkQueryPermission_noPermission() { 171 mockQueryPermission(false); 172 173 assertThat(mService.callerCanQueryPackage("blah")).isFalse(); 174 } 175 176 @Test test_checkQueryPermission_correctPermission()177 public void test_checkQueryPermission_correctPermission() { 178 mockQueryPermission(true); 179 180 assertThat(mService.callerCanQueryPackage("blah")).isTrue(); 181 } 182 183 @Test test_checkQueryPermission_samePackage()184 public void test_checkQueryPermission_samePackage() { 185 mockQueryPermission(false); 186 187 assertThat(mService.callerCanQueryPackage( 188 "com.android.car.carservice_unittest")).isTrue(); 189 } 190 191 @Test testIsPendingIntentDistractionOptimised_withoutActivity()192 public void testIsPendingIntentDistractionOptimised_withoutActivity() { 193 when(mMockPendingIntent.isActivity()).thenReturn(false); 194 195 assertThat(mService.isPendingIntentDistractionOptimized(mMockPendingIntent)).isFalse(); 196 } 197 198 @Test testIsPendingIntentDistractionOptimised_noIntentComponents()199 public void testIsPendingIntentDistractionOptimised_noIntentComponents() { 200 when(mMockPendingIntent.isActivity()).thenReturn(true); 201 when(mMockPendingIntent.queryIntentComponents(MATCH_DEFAULT_ONLY)).thenReturn( 202 new ArrayList<>()); 203 204 assertThat(mService.isPendingIntentDistractionOptimized(mMockPendingIntent)).isFalse(); 205 } 206 207 @Test testGetTargetCarVersion_ok()208 public void testGetTargetCarVersion_ok() { 209 String pkgName = "dr.evil"; 210 CarVersion Version = CarVersion.forMajorAndMinorVersions(66, 6); 211 212 doReturn(Version) 213 .when(() -> CarPackageManagerService.getTargetCarVersion(mUserContext, pkgName)); 214 215 mockCallingUser(); 216 217 assertWithMessage("getTargetCarVersion(%s)", pkgName) 218 .that(mService.getTargetCarVersion(pkgName)).isSameInstanceAs(Version); 219 } 220 221 @Test testGetTargetCarVersion_byUser()222 public void testGetTargetCarVersion_byUser() { 223 String pkgName = "dr.evil"; 224 CarVersion Version = CarVersion.forMajorAndMinorVersions(66, 6); 225 226 doReturn(Version) 227 .when(() -> CarPackageManagerService.getTargetCarVersion(mUserContext, pkgName)); 228 229 mockCallingUser(); 230 231 assertWithMessage("getTargetCarVersion(%s)", pkgName) 232 .that(mService.getTargetCarVersion(mUserHandle, pkgName)) 233 .isSameInstanceAs(Version); 234 } 235 236 @Test testGetTargetCarVersion_self_ok()237 public void testGetTargetCarVersion_self_ok() throws Exception { 238 String pkgName = "dr.evil"; 239 int myUid = Process.myUid(); 240 doReturn(myUid).when(mSpiedPackageManager).getPackageUidAsUser(eq(pkgName), anyInt()); 241 CarVersion Version = CarVersion.forMajorAndMinorVersions(66, 6); 242 243 doReturn(Version) 244 .when(() -> CarPackageManagerService.getTargetCarVersion(mUserContext, pkgName)); 245 246 mockCallingUser(); 247 248 assertWithMessage("getTargetCarVersion(%s)", pkgName) 249 .that(mService.getSelfTargetCarVersion(pkgName)).isSameInstanceAs(Version); 250 } 251 252 @Test testGetTargetCarVersion_self_wrongUid()253 public void testGetTargetCarVersion_self_wrongUid() throws Exception { 254 int myUid = Process.myUid(); 255 String pkgName = "dr.evil"; 256 doReturn(INVALID_UID).when(mSpiedPackageManager).getPackageUidAsUser(eq(pkgName), anyInt()); 257 CarVersion Version = CarVersion.forMajorAndMinorVersions(66, 6); 258 259 doReturn(Version) 260 .when(() -> CarPackageManagerService.getTargetCarVersion(mUserContext, pkgName)); 261 262 mockCallingUser(); 263 264 SecurityException e = assertThrows(SecurityException.class, 265 () -> mService.getSelfTargetCarVersion(pkgName)); 266 267 String msg = e.getMessage(); 268 assertWithMessage("exception message (pkg)").that(msg).contains(pkgName); 269 assertWithMessage("exception message (uid)").that(msg).contains(String.valueOf(myUid)); 270 } 271 272 @Test testGetTargetCarVersion_static_null()273 public void testGetTargetCarVersion_static_null() { 274 assertThrows(NullPointerException.class, 275 () -> CarPackageManagerService.getTargetCarVersion(mUserContext, null)); 276 } 277 278 @Test testGetTargetCarVersion_static_noPermission()279 public void testGetTargetCarVersion_static_noPermission() { 280 mockQueryPermission(/* granted= */ false); 281 282 assertThrows(SecurityException.class, 283 () -> CarPackageManagerService.getTargetCarVersion(mUserContext, "d.oh")); 284 } 285 286 287 @Test testGetTargetCarVersion_static_noApp()288 public void testGetTargetCarVersion_static_noApp() throws Exception { 289 mockQueryPermission(/* granted= */ true); 290 String causeMsg = mockGetApplicationInfoThrowsNotFound(mUserContext, "meaning.of.life"); 291 292 ServiceSpecificException e = assertThrows(ServiceSpecificException.class, 293 () -> CarPackageManagerService.getTargetCarVersion(mUserContext, 294 "meaning.of.life")); 295 assertWithMessage("exception code").that(e.errorCode).isEqualTo(ERROR_CODE_NO_PACKAGE); 296 assertWithMessage("exception msg").that(e.getMessage()).isEqualTo(causeMsg); 297 } 298 299 // No need to test all scenarios, as they're tested by CarVersionParserParseMethodTest 300 @Test testGetTargetCarVersion_static_ok()301 public void testGetTargetCarVersion_static_ok() throws Exception { 302 mockQueryPermission(/* granted= */ true); 303 ApplicationInfo info = mockGetApplicationInfo(mUserContext, "meaning.of.life"); 304 info.targetSdkVersion = 666; // Set to make sure it's not used 305 info.metaData = new Bundle(); 306 info.metaData.putString(MANIFEST_METADATA_TARGET_CAR_VERSION, "42:108"); 307 308 CarVersion actualVersion = CarPackageManagerService.getTargetCarVersion( 309 mUserContext, "meaning.of.life"); 310 311 assertWithMessage("static getTargetCarVersion()").that(actualVersion).isNotNull(); 312 assertWithMessage("major version").that(actualVersion.getMajorVersion()).isEqualTo(42); 313 assertWithMessage("minor version").that(actualVersion.getMinorVersion()).isEqualTo(108); 314 } 315 mockQueryPermission(boolean granted)316 private void mockQueryPermission(boolean granted) { 317 int result = android.content.pm.PackageManager.PERMISSION_DENIED; 318 if (granted) { 319 result = android.content.pm.PackageManager.PERMISSION_GRANTED; 320 } 321 doReturn(result).when(() -> ActivityManagerHelper.checkComponentPermission( 322 eq(QUERY_ALL_PACKAGES), anyInt(), anyInt(), anyBoolean())); 323 when(mUserContext.checkCallingOrSelfPermission(QUERY_ALL_PACKAGES)).thenReturn(result); 324 } 325 mockCallingUser()326 private void mockCallingUser() { 327 doReturn(mUserHandle).when(() -> Binder.getCallingUserHandle()); 328 } 329 mockGetApplicationInfoThrowsNotFound(Context context, String packageName)330 private static String mockGetApplicationInfoThrowsNotFound(Context context, String packageName) 331 throws NameNotFoundException { 332 String msg = "D'OH!"; 333 PackageManager pm = mock(PackageManager.class); 334 when(context.getPackageManager()).thenReturn(pm); 335 when(pm.getApplicationInfo(eq(packageName), any())) 336 .thenThrow(new NameNotFoundException(msg)); 337 return msg; 338 } 339 mockGetApplicationInfo(Context context, String packageName)340 private static ApplicationInfo mockGetApplicationInfo(Context context, String packageName) 341 throws NameNotFoundException { 342 ApplicationInfo info = new ApplicationInfo(); 343 PackageManager pm = mock(PackageManager.class); 344 when(context.getPackageManager()).thenReturn(pm); 345 when(pm.getApplicationInfo(eq(packageName), any())).thenReturn(info); 346 return info; 347 } 348 349 @Test registerBlockingUiCommandListenerThrowsException_withoutPermission()350 public void registerBlockingUiCommandListenerThrowsException_withoutPermission() { 351 applyPermission(PackageManager.PERMISSION_DENIED); 352 ICarBlockingUiCommandListener carBlockingUiCommandListener = 353 setupBlockingUiCommandListener(); 354 355 assertThrows(SecurityException.class, 356 () -> mService.registerBlockingUiCommandListener( 357 carBlockingUiCommandListener, DEFAULT_DISPLAY)); 358 } 359 360 @Test unregisterBlockingUiCommandListenerThrowsException_withoutPermission()361 public void unregisterBlockingUiCommandListenerThrowsException_withoutPermission() { 362 applyPermission(PackageManager.PERMISSION_DENIED); 363 ICarBlockingUiCommandListener carBlockingUiCommandListener = 364 setupBlockingUiCommandListener(); 365 366 assertThrows(SecurityException.class, 367 () -> mService.unregisterBlockingUiCommandListener(carBlockingUiCommandListener)); 368 } 369 370 @Test registerBlockingUiCommandListener()371 public void registerBlockingUiCommandListener() { 372 applyPermission(PackageManager.PERMISSION_GRANTED); 373 ICarBlockingUiCommandListener carBlockingUiCommandListener = 374 setupBlockingUiCommandListener(); 375 376 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener, DEFAULT_DISPLAY); 377 378 assertThat(mService.getCarBlockingUiCommandListenerRegisteredCallbacksForDisplay( 379 DEFAULT_DISPLAY)).isEqualTo(1); 380 } 381 382 @Test unregisterBlockingUiCommandListener()383 public void unregisterBlockingUiCommandListener() { 384 applyPermission(PackageManager.PERMISSION_GRANTED); 385 ICarBlockingUiCommandListener carBlockingUiCommandListener = 386 setupBlockingUiCommandListener(); 387 388 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener, DEFAULT_DISPLAY); 389 mService.unregisterBlockingUiCommandListener(carBlockingUiCommandListener); 390 391 assertThat(mService.getCarBlockingUiCommandListenerRegisteredCallbacksForDisplay( 392 DEFAULT_DISPLAY)).isEqualTo(0); 393 } 394 395 @Test registerBlockingUiCommandListener_sameListenerNotRegisteredAgain()396 public void registerBlockingUiCommandListener_sameListenerNotRegisteredAgain() { 397 applyPermission(PackageManager.PERMISSION_GRANTED); 398 ICarBlockingUiCommandListener carBlockingUiCommandListener = 399 setupBlockingUiCommandListener(); 400 401 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener, DEFAULT_DISPLAY); 402 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener, DEFAULT_DISPLAY); 403 404 assertThat(mService.getCarBlockingUiCommandListenerRegisteredCallbacksForDisplay( 405 DEFAULT_DISPLAY)).isEqualTo(1); 406 } 407 408 @Test registerBlockingUiCommandListener_registerMultipleListeners()409 public void registerBlockingUiCommandListener_registerMultipleListeners() { 410 applyPermission(PackageManager.PERMISSION_GRANTED); 411 ICarBlockingUiCommandListener carBlockingUiCommandListener1 = 412 setupBlockingUiCommandListener(); 413 ICarBlockingUiCommandListener carBlockingUiCommandListener2 = 414 setupBlockingUiCommandListener(); 415 416 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener1, DEFAULT_DISPLAY); 417 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener2, DEFAULT_DISPLAY); 418 419 assertThat(mService.getCarBlockingUiCommandListenerRegisteredCallbacksForDisplay( 420 DEFAULT_DISPLAY)).isEqualTo(2); 421 } 422 423 @Test registerMultipleListeners_finishBlockingUiInvoked()424 public void registerMultipleListeners_finishBlockingUiInvoked() 425 throws RemoteException { 426 applyPermission(PackageManager.PERMISSION_GRANTED); 427 ICarBlockingUiCommandListener carBlockingUiCommandListener1 = 428 setupBlockingUiCommandListener(); 429 ICarBlockingUiCommandListener carBlockingUiCommandListener2 = 430 setupBlockingUiCommandListener(); 431 ActivityManager.RunningTaskInfo taskInfo = createTask(); 432 433 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener1, DEFAULT_DISPLAY); 434 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener2, DEFAULT_DISPLAY); 435 mService.finishBlockingUi(taskInfo); 436 437 verify(carBlockingUiCommandListener1).finishBlockingUi(); 438 verify(carBlockingUiCommandListener2).finishBlockingUi(); 439 } 440 441 @Test registerListenerForOtherDisplay_finishBlockingUiNotInvoked()442 public void registerListenerForOtherDisplay_finishBlockingUiNotInvoked() 443 throws RemoteException { 444 applyPermission(PackageManager.PERMISSION_GRANTED); 445 ICarBlockingUiCommandListener carBlockingUiCommandListener = 446 setupBlockingUiCommandListener(); 447 ActivityManager.RunningTaskInfo taskInfo = createTask(); 448 int tempDisplayId = 1; 449 450 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener, tempDisplayId); 451 mService.finishBlockingUi(taskInfo); 452 453 verify(carBlockingUiCommandListener, times(0)).finishBlockingUi(); 454 } 455 456 @Test registerMultipleListenersForDifferentDisplay_finishBlockingUiInvokedForSomeDisplay()457 public void registerMultipleListenersForDifferentDisplay_finishBlockingUiInvokedForSomeDisplay() 458 throws RemoteException { 459 applyPermission(PackageManager.PERMISSION_GRANTED); 460 ICarBlockingUiCommandListener carBlockingUiCommandListener1 = 461 setupBlockingUiCommandListener(); 462 ICarBlockingUiCommandListener carBlockingUiCommandListener2 = 463 setupBlockingUiCommandListener(); 464 ICarBlockingUiCommandListener carBlockingUiCommandListener3 = 465 setupBlockingUiCommandListener(); 466 ActivityManager.RunningTaskInfo taskInfo = createTask(); 467 int tempDisplayId = 1; 468 469 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener1, DEFAULT_DISPLAY); 470 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener2, DEFAULT_DISPLAY); 471 mService.registerBlockingUiCommandListener(carBlockingUiCommandListener3, tempDisplayId); 472 mService.finishBlockingUi(taskInfo); 473 474 verify(carBlockingUiCommandListener1).finishBlockingUi(); 475 verify(carBlockingUiCommandListener2).finishBlockingUi(); 476 verify(carBlockingUiCommandListener3, times(0)).finishBlockingUi(); 477 } 478 createTask()479 private ActivityManager.RunningTaskInfo createTask() { 480 ActivityManager.RunningTaskInfo taskInfo = 481 new ActivityManager.RunningTaskInfo(); 482 taskInfo.taskId = 1; 483 taskInfo.displayId = DEFAULT_DISPLAY; 484 return taskInfo; 485 } 486 setupBlockingUiCommandListener()487 private ICarBlockingUiCommandListener setupBlockingUiCommandListener() { 488 ICarBlockingUiCommandListener carBlockingUiCommandListener = 489 mock(ICarBlockingUiCommandListener.class); 490 IBinder tempToken = new Binder(); 491 when(carBlockingUiCommandListener.asBinder()).thenReturn(tempToken); 492 return carBlockingUiCommandListener; 493 } 494 applyPermission(int permissionValue)495 private void applyPermission(int permissionValue) { 496 doReturn(permissionValue).when(mSpiedContext).checkCallingOrSelfPermission( 497 PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY); 498 } 499 } 500