• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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