1 /* 2 * Copyright (C) 2019 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.car.test.mocks.CarArgumentMatchers.isUserHandle; 20 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 22 23 import static org.junit.Assert.fail; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.Mockito.doReturn; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.annotation.UserIdInt; 30 import android.app.ActivityManager; 31 import android.car.hardware.power.CarPowerManager; 32 import android.car.hardware.power.ICarPowerStateListener; 33 import android.car.test.mocks.AbstractExtendedMockitoTestCase; 34 import android.car.testapi.BlockingUserLifecycleListener; 35 import android.car.user.CarUserManager; 36 import android.content.BroadcastReceiver; 37 import android.content.ComponentName; 38 import android.content.Context; 39 import android.content.ContextWrapper; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.ServiceConnection; 43 import android.content.pm.UserInfo; 44 import android.content.res.Resources; 45 import android.net.Uri; 46 import android.os.Handler; 47 import android.os.Looper; 48 import android.os.UserHandle; 49 import android.os.UserManager; 50 import android.util.Log; 51 52 import androidx.annotation.Nullable; 53 import androidx.test.core.app.ApplicationProvider; 54 55 import com.android.car.CarLocalServices; 56 import com.android.car.CarOccupantZoneService; 57 import com.android.car.CarUxRestrictionsManagerService; 58 import com.android.car.hal.UserHalService; 59 import com.android.car.internal.common.CommonConstants.UserLifecycleEventType; 60 import com.android.car.power.CarPowerManagementService; 61 import com.android.car.user.CarUserService; 62 import com.android.internal.annotations.GuardedBy; 63 import com.android.internal.util.Preconditions; 64 65 import org.junit.After; 66 import org.junit.Before; 67 import org.junit.Test; 68 import org.mockito.ArgumentCaptor; 69 import org.mockito.Mock; 70 71 import java.util.ArrayList; 72 import java.util.Arrays; 73 import java.util.HashMap; 74 import java.util.List; 75 import java.util.Map; 76 import java.util.concurrent.CountDownLatch; 77 import java.util.concurrent.Executor; 78 import java.util.concurrent.TimeUnit; 79 import java.util.stream.Collectors; 80 81 public final class VendorServiceControllerTest extends AbstractExtendedMockitoTestCase { 82 private static final String TAG = VendorServiceControllerTest.class.getSimpleName(); 83 84 // TODO(b/152069895): decrease value once refactored. In fact, it should not even use 85 // runWithScissors(), but only rely on CountdownLatches 86 private static final long DEFAULT_TIMEOUT_MS = 5_000; 87 88 private static final int FG_USER_ID = 13; 89 private static final int VISIBLE_BG_USER1_ID = 15; 90 private static final int VISIBLE_BG_USER2_ID = 17; 91 92 private static final String SERVICE_BIND_ALL_USERS_ASAP = "com.android.car/.AllUsersService"; 93 private static final String SERVICE_BIND_BG_VISIBLE_USER_ASAP = 94 "com.android.car/.BackgroundVisibleUsersAsap"; 95 private static final String SERVICE_BIND_FG_USER_UNLOCKED = "com.android.car/.ForegroundUsers"; 96 private static final String SERVICE_BIND_FG_USER_POST_UNLOCKED = 97 "com.android.car/.ForegroundUsersPostUnlocked"; 98 private static final String SERVICE_BIND_SYSTEM_USER_RESUME = 99 "com.android.car/.SystemUserBindOnResume"; 100 private static final String SERVICE_START_VISIBLE_USER_ASAP = 101 "com.android.car/.VisibleUsersAsap"; 102 private static final String SERVICE_START_VISIBLE_USER_UNLOCKED = 103 "com.android.car/.VisibleUsersUnlocked"; 104 private static final String SERVICE_START_SYSTEM_UNLOCKED = "com.android.car/.SystemUser"; 105 106 private static final String[] FAKE_SERVICES = new String[] { 107 SERVICE_BIND_ALL_USERS_ASAP + "#bind=bind,user=all,trigger=asap", 108 SERVICE_BIND_BG_VISIBLE_USER_ASAP + "#bind=bind,user=backgroundVisible,trigger=asap", 109 SERVICE_BIND_FG_USER_UNLOCKED + "#bind=bind,user=foreground,trigger=userUnlocked", 110 SERVICE_BIND_FG_USER_POST_UNLOCKED 111 + "#bind=bind,user=foreground,trigger=userPostUnlocked", 112 SERVICE_BIND_SYSTEM_USER_RESUME + "#bind=bind,user=system,trigger=resume", 113 SERVICE_START_VISIBLE_USER_ASAP + "#bind=start,user=visible,trigger=asap", 114 SERVICE_START_VISIBLE_USER_UNLOCKED + "#bind=start,user=visible,trigger=userUnlocked", 115 SERVICE_START_SYSTEM_UNLOCKED + "#bind=start,user=system,trigger=userUnlocked" 116 }; 117 118 @Mock 119 private Resources mResources; 120 121 @Mock 122 private UserManager mUserManager; 123 124 @Mock 125 private UserHalService mUserHal; 126 127 @Mock 128 private CarUxRestrictionsManagerService mUxRestrictionService; 129 130 @Mock 131 private CarPackageManagerService mCarPackageManagerService; 132 133 @Mock 134 private CarPowerManagementService mCarPowerManagementService; 135 136 @Mock 137 private CarOccupantZoneService mCarOccupantZoneService; 138 139 private ServiceLauncherContext mContext; 140 private CarUserService mCarUserService; 141 private VendorServiceController mController; 142 VendorServiceControllerTest()143 public VendorServiceControllerTest() { 144 super(VendorServiceController.TAG); 145 } 146 147 @Override onSessionBuilder(CustomMockitoSessionBuilder session)148 protected void onSessionBuilder(CustomMockitoSessionBuilder session) { 149 session.spyStatic(ActivityManager.class); 150 } 151 152 @Before setUp()153 public void setUp() throws Exception { 154 mContext = new ServiceLauncherContext(ApplicationProvider.getApplicationContext()); 155 156 mCarUserService = new CarUserService(mContext, mUserHal, mUserManager, 157 /* maxRunningUsers= */ 2, mUxRestrictionService, mCarPackageManagerService, 158 mCarOccupantZoneService); 159 spyOn(mCarUserService); 160 CarLocalServices.removeServiceForTest(CarUserService.class); 161 CarLocalServices.addService(CarUserService.class, mCarUserService); 162 CarLocalServices.removeServiceForTest(CarPowerManagementService.class); 163 CarLocalServices.addService(CarPowerManagementService.class, mCarPowerManagementService); 164 // No visible users by default. 165 doReturn(false).when(mCarUserService).isUserVisible(anyInt()); 166 167 mController = new VendorServiceController(mContext, Looper.getMainLooper()); 168 169 UserInfo persistentFgUser = new UserInfo(FG_USER_ID, "persistent user", /* flags= */ 0); 170 when(mUserManager.getUserInfo(FG_USER_ID)).thenReturn(persistentFgUser); 171 172 when(mResources.getStringArray(com.android.car.R.array.config_earlyStartupServices)) 173 .thenReturn(FAKE_SERVICES); 174 } 175 176 @After tearDown()177 public void tearDown() { 178 CarLocalServices.removeServiceForTest(CarUserService.class); 179 CarLocalServices.removeServiceForTest(CarPowerManagementService.class); 180 } 181 182 @Test init_nothingConfigured()183 public void init_nothingConfigured() { 184 when(mResources.getStringArray(com.android.car.R.array.config_earlyStartupServices)) 185 .thenReturn(new String[0]); 186 187 mController.init(); 188 189 mContext.expectNoMoreServiceLaunches(); 190 } 191 192 @Test init_systemUser()193 public void init_systemUser() throws Exception { 194 mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP); 195 mockGetCurrentUser(UserHandle.USER_SYSTEM); 196 mController.init(); 197 198 mContext.expectRecentBoundServices(SERVICE_BIND_ALL_USERS_ASAP); 199 mContext.expectNoMoreServiceLaunches(); 200 } 201 202 @Test systemUserUnlocked()203 public void systemUserUnlocked() throws Exception { 204 mController.init(); 205 mContext.reset(); 206 mContext.expectServices(SERVICE_START_SYSTEM_UNLOCKED, SERVICE_BIND_SYSTEM_USER_RESUME); 207 208 // Unlock system user 209 mockUserUnlock(UserHandle.USER_SYSTEM); 210 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, 211 UserHandle.USER_SYSTEM); 212 213 mContext.expectRecentStartedServices(SERVICE_START_SYSTEM_UNLOCKED); 214 // Note that user unlock also triggers `resume`. 215 mContext.expectRecentBoundServices(SERVICE_BIND_SYSTEM_USER_RESUME); 216 mContext.expectNoMoreServiceLaunches(); 217 } 218 219 @Test fgUserUnlocked()220 public void fgUserUnlocked() throws Exception { 221 mockGetCurrentUser(UserHandle.USER_SYSTEM); 222 mController.init(); 223 mContext.reset(); 224 225 mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP, SERVICE_BIND_FG_USER_UNLOCKED, 226 SERVICE_BIND_FG_USER_POST_UNLOCKED); 227 228 // Switch user to foreground 229 mockGetCurrentUser(FG_USER_ID); 230 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, FG_USER_ID); 231 232 // Expect only services with ASAP trigger to be started 233 mContext.expectRecentBoundServices(SERVICE_BIND_ALL_USERS_ASAP); 234 mContext.expectNoMoreServiceLaunches(); 235 236 // Unlock foreground user 237 mockUserUnlock(FG_USER_ID); 238 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, FG_USER_ID); 239 240 mContext.expectRecentBoundServices(SERVICE_BIND_FG_USER_UNLOCKED); 241 mContext.expectNoMoreServiceLaunches(); 242 243 // Send USER_POST_UNLOCKED event. 244 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, FG_USER_ID); 245 246 mContext.expectRecentBoundServices(SERVICE_BIND_FG_USER_POST_UNLOCKED); 247 mContext.expectNoMoreServiceLaunches(); 248 } 249 250 @Test testVisibleUsers()251 public void testVisibleUsers() throws Exception { 252 // No visible users yet. 253 mockGetCurrentUser(UserHandle.USER_SYSTEM); 254 mController.init(); 255 mContext.reset(); 256 257 // A background user becomes visible. 258 mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP, SERVICE_BIND_BG_VISIBLE_USER_ASAP, 259 SERVICE_START_VISIBLE_USER_ASAP); 260 mockIsUserVisible(VISIBLE_BG_USER1_ID, true); 261 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_VISIBLE, 262 VISIBLE_BG_USER1_ID); 263 264 mContext.expectRecentBoundServices(SERVICE_BIND_ALL_USERS_ASAP, 265 SERVICE_BIND_BG_VISIBLE_USER_ASAP); 266 mContext.expectRecentStartedServices(SERVICE_START_VISIBLE_USER_ASAP); 267 mContext.expectNoMoreServiceLaunches(); 268 269 // Unlock another visible background user. 270 mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP, SERVICE_BIND_BG_VISIBLE_USER_ASAP, 271 SERVICE_START_VISIBLE_USER_ASAP, SERVICE_START_VISIBLE_USER_UNLOCKED); 272 mockIsUserVisible(VISIBLE_BG_USER2_ID, true); 273 mockUserUnlock(VISIBLE_BG_USER2_ID); 274 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, 275 VISIBLE_BG_USER2_ID); 276 277 mContext.expectRecentBoundServices(SERVICE_BIND_ALL_USERS_ASAP, 278 SERVICE_BIND_BG_VISIBLE_USER_ASAP); 279 mContext.expectRecentStartedServices(SERVICE_START_VISIBLE_USER_ASAP, 280 SERVICE_START_VISIBLE_USER_UNLOCKED); 281 mContext.expectNoMoreServiceLaunches(); 282 283 // Switch user to foreground 284 mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP); 285 mockGetCurrentUser(FG_USER_ID); 286 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, FG_USER_ID); 287 288 mContext.expectRecentBoundServices(SERVICE_BIND_ALL_USERS_ASAP); 289 mContext.expectNoMoreServiceLaunches(); 290 // Switching to foreground user should not stop the service for other visible users. 291 mContext.expectNoUnboundOrStoppedServices(); 292 293 // Unlock foreground user. This triggers "visible", but not "backgroundVisible". 294 mContext.expectServices(SERVICE_BIND_FG_USER_UNLOCKED, SERVICE_START_VISIBLE_USER_ASAP, 295 SERVICE_START_VISIBLE_USER_UNLOCKED); 296 mockIsUserVisible(FG_USER_ID, true); 297 mockUserUnlock(FG_USER_ID); 298 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, FG_USER_ID); 299 300 mContext.expectRecentBoundServices(SERVICE_BIND_FG_USER_UNLOCKED); 301 mContext.expectRecentStartedServices(SERVICE_START_VISIBLE_USER_ASAP, 302 SERVICE_START_VISIBLE_USER_UNLOCKED); 303 mContext.expectNoMoreServiceLaunches(); 304 mContext.expectNoUnboundOrStoppedServices(); 305 306 // A background user becomes invisible. 307 mContext.expectServicesToUnbindOrStop(SERVICE_BIND_BG_VISIBLE_USER_ASAP, 308 SERVICE_START_VISIBLE_USER_ASAP, SERVICE_START_VISIBLE_USER_UNLOCKED); 309 mockIsUserVisible(VISIBLE_BG_USER2_ID, false); 310 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_INVISIBLE, 311 VISIBLE_BG_USER2_ID); 312 313 mContext.expectRecentUnboundOrStoppedServices(SERVICE_BIND_BG_VISIBLE_USER_ASAP, 314 SERVICE_START_VISIBLE_USER_ASAP, SERVICE_START_VISIBLE_USER_UNLOCKED); 315 mContext.expectNoMoreServiceLaunches(); 316 mContext.expectNoUnboundOrStoppedServices(); 317 } 318 319 @Test packageChanged_bindsTheService()320 public void packageChanged_bindsTheService() throws Exception { 321 mockGetCurrentUser(UserHandle.USER_SYSTEM); 322 mController.init(); 323 mContext.reset(); 324 325 mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP, SERVICE_BIND_FG_USER_UNLOCKED, 326 SERVICE_BIND_FG_USER_POST_UNLOCKED); 327 328 // Switch user to foreground 329 mockGetCurrentUser(FG_USER_ID); 330 UserInfo nullUser = new UserInfo(UserHandle.USER_NULL, "null user", /* flags= */ 0); 331 when(mUserManager.getUserInfo(UserHandle.USER_NULL)).thenReturn(nullUser); 332 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, FG_USER_ID); 333 mContext.expectRecentBoundServices(SERVICE_BIND_ALL_USERS_ASAP); 334 mockUserUnlock(FG_USER_ID); 335 mContext.expectServiceNotBound(SERVICE_BIND_FG_USER_POST_UNLOCKED); 336 mContext.expectServiceNotBound(SERVICE_BIND_FG_USER_UNLOCKED); 337 338 mContext.expectServices(SERVICE_BIND_FG_USER_UNLOCKED, SERVICE_BIND_FG_USER_POST_UNLOCKED); 339 340 Intent packageIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED); 341 int appId = 123; 342 packageIntent.setData(new Uri.Builder().path("com.android.car").build()); 343 packageIntent.putExtra(Intent.EXTRA_UID, UserHandle.getUid(FG_USER_ID, appId)); 344 mContext.mPackageChangeReceiver.onReceive(mContext, packageIntent); 345 runOnMainThreadAndWaitForIdle(() -> {}); 346 347 mContext.expectRecentBoundServices( 348 SERVICE_BIND_FG_USER_UNLOCKED, SERVICE_BIND_FG_USER_POST_UNLOCKED); 349 } 350 351 @Test packageRemoved_unbindsTheService()352 public void packageRemoved_unbindsTheService() throws Exception { 353 mockGetCurrentUser(UserHandle.USER_SYSTEM); 354 mController.init(); 355 mContext.reset(); 356 mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP, SERVICE_BIND_FG_USER_UNLOCKED, 357 SERVICE_BIND_FG_USER_POST_UNLOCKED); 358 359 // Switch user to foreground 360 mockGetCurrentUser(FG_USER_ID); 361 UserInfo nullUser = new UserInfo(UserHandle.USER_NULL, "null user", /* flags= */ 0); 362 when(mUserManager.getUserInfo(UserHandle.USER_NULL)).thenReturn(nullUser); 363 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, FG_USER_ID); 364 mContext.expectRecentBoundServices(SERVICE_BIND_ALL_USERS_ASAP); 365 mockUserUnlock(FG_USER_ID); 366 367 // assertRecentBoundService() is important after every sendUserLifecycleEvent to ensure 368 // that the event has been handled completely. 369 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, FG_USER_ID); 370 mContext.expectRecentBoundServices(SERVICE_BIND_FG_USER_UNLOCKED); 371 sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, FG_USER_ID); 372 mContext.expectRecentBoundServices(SERVICE_BIND_FG_USER_POST_UNLOCKED); 373 374 Intent packageIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED); 375 int appId = 123; 376 packageIntent.setData(new Uri.Builder().path("com.android.car").build()); 377 packageIntent.putExtra(Intent.EXTRA_UID, UserHandle.getUid(FG_USER_ID, appId)); 378 mContext.mPackageChangeReceiver.onReceive(mContext, packageIntent); 379 runOnMainThreadAndWaitForIdle(() -> {}); 380 381 mContext.expectServiceNotBound(SERVICE_BIND_FG_USER_POST_UNLOCKED); 382 mContext.expectServiceNotBound(SERVICE_BIND_FG_USER_UNLOCKED); 383 } 384 385 @Test powerStateResume_bindsSystemService()386 public void powerStateResume_bindsSystemService() throws Exception { 387 mockGetCurrentUser(UserHandle.USER_SYSTEM); 388 mController.init(); 389 ICarPowerStateListener listener = mockPowerStateListener(); 390 mContext.reset(); 391 mContext.expectServices(SERVICE_BIND_SYSTEM_USER_RESUME); 392 393 // Send power state resume event. 394 sendPowerStateChange(listener, CarPowerManager.STATE_SUSPEND_EXIT); 395 396 mContext.expectRecentBoundServices(SERVICE_BIND_SYSTEM_USER_RESUME); 397 mContext.expectNoMoreServiceLaunches(); 398 } 399 400 /** Returns the power state listener captured during initialization. */ mockPowerStateListener()401 private ICarPowerStateListener mockPowerStateListener() { 402 ArgumentCaptor<ICarPowerStateListener> listenerCaptor = 403 ArgumentCaptor.forClass(ICarPowerStateListener.class); 404 verify(mCarPowerManagementService).registerListener(listenerCaptor.capture()); 405 return listenerCaptor.getValue(); 406 } 407 408 /** Sends the given power {@code state} to the {@code listener}. */ sendPowerStateChange(ICarPowerStateListener listener, int state)409 private void sendPowerStateChange(ICarPowerStateListener listener, int state) throws Exception { 410 listener.onStateChanged(state, /* expirationTimeMs= */ 3000L); 411 } 412 413 // TODO: Replace this with AndroidMockitoHelper#mockUmIsUserUnlockingOrUnlocked 414 // We need to figure out why we get WrongTypeOfReturnValue error with when()..thenReturn(). mockUserUnlock(@serIdInt int userId)415 private void mockUserUnlock(@UserIdInt int userId) { 416 doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(isUserHandle(userId)); 417 doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(userId); 418 } 419 mockIsUserVisible(@serIdInt int userId, boolean visible)420 private void mockIsUserVisible(@UserIdInt int userId, boolean visible) throws Exception { 421 doReturn(visible).when(mCarUserService).isUserVisible(userId); 422 } 423 runOnMainThreadAndWaitForIdle(Runnable r)424 private static void runOnMainThreadAndWaitForIdle(Runnable r) { 425 Handler.getMain().runWithScissors(r, DEFAULT_TIMEOUT_MS); 426 // Run empty runnable to make sure that all posted handlers are done. 427 Handler.getMain().runWithScissors(() -> { }, DEFAULT_TIMEOUT_MS); 428 } 429 expectHasServices(List<ComponentName> recentServices, String action, String... services)430 private void expectHasServices(List<ComponentName> recentServices, String action, 431 String... services) { 432 expectWithMessage("Recent %s services", action).that(recentServices) 433 .containsExactlyElementsIn(Arrays.stream(services) 434 .map(ComponentName::unflattenFromString).collect(Collectors.toList())); 435 recentServices.clear(); 436 } 437 sendUserLifecycleEvent(@serLifecycleEventType int eventType, @UserIdInt int userId)438 private void sendUserLifecycleEvent(@UserLifecycleEventType int eventType, 439 @UserIdInt int userId) throws InterruptedException { 440 // Adding a blocking listener to ensure CarUserService event notification is completed 441 // before proceeding with test execution. 442 BlockingUserLifecycleListener blockingListener = 443 BlockingUserLifecycleListener.forAnyEvent().build(); 444 mCarUserService.addUserLifecycleListener(/* filter= */null, blockingListener); 445 446 runOnMainThreadAndWaitForIdle(() -> mCarUserService.onUserLifecycleEvent(eventType, 447 /* fromUserId= */ UserHandle.USER_NULL, userId)); 448 blockingListener.waitForAnyEvent(); 449 } 450 451 /** Overrides framework behavior to succeed on binding/starting processes. */ 452 public final class ServiceLauncherContext extends ContextWrapper { 453 454 private final Object mLock = new Object(); 455 456 @GuardedBy("mLock") 457 private Map<ServiceConnection, ComponentName> mBoundConnectionToServiceMap = 458 new HashMap<>(); 459 @GuardedBy("mLock") 460 private List<ComponentName> mRecentBoundServices = new ArrayList<>(); 461 @GuardedBy("mLock") 462 private List<ComponentName> mRecentStartedServices = new ArrayList<>(); 463 @GuardedBy("mLock") 464 private List<ComponentName> mUnboundOrStoppedServices = new ArrayList<>(); 465 466 private final Map<String, CountDownLatch> mBoundLatches = new HashMap<>(); 467 private final Map<String, CountDownLatch> mStartedLatches = new HashMap<>(); 468 private final Map<String, CountDownLatch> mUnboundOrStoppedLatches = new HashMap<>(); 469 private final Map<String, ServiceConnection> mBoundServiceToConnectionMap = 470 new HashMap<>(); 471 private BroadcastReceiver mPackageChangeReceiver; 472 ServiceLauncherContext(Context base)473 ServiceLauncherContext(Context base) { 474 super(base); 475 } 476 477 @Override createContextAsUser(UserHandle user, int flags)478 public Context createContextAsUser(UserHandle user, int flags) { 479 Log.v(TAG, "using same context for user " + user); 480 return this; 481 } 482 483 @Override startService(Intent service)484 public ComponentName startService(Intent service) { 485 ComponentName serviceComponent = service.getComponent(); 486 synchronized (mLock) { 487 mRecentStartedServices.add(serviceComponent); 488 mUnboundOrStoppedServices.remove(serviceComponent); 489 } 490 Log.v(TAG, "Started service (" + serviceComponent + ")"); 491 countdown(mStartedLatches, serviceComponent, "started"); 492 return service.getComponent(); 493 } 494 495 @Override stopService(Intent intent)496 public boolean stopService(Intent intent) { 497 ComponentName serviceComponent = intent.getComponent(); 498 Log.v(TAG, "Remove service (" + serviceComponent + ") from started services"); 499 synchronized (mLock) { 500 mRecentStartedServices.remove(serviceComponent); 501 mUnboundOrStoppedServices.add(serviceComponent); 502 } 503 countdown(mUnboundOrStoppedLatches, serviceComponent, "stopped"); 504 return true; 505 } 506 507 @Override bindService(Intent service, int flags, Executor executor, ServiceConnection conn)508 public boolean bindService(Intent service, int flags, Executor executor, 509 ServiceConnection conn) { 510 ComponentName serviceComponent = service.getComponent(); 511 synchronized (mLock) { 512 mUnboundOrStoppedServices.remove(serviceComponent); 513 mRecentBoundServices.add(serviceComponent); 514 mBoundServiceToConnectionMap.put(serviceComponent.flattenToShortString(), conn); 515 mBoundConnectionToServiceMap.put(conn, serviceComponent); 516 } 517 Log.v(TAG, "Added service (" + serviceComponent + ") to bound intents"); 518 conn.onServiceConnected(serviceComponent, null); 519 countdown(mBoundLatches, serviceComponent, "bound"); 520 return true; 521 } 522 523 @Override unbindService(ServiceConnection conn)524 public void unbindService(ServiceConnection conn) { 525 ComponentName serviceComponent; 526 synchronized (mLock) { 527 serviceComponent = mBoundConnectionToServiceMap.get(conn); 528 Log.v(TAG, "Remove service (" + serviceComponent + ") from bound services"); 529 mRecentBoundServices.remove(serviceComponent); 530 mBoundServiceToConnectionMap.remove(serviceComponent.flattenToShortString()); 531 mBoundConnectionToServiceMap.remove(conn); 532 mUnboundOrStoppedServices.add(serviceComponent); 533 } 534 countdown(mUnboundOrStoppedLatches, serviceComponent, "unbound"); 535 } 536 537 @Override getResources()538 public Resources getResources() { 539 return mResources; 540 } 541 expectServices(String... services)542 private void expectServices(String... services) { 543 for (String service : services) { 544 Log.v(TAG, "expecting service " + service); 545 mBoundLatches.put(service, new CountDownLatch(1)); 546 mStartedLatches.put(service, new CountDownLatch(1)); 547 } 548 } 549 expectServicesToUnbindOrStop(String... services)550 private void expectServicesToUnbindOrStop(String... services) { 551 for (String service : services) { 552 Log.v(TAG, "expecting service " + service); 553 mUnboundOrStoppedLatches.put(service, new CountDownLatch(1)); 554 } 555 } 556 await(Map<String, CountDownLatch> latches, String method, String... services)557 private void await(Map<String, CountDownLatch> latches, String method, String... services) 558 throws InterruptedException { 559 for (String service : services) { 560 CountDownLatch latch = latches.get(service); 561 Preconditions.checkArgument(latch != null, 562 "no latch set for %s - did you call expectBoundServices()?", service); 563 Log.d(TAG, "waiting " + DEFAULT_TIMEOUT_MS + "ms for " + method); 564 if (!latch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 565 String errorMessage = method + " not called in " + DEFAULT_TIMEOUT_MS + "ms"; 566 Log.e(TAG, errorMessage); 567 fail(errorMessage); 568 } 569 } 570 Log.v(TAG, "latch.await for services (" + Arrays.toString(services) 571 + ") and method (" + method + ") called fine"); 572 } 573 countdown(Map<String, CountDownLatch> latches, ComponentName serviceComponent, String action)574 private void countdown(Map<String, CountDownLatch> latches, ComponentName serviceComponent, 575 String action) { 576 String serviceName = serviceComponent.flattenToShortString(); 577 CountDownLatch latch = latches.get(serviceName); 578 if (latch == null) { 579 Log.e(TAG, "unexpected service (" + serviceName + ") " + action + ". Expected only " 580 + mBoundLatches.keySet()); 581 } else { 582 latch.countDown(); 583 Log.v(TAG, "latch.countDown for service (" + serviceName + ") and action (" 584 + action + ") called fine"); 585 } 586 } 587 expectRecentBoundServices(String... services)588 void expectRecentBoundServices(String... services) throws InterruptedException { 589 await(mBoundLatches, "bind()", services); 590 synchronized (mLock) { 591 expectHasServices(mRecentBoundServices, "bound", services); 592 } 593 } 594 expectServiceNotBound(String service)595 void expectServiceNotBound(String service) throws InterruptedException { 596 synchronized (mLock) { 597 expectWithMessage("Service is bound.").that(mRecentBoundServices) 598 .doesNotContain(ComponentName.unflattenFromString(service)); 599 } 600 } 601 expectRecentStartedServices(String... services)602 void expectRecentStartedServices(String... services) throws InterruptedException { 603 await(mStartedLatches, "start()", services); 604 synchronized (mLock) { 605 expectHasServices(mRecentStartedServices, "started", services); 606 } 607 } 608 expectRecentUnboundOrStoppedServices(String... services)609 void expectRecentUnboundOrStoppedServices(String... services) throws InterruptedException { 610 await(mUnboundOrStoppedLatches, "unbind() or stop()", services); 611 synchronized (mLock) { 612 expectHasServices(mUnboundOrStoppedServices, "unbound or stopped", services); 613 } 614 } 615 expectNoUnboundOrStoppedServices()616 void expectNoUnboundOrStoppedServices() { 617 synchronized (mLock) { 618 expectWithMessage("Unbounded or stopped services") 619 .that(mUnboundOrStoppedServices).isEmpty(); 620 } 621 } 622 expectNoMoreServiceLaunches()623 void expectNoMoreServiceLaunches() { 624 synchronized (mLock) { 625 expectWithMessage("Recently started services").that(mRecentStartedServices) 626 .isEmpty(); 627 expectWithMessage("Recently bound services").that(mRecentBoundServices).isEmpty(); 628 } 629 } 630 reset()631 void reset() { 632 synchronized (mLock) { 633 mRecentStartedServices.clear(); 634 mRecentBoundServices.clear(); 635 mBoundServiceToConnectionMap.clear(); 636 mBoundConnectionToServiceMap.clear(); 637 mUnboundOrStoppedServices.clear(); 638 } 639 } 640 641 @Override getSystemService(String name)642 public Object getSystemService(String name) { 643 if (Context.USER_SERVICE.equals(name)) { 644 return mUserManager; 645 } 646 return super.getSystemService(name); 647 } 648 649 @Nullable 650 @Override registerReceiverForAllUsers(@ullable BroadcastReceiver receiver, IntentFilter filter, @Nullable String broadcastPermission, @Nullable Handler scheduler, int flags)651 public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver, 652 IntentFilter filter, @Nullable String broadcastPermission, 653 @Nullable Handler scheduler, int flags) { 654 mPackageChangeReceiver = receiver; 655 return null; 656 } 657 } 658 } 659