• 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 
22 import static com.google.common.truth.Truth.assertThat;
23 import static com.google.common.truth.Truth.assertWithMessage;
24 
25 import static org.junit.Assert.fail;
26 import static org.mockito.Mockito.when;
27 
28 import android.annotation.UserIdInt;
29 import android.app.ActivityManager;
30 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
31 import android.car.testapi.BlockingUserLifecycleListener;
32 import android.car.user.CarUserManager;
33 import android.content.BroadcastReceiver;
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.ContextWrapper;
37 import android.content.Intent;
38 import android.content.IntentFilter;
39 import android.content.ServiceConnection;
40 import android.content.pm.UserInfo;
41 import android.content.res.Resources;
42 import android.net.Uri;
43 import android.os.Handler;
44 import android.os.Looper;
45 import android.os.UserHandle;
46 import android.os.UserManager;
47 import android.util.Log;
48 
49 import androidx.annotation.Nullable;
50 import androidx.test.core.app.ApplicationProvider;
51 
52 import com.android.car.CarLocalServices;
53 import com.android.car.CarUxRestrictionsManagerService;
54 import com.android.car.hal.UserHalService;
55 import com.android.car.internal.common.CommonConstants.UserLifecycleEventType;
56 import com.android.car.user.CarUserService;
57 import com.android.internal.annotations.GuardedBy;
58 import com.android.internal.util.Preconditions;
59 
60 import org.junit.After;
61 import org.junit.Before;
62 import org.junit.Test;
63 import org.mockito.Mock;
64 
65 import java.util.ArrayList;
66 import java.util.HashMap;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.concurrent.CountDownLatch;
70 import java.util.concurrent.Executor;
71 import java.util.concurrent.TimeUnit;
72 
73 public final class VendorServiceControllerTest extends AbstractExtendedMockitoTestCase {
74     private static final String TAG = VendorServiceControllerTest.class.getSimpleName();
75 
76     // TODO(b/152069895): decrease value once refactored. In fact, it should not even use
77     // runWithScissors(), but only rely on CountdownLatches
78     private static final long DEFAULT_TIMEOUT_MS = 5_000;
79 
80     private static final int FG_USER_ID = 13;
81 
82     private static final String SERVICE_BIND_ALL_USERS_ASAP = "com.android.car/.AllUsersService";
83     private static final String SERVICE_BIND_FG_USER_UNLOCKED = "com.android.car/.ForegroundUsers";
84     private static final String SERVICE_BIND_FG_USER_POST_UNLOCKED =
85             "com.android.car/.ForegroundUsersPostUnlocked";
86     private static final String SERVICE_START_SYSTEM_UNLOCKED = "com.android.car/.SystemUser";
87 
88     private static final String[] FAKE_SERVICES = new String[] {
89             SERVICE_BIND_ALL_USERS_ASAP + "#bind=bind,user=all,trigger=asap",
90             SERVICE_BIND_FG_USER_UNLOCKED + "#bind=bind,user=foreground,trigger=userUnlocked",
91             SERVICE_BIND_FG_USER_POST_UNLOCKED
92                     + "#bind=bind,user=foreground,trigger=userPostUnlocked",
93             SERVICE_START_SYSTEM_UNLOCKED + "#bind=start,user=system,trigger=userUnlocked"
94     };
95 
96     @Mock
97     private Resources mResources;
98 
99     @Mock
100     private UserManager mUserManager;
101 
102     @Mock
103     private UserHalService mUserHal;
104 
105     @Mock
106     private CarUxRestrictionsManagerService mUxRestrictionService;
107 
108     @Mock
109     private CarPackageManagerService mCarPackageManagerService;
110 
111     private ServiceLauncherContext mContext;
112     private CarUserService mCarUserService;
113     private VendorServiceController mController;
114 
VendorServiceControllerTest()115     public VendorServiceControllerTest() {
116         super(VendorServiceController.TAG);
117     }
118 
119     @Override
onSessionBuilder(CustomMockitoSessionBuilder session)120     protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
121         session.spyStatic(ActivityManager.class);
122     }
123 
124     @Before
setUp()125     public void setUp() {
126         mContext = new ServiceLauncherContext(ApplicationProvider.getApplicationContext());
127 
128         mCarUserService = new CarUserService(mContext, mUserHal, mUserManager,
129                 /* maxRunningUsers= */ 2, mUxRestrictionService, mCarPackageManagerService);
130         CarLocalServices.addService(CarUserService.class, mCarUserService);
131 
132         mController = new VendorServiceController(mContext, Looper.getMainLooper());
133 
134         UserInfo persistentFgUser = new UserInfo(FG_USER_ID, "persistent user", /* flags= */ 0);
135         when(mUserManager.getUserInfo(FG_USER_ID)).thenReturn(persistentFgUser);
136 
137         when(mResources.getStringArray(com.android.car.R.array.config_earlyStartupServices))
138                 .thenReturn(FAKE_SERVICES);
139     }
140 
141     @After
tearDown()142     public void tearDown() {
143         CarLocalServices.removeServiceForTest(CarUserService.class);
144     }
145 
146     @Test
init_nothingConfigured()147     public void init_nothingConfigured() {
148         when(mResources.getStringArray(com.android.car.R.array.config_earlyStartupServices))
149                 .thenReturn(new String[0]);
150 
151         mController.init();
152 
153         mContext.verifyNoMoreServiceLaunches();
154     }
155 
156     @Test
init_systemUser()157     public void init_systemUser() throws Exception {
158         mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP);
159         mockGetCurrentUser(UserHandle.USER_SYSTEM);
160         mController.init();
161 
162         mContext.assertRecentBoundService(SERVICE_BIND_ALL_USERS_ASAP);
163         mContext.verifyNoMoreServiceLaunches();
164     }
165 
166     @Test
systemUserUnlocked()167     public void systemUserUnlocked() throws Exception {
168         mController.init();
169         mContext.reset();
170 
171         // TODO(b/152069895): must refactor this test because
172         // SERVICE_BIND_ALL_USERS_ASAP is bound twice (users 0 and 10)
173         mContext.expectServices(SERVICE_START_SYSTEM_UNLOCKED);
174 
175         // Unlock system user
176         mockUserUnlock(UserHandle.USER_SYSTEM);
177         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED,
178                 UserHandle.USER_SYSTEM);
179 
180         mContext.assertRecentStartedService(SERVICE_START_SYSTEM_UNLOCKED);
181         mContext.verifyNoMoreServiceLaunches();
182     }
183 
184     @Test
fgUserUnlocked()185     public void fgUserUnlocked() throws Exception {
186         mockGetCurrentUser(UserHandle.USER_SYSTEM);
187         mController.init();
188         mContext.reset();
189 
190         mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP, SERVICE_BIND_FG_USER_UNLOCKED,
191                 SERVICE_BIND_FG_USER_POST_UNLOCKED);
192 
193         // Switch user to foreground
194         mockGetCurrentUser(FG_USER_ID);
195         // TODO(b/155918094): Update this test,
196         UserInfo nullUser = new UserInfo(UserHandle.USER_NULL, "null user", /* flags= */ 0);
197         when(mUserManager.getUserInfo(UserHandle.USER_NULL)).thenReturn(nullUser);
198         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, FG_USER_ID);
199 
200         // Expect only services with ASAP trigger to be started
201         mContext.assertRecentBoundService(SERVICE_BIND_ALL_USERS_ASAP);
202         mContext.verifyNoMoreServiceLaunches();
203 
204         // Unlock foreground user
205         mockUserUnlock(FG_USER_ID);
206         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, FG_USER_ID);
207 
208         mContext.assertRecentBoundService(SERVICE_BIND_FG_USER_UNLOCKED);
209         mContext.verifyNoMoreServiceLaunches();
210 
211         // Send USER_POST_UNLOCKED event.
212         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, FG_USER_ID);
213 
214         mContext.assertRecentBoundService(SERVICE_BIND_FG_USER_POST_UNLOCKED);
215         mContext.verifyNoMoreServiceLaunches();
216     }
217 
218     @Test
packageChanged_attemptsRebind()219     public void packageChanged_attemptsRebind() throws Exception {
220         mockGetCurrentUser(UserHandle.USER_SYSTEM);
221         mController.init();
222         mContext.reset();
223 
224         mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP, SERVICE_BIND_FG_USER_UNLOCKED,
225                 SERVICE_BIND_FG_USER_POST_UNLOCKED);
226 
227         // Switch user to foreground
228         mockGetCurrentUser(FG_USER_ID);
229         UserInfo nullUser = new UserInfo(UserHandle.USER_NULL, "null user", /* flags= */ 0);
230         when(mUserManager.getUserInfo(UserHandle.USER_NULL)).thenReturn(nullUser);
231         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, FG_USER_ID);
232         mContext.assertRecentBoundService(SERVICE_BIND_ALL_USERS_ASAP);
233         mockUserUnlock(FG_USER_ID);
234 
235         // assertRecentBoundService() is important after every sendUserLifecycleEvent to ensure
236         // that the event has been handled completely.
237         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, FG_USER_ID);
238         mContext.assertRecentBoundService(SERVICE_BIND_FG_USER_UNLOCKED);
239         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, FG_USER_ID);
240         mContext.assertRecentBoundService(SERVICE_BIND_FG_USER_POST_UNLOCKED);
241 
242         Intent packageIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
243         int appId = 123;
244         packageIntent.setData(new Uri.Builder().path("Any package").build());
245         packageIntent.putExtra(Intent.EXTRA_UID, UserHandle.getUid(FG_USER_ID, appId));
246         mContext.mPackageChangeReceiver.onReceive(mContext, packageIntent);
247         runOnMainThreadAndWaitForIdle(() -> {});
248 
249         assertThat(((VendorServiceController.VendorServiceConnection)
250                 mContext.mBoundServiceToConnectionMap.get(SERVICE_BIND_FG_USER_POST_UNLOCKED))
251                 .isPendingRebind()).isTrue();
252         assertThat(((VendorServiceController.VendorServiceConnection)
253                 mContext.mBoundServiceToConnectionMap.get(SERVICE_BIND_FG_USER_UNLOCKED))
254                 .isPendingRebind()).isTrue();
255     }
256 
257     @Test
packageRemoved_unbindsTheService()258     public void packageRemoved_unbindsTheService() throws Exception {
259         mockGetCurrentUser(UserHandle.USER_SYSTEM);
260         mController.init();
261         mContext.reset();
262         mContext.expectServices(SERVICE_BIND_ALL_USERS_ASAP, SERVICE_BIND_FG_USER_UNLOCKED,
263                 SERVICE_BIND_FG_USER_POST_UNLOCKED);
264 
265         // Switch user to foreground
266         mockGetCurrentUser(FG_USER_ID);
267         UserInfo nullUser = new UserInfo(UserHandle.USER_NULL, "null user", /* flags= */ 0);
268         when(mUserManager.getUserInfo(UserHandle.USER_NULL)).thenReturn(nullUser);
269         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, FG_USER_ID);
270         mContext.assertRecentBoundService(SERVICE_BIND_ALL_USERS_ASAP);
271         mockUserUnlock(FG_USER_ID);
272 
273         // assertRecentBoundService() is important after every sendUserLifecycleEvent to ensure
274         // that the event has been handled completely.
275         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, FG_USER_ID);
276         mContext.assertRecentBoundService(SERVICE_BIND_FG_USER_UNLOCKED);
277         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, FG_USER_ID);
278         mContext.assertRecentBoundService(SERVICE_BIND_FG_USER_POST_UNLOCKED);
279 
280         Intent packageIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
281         int appId = 123;
282         packageIntent.setData(new Uri.Builder().path("com.android.car").build());
283         packageIntent.putExtra(Intent.EXTRA_UID, UserHandle.getUid(FG_USER_ID, appId));
284         mContext.mPackageChangeReceiver.onReceive(mContext, packageIntent);
285         runOnMainThreadAndWaitForIdle(() -> {});
286 
287         mContext.assertServiceNotBound(SERVICE_BIND_FG_USER_POST_UNLOCKED);
288         mContext.assertServiceNotBound(SERVICE_BIND_FG_USER_UNLOCKED);
289     }
290 
runOnMainThreadAndWaitForIdle(Runnable r)291     private static void runOnMainThreadAndWaitForIdle(Runnable r) {
292         Handler.getMain().runWithScissors(r, DEFAULT_TIMEOUT_MS);
293         // Run empty runnable to make sure that all posted handlers are done.
294         Handler.getMain().runWithScissors(() -> { }, DEFAULT_TIMEOUT_MS);
295     }
296 
mockUserUnlock(@serIdInt int userId)297     private void mockUserUnlock(@UserIdInt int userId) {
298         when(mUserManager.isUserUnlockingOrUnlocked(isUserHandle(userId))).thenReturn(true);
299         when(mUserManager.isUserUnlockingOrUnlocked(userId)).thenReturn(true);
300     }
301 
assertHasService(List<ComponentName> recentServices, String service, String action)302     private static void assertHasService(List<ComponentName> recentServices, String service,
303             String action) {
304         assertWithMessage("Number of recent %s services", action).that(recentServices)
305                 .hasSize(1);
306         assertWithMessage("Recent service").that(recentServices.get(0))
307                 .isEqualTo(ComponentName.unflattenFromString(service));
308         recentServices.clear();
309     }
310 
sendUserLifecycleEvent(@serLifecycleEventType int eventType, @UserIdInt int userId)311     private void sendUserLifecycleEvent(@UserLifecycleEventType int eventType,
312             @UserIdInt int userId) throws InterruptedException {
313         // Adding a blocking listener to ensure CarUserService event notification is completed
314         // before proceeding with test execution.
315         BlockingUserLifecycleListener blockingListener =
316                 BlockingUserLifecycleListener.forAnyEvent().build();
317         mCarUserService.addUserLifecycleListener(/* filter= */null, blockingListener);
318 
319         runOnMainThreadAndWaitForIdle(() -> mCarUserService.onUserLifecycleEvent(eventType,
320                 /* fromUserId= */ UserHandle.USER_NULL, userId));
321         blockingListener.waitForAnyEvent();
322     }
323 
324     /** Overrides framework behavior to succeed on binding/starting processes. */
325     public final class ServiceLauncherContext extends ContextWrapper {
326 
327         private final Object mLock = new Object();
328 
329         @GuardedBy("mLock")
330         private Map<ServiceConnection, ComponentName> mBoundConnectionToServiceMap =
331                 new HashMap<>();
332         @GuardedBy("mLock")
333         private List<ComponentName> mRecentBoundServices = new ArrayList<>();
334         @GuardedBy("mLock")
335         private List<ComponentName> mRecentStartedServices = new ArrayList<>();
336 
337         private final Map<String, CountDownLatch> mBoundLatches = new HashMap<>();
338         private final Map<String, CountDownLatch> mStartedLatches = new HashMap<>();
339         private final Map<String, ServiceConnection> mBoundServiceToConnectionMap =
340                 new HashMap<>();
341         private BroadcastReceiver mPackageChangeReceiver;
342 
ServiceLauncherContext(Context base)343         ServiceLauncherContext(Context base) {
344             super(base);
345         }
346 
347         @Override
createContextAsUser(UserHandle user, int flags)348         public Context createContextAsUser(UserHandle user, int flags) {
349             Log.v(TAG, "using same context for user " + user);
350             return this;
351         }
352 
353         @Override
startService(Intent service)354         public ComponentName startService(Intent service) {
355             synchronized (mLock) {
356                 mRecentStartedServices.add(service.getComponent());
357             }
358             countdown(mStartedLatches, service, "started");
359             return service.getComponent();
360         }
361 
362         @Override
bindService(Intent service, int flags, Executor executor, ServiceConnection conn)363         public boolean bindService(Intent service, int flags, Executor executor,
364                 ServiceConnection conn) {
365             synchronized (mLock) {
366                 mRecentBoundServices.add(service.getComponent());
367                 mBoundServiceToConnectionMap.put(service.getComponent().flattenToShortString(),
368                         conn);
369                 mBoundConnectionToServiceMap.put(conn, service.getComponent());
370                 Log.v(TAG, "Added service (" + service + ") to bound intents");
371             }
372             conn.onServiceConnected(service.getComponent(), null);
373             countdown(mBoundLatches, service, "bound");
374             return true;
375         }
376 
377         @Override
unbindService(ServiceConnection conn)378         public void unbindService(ServiceConnection conn) {
379             synchronized (mLock) {
380                 ComponentName serviceComponent = mBoundConnectionToServiceMap.get(conn);
381                 Log.v(TAG, "Remove service (" + serviceComponent + ") from bound services");
382                 mRecentBoundServices.remove(serviceComponent);
383                 mBoundServiceToConnectionMap.remove(serviceComponent.flattenToShortString());
384                 mBoundConnectionToServiceMap.remove(conn);
385             }
386         }
387 
388         @Override
getResources()389         public Resources getResources() {
390             return mResources;
391         }
392 
expectServices(String... services)393         private void expectServices(String... services) {
394             for (String service : services) {
395                 Log.v(TAG, "expecting service " + service);
396                 mBoundLatches.put(service, new CountDownLatch(1));
397                 mStartedLatches.put(service, new CountDownLatch(1));
398             }
399         }
400 
await(Map<String, CountDownLatch> latches, String service, String method)401         private void await(Map<String, CountDownLatch> latches, String service, String method)
402                 throws InterruptedException {
403             CountDownLatch latch = latches.get(service);
404             Preconditions.checkArgument(latch != null,
405                     "no latch set for %s - did you call expectBoundServices()?", service);
406             Log.d(TAG, "waiting " + DEFAULT_TIMEOUT_MS + "ms for " + method);
407             if (!latch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
408                 String errorMessage = method + " not called in " + DEFAULT_TIMEOUT_MS + "ms";
409                 Log.e(TAG, errorMessage);
410                 fail(errorMessage);
411             }
412             Log.v(TAG, "latch.await for service (" + service + ") and method ("
413                     + method + ") called fine");
414         }
415 
countdown(Map<String, CountDownLatch> latches, Intent service, String action)416         private void countdown(Map<String, CountDownLatch> latches, Intent service, String action) {
417             String serviceName = service.getComponent().flattenToShortString();
418             CountDownLatch latch = latches.get(serviceName);
419             if (latch == null) {
420                 Log.e(TAG, "unexpected service (" + serviceName + ") " + action + ". Expected only "
421                         + mBoundLatches.keySet());
422             } else {
423                 latch.countDown();
424                 Log.v(TAG, "latch.countDown for service (" + service + ") and action ("
425                         + action + ") called fine");
426             }
427         }
428 
assertRecentBoundService(String service)429         void assertRecentBoundService(String service) throws InterruptedException {
430             await(mBoundLatches, service, "bind()");
431             synchronized (mLock) {
432                 assertHasService(mRecentBoundServices, service, "bound");
433             }
434         }
435 
assertServiceNotBound(String service)436         void assertServiceNotBound(String service) throws InterruptedException {
437             synchronized (mLock) {
438                 assertWithMessage("Service is binded.").that(mRecentBoundServices)
439                         .doesNotContain(ComponentName.unflattenFromString(service));
440             }
441         }
442 
assertRecentStartedService(String service)443         void assertRecentStartedService(String service) throws InterruptedException {
444             await(mStartedLatches, service, "start()");
445             synchronized (mLock) {
446                 assertHasService(mRecentStartedServices, service, "started");
447             }
448         }
449 
verifyNoMoreServiceLaunches()450         void verifyNoMoreServiceLaunches() {
451             synchronized (mLock) {
452                 assertThat(mRecentStartedServices).isEmpty();
453                 assertThat(mRecentBoundServices).isEmpty();
454             }
455         }
456 
reset()457         void reset() {
458             synchronized (mLock) {
459                 mRecentStartedServices.clear();
460                 mRecentBoundServices.clear();
461                 mBoundServiceToConnectionMap.clear();
462                 mBoundConnectionToServiceMap.clear();
463             }
464         }
465 
466         @Override
getSystemService(String name)467         public Object getSystemService(String name) {
468             if (Context.USER_SERVICE.equals(name)) {
469                 return mUserManager;
470             }
471             return super.getSystemService(name);
472         }
473 
474         @Nullable
475         @Override
registerReceiverForAllUsers(@ullable BroadcastReceiver receiver, IntentFilter filter, @Nullable String broadcastPermission, @Nullable Handler scheduler, int flags)476         public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
477                 IntentFilter filter, @Nullable String broadcastPermission,
478                 @Nullable Handler scheduler, int flags) {
479             mPackageChangeReceiver = receiver;
480             return null;
481         }
482     }
483 }
484