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 import static android.os.Process.INVALID_UID; 24 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 26 27 import static com.google.common.truth.Truth.assertThat; 28 import static com.google.common.truth.Truth.assertWithMessage; 29 30 import static org.junit.Assert.assertThrows; 31 import static org.mockito.ArgumentMatchers.any; 32 import static org.mockito.ArgumentMatchers.anyBoolean; 33 import static org.mockito.ArgumentMatchers.anyInt; 34 import static org.mockito.ArgumentMatchers.eq; 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() throws Exception { 230 String pkgName = "dr.evil"; 231 int myUid = Process.myUid(); 232 doReturn(myUid).when(mSpiedPackageManager).getPackageUidAsUser(eq(pkgName), anyInt()); 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() throws Exception { 246 int myUid = Process.myUid(); 247 String pkgName = "dr.evil"; 248 doReturn(INVALID_UID).when(mSpiedPackageManager).getPackageUidAsUser(eq(pkgName), anyInt()); 249 CarVersion Version = CarVersion.forMajorAndMinorVersions(66, 6); 250 251 doReturn(Version) 252 .when(() -> CarPackageManagerService.getTargetCarVersion(mUserContext, pkgName)); 253 254 mockCallingUser(); 255 256 SecurityException e = assertThrows(SecurityException.class, 257 () -> mService.getSelfTargetCarVersion(pkgName)); 258 259 String msg = e.getMessage(); 260 assertWithMessage("exception message (pkg)").that(msg).contains(pkgName); 261 assertWithMessage("exception message (uid)").that(msg).contains(String.valueOf(myUid)); 262 } 263 264 @Test testGetTargetCarVersion_static_null()265 public void testGetTargetCarVersion_static_null() { 266 assertThrows(NullPointerException.class, 267 () -> CarPackageManagerService.getTargetCarVersion(mUserContext, null)); 268 } 269 270 @Test testGetTargetCarVersion_static_noPermission()271 public void testGetTargetCarVersion_static_noPermission() { 272 mockQueryPermission(/* granted= */ false); 273 274 assertThrows(SecurityException.class, 275 () -> CarPackageManagerService.getTargetCarVersion(mUserContext, "d.oh")); 276 } 277 278 279 @Test testGetTargetCarVersion_static_noApp()280 public void testGetTargetCarVersion_static_noApp() throws Exception { 281 mockQueryPermission(/* granted= */ true); 282 String causeMsg = mockGetApplicationInfoThrowsNotFound(mUserContext, "meaning.of.life"); 283 284 ServiceSpecificException e = assertThrows(ServiceSpecificException.class, 285 () -> CarPackageManagerService.getTargetCarVersion(mUserContext, 286 "meaning.of.life")); 287 assertWithMessage("exception code").that(e.errorCode).isEqualTo(ERROR_CODE_NO_PACKAGE); 288 assertWithMessage("exception msg").that(e.getMessage()).isEqualTo(causeMsg); 289 } 290 291 // No need to test all scenarios, as they're tested by CarVersionParserParseMethodTest 292 @Test testGetTargetCarVersion_static_ok()293 public void testGetTargetCarVersion_static_ok() throws Exception { 294 mockQueryPermission(/* granted= */ true); 295 ApplicationInfo info = mockGetApplicationInfo(mUserContext, "meaning.of.life"); 296 info.targetSdkVersion = 666; // Set to make sure it's not used 297 info.metaData = new Bundle(); 298 info.metaData.putString(MANIFEST_METADATA_TARGET_CAR_VERSION, "42:108"); 299 300 CarVersion actualVersion = CarPackageManagerService.getTargetCarVersion( 301 mUserContext, "meaning.of.life"); 302 303 assertWithMessage("static getTargetCarVersion()").that(actualVersion).isNotNull(); 304 assertWithMessage("major version").that(actualVersion.getMajorVersion()).isEqualTo(42); 305 assertWithMessage("minor version").that(actualVersion.getMinorVersion()).isEqualTo(108); 306 } 307 mockQueryPermission(boolean granted)308 private void mockQueryPermission(boolean granted) { 309 int result = android.content.pm.PackageManager.PERMISSION_DENIED; 310 if (granted) { 311 result = android.content.pm.PackageManager.PERMISSION_GRANTED; 312 } 313 doReturn(result).when(() -> ActivityManagerHelper.checkComponentPermission( 314 eq(QUERY_ALL_PACKAGES), anyInt(), anyInt(), anyBoolean())); 315 when(mUserContext.checkCallingOrSelfPermission(QUERY_ALL_PACKAGES)).thenReturn(result); 316 } 317 mockCallingUser()318 private void mockCallingUser() { 319 doReturn(mUserHandle).when(() -> Binder.getCallingUserHandle()); 320 } 321 mockGetApplicationInfoThrowsNotFound(Context context, String packageName)322 private static String mockGetApplicationInfoThrowsNotFound(Context context, String packageName) 323 throws NameNotFoundException { 324 String msg = "D'OH!"; 325 PackageManager pm = mock(PackageManager.class); 326 when(context.getPackageManager()).thenReturn(pm); 327 when(pm.getApplicationInfo(eq(packageName), any())) 328 .thenThrow(new NameNotFoundException(msg)); 329 return msg; 330 } 331 mockGetApplicationInfo(Context context, String packageName)332 private static ApplicationInfo mockGetApplicationInfo(Context context, String packageName) 333 throws NameNotFoundException { 334 ApplicationInfo info = new ApplicationInfo(); 335 PackageManager pm = mock(PackageManager.class); 336 when(context.getPackageManager()).thenReturn(pm); 337 when(pm.getApplicationInfo(eq(packageName), any())).thenReturn(info); 338 return info; 339 } 340 } 341