1 /* 2 * Copyright (C) 2020 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 package android.car.hiddenapitest; 17 18 import static android.car.test.util.UserTestingHelper.clearUserLockCredentials; 19 import static android.car.test.util.UserTestingHelper.setMaxSupportedUsers; 20 import static android.car.test.util.UserTestingHelper.setUserLockCredentials; 21 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING; 22 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 23 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED; 24 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKING; 25 import static android.car.user.UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE; 26 27 import static com.google.common.truth.Truth.assertWithMessage; 28 29 import android.annotation.UserIdInt; 30 import android.app.ActivityManager; 31 import android.app.IActivityManager; 32 import android.car.Car; 33 import android.car.SyncResultCallback; 34 import android.car.testapi.BlockingUserLifecycleListener; 35 import android.car.user.CarUserManager; 36 import android.car.user.CarUserManager.UserLifecycleEvent; 37 import android.car.user.UserLifecycleEventFilter; 38 import android.car.user.UserSwitchRequest; 39 import android.car.user.UserSwitchResult; 40 import android.content.pm.UserInfo; 41 import android.os.Process; 42 import android.os.SystemProperties; 43 import android.os.UserHandle; 44 import android.os.UserManager; 45 import android.util.Log; 46 47 import androidx.test.filters.FlakyTest; 48 49 import com.android.compatibility.common.util.ApiTest; 50 51 import org.junit.AfterClass; 52 import org.junit.BeforeClass; 53 import org.junit.Ignore; 54 import org.junit.Test; 55 56 import java.util.List; 57 import java.util.Objects; 58 import java.util.concurrent.TimeUnit; 59 60 public final class CarUserManagerTest extends CarMultiUserTestBase { 61 62 private static final String TAG = CarUserManagerTest.class.getSimpleName(); 63 64 private static final int PIN = 2345; 65 66 private static final int START_TIMEOUT_MS = 20_000; 67 private static final int SWITCH_TIMEOUT_MS = 70_000; 68 private static final long TEST_WAIT_MS = 50; 69 private static final long TEST_TIMEOUT_MS = 10_000; 70 71 private static final int sMaxNumberUsersBefore = UserManager.getMaxSupportedUsers(); 72 private static boolean sChangedMaxNumberUsers; 73 74 @BeforeClass setupMaxNumberOfUsers()75 public static void setupMaxNumberOfUsers() { 76 int requiredUsers = 3; // system user, current user, 1 extra user 77 if (sMaxNumberUsersBefore < requiredUsers) { 78 sChangedMaxNumberUsers = true; 79 Log.i(TAG, "Increasing maximing number of users from " + sMaxNumberUsersBefore + " to " 80 + requiredUsers); 81 setMaxSupportedUsers(requiredUsers); 82 } 83 } 84 85 @AfterClass restoreMaxNumberOfUsers()86 public static void restoreMaxNumberOfUsers() { 87 if (sChangedMaxNumberUsers) { 88 Log.i(TAG, "Restoring maximum number of users to " + sMaxNumberUsersBefore); 89 setMaxSupportedUsers(sMaxNumberUsersBefore); 90 } 91 } 92 93 @Test 94 @ApiTest(apis = {"android.car.user.CarUserManager#createUser(String, int)"}) testCreateUser()95 public void testCreateUser() throws Exception { 96 UserInfo newUser = createUser("DaNewUserInTheBlock"); 97 assertWithMessage("(%s).isGuest()", newUser.toFullString()).that(newUser.isGuest()) 98 .isFalse(); 99 100 assertWithMessage("user(%s).name", newUser.toFullString()).that(newUser.name) 101 .contains("DaNewUserInTheBlock"); 102 103 // Make sure the user exists 104 UserInfo loadedUser = getUser(newUser.id); 105 assertUserInfo(newUser, loadedUser); 106 } 107 108 @Test 109 @ApiTest(apis = {"android.car.user.CarUserManager#createGuest(String)"}) testCreateGuest()110 public void testCreateGuest() throws Exception { 111 UserInfo newGuest = createGuest("DaNewGuestInTheBlock"); 112 assertWithMessage("(%s).isGuest()", newGuest.toFullString()).that(newGuest.isGuest()) 113 .isTrue(); 114 115 assertWithMessage("guest(%s).name ", newGuest.toFullString()).that(newGuest.name) 116 .contains("DaNewGuestInTheBlock"); 117 118 // Make sure the guest exists 119 UserInfo loadedGuest = getUser(newGuest.id); 120 assertUserInfo(newGuest, loadedGuest); 121 } 122 123 @Test 124 @ApiTest(apis = { 125 "android.car.user.CarUserManager#addListener(Executor,UserLifecycleListener)", 126 "android.car.user.CarUserManager#removeListener(UserLifecycleListener)" 127 }) testLifecycleMultipleListeners()128 public void testLifecycleMultipleListeners() throws Exception { 129 int newUserId = createUser("Test").id; 130 Car car2 = Car.createCar(getContext().getApplicationContext()); 131 CarUserManager mgr2 = (CarUserManager) car2.getCarManager(Car.CAR_USER_SERVICE); 132 CarUserManager mgr1 = mCarUserManager; 133 Log.d(TAG, "myUid=" + Process.myUid() + ",mgr1=" + mgr1 + ", mgr2=" + mgr2); 134 assertWithMessage("mgrs").that(mgr1).isNotSameInstanceAs(mgr2); 135 136 BlockingUserLifecycleListener listener1 = BlockingUserLifecycleListener 137 .forSpecificEvents() 138 .forUser(newUserId) 139 .setTimeout(START_TIMEOUT_MS) 140 .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_STARTING) 141 .build(); 142 BlockingUserLifecycleListener listener2 = BlockingUserLifecycleListener 143 .forSpecificEvents() 144 .forUser(newUserId) 145 .setTimeout(START_TIMEOUT_MS) 146 .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_STARTING) 147 .build(); 148 BlockingUserLifecycleListener listener3 = BlockingUserLifecycleListener 149 .forSpecificEvents() 150 .forUser(newUserId) 151 .setTimeout(START_TIMEOUT_MS) 152 .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_STARTING) 153 .build(); 154 UserLifecycleEventFilter unlockingEventFilter = new UserLifecycleEventFilter.Builder() 155 .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING).build(); 156 UserLifecycleEventFilter userFilter = new UserLifecycleEventFilter.Builder() 157 .addUser(UserHandle.of(newUserId)).build(); 158 159 Log.d(TAG, "registering listener1: " + listener1); 160 mgr1.addListener(Runnable::run, listener1); 161 Log.v(TAG, "ok"); 162 try { 163 Log.d(TAG, "registering listener2: " + listener2 + " with filter: " 164 + unlockingEventFilter); 165 mgr2.addListener(Runnable::run, unlockingEventFilter, listener2); 166 Log.v(TAG, "ok"); 167 Log.d(TAG, "registering listener3: " + listener3 + " with filter: " + userFilter); 168 mgr2.addListener(Runnable::run, userFilter, listener3); 169 Log.v(TAG, "ok"); 170 try { 171 IActivityManager am = ActivityManager.getService(); 172 Log.d(TAG, "Starting user " + newUserId); 173 am.startUserInBackground(newUserId); 174 Log.v(TAG, "ok"); 175 176 Log.d(TAG, "Waiting for events"); 177 List<UserLifecycleEvent> events1 = listener1.waitForEvents(); 178 Log.d(TAG, "events1: " + events1); 179 List<UserLifecycleEvent> events3 = listener3.waitForEvents(); 180 Log.d(TAG, "events2: " + events3); 181 assertStartUserEvent(events1, newUserId); 182 assertStartUserEvent(events3, newUserId); 183 assertWithMessage("all events received by listener %s", listener2) 184 .that(listener2.getAllReceivedEvents()).isEmpty(); 185 } finally { 186 Log.d(TAG, "unregistering listener2: " + listener2); 187 mgr2.removeListener(listener2); 188 Log.v(TAG, "ok"); 189 Log.d(TAG, "unregistering listener3: " + listener3); 190 mgr2.removeListener(listener3); 191 Log.v(TAG, "ok"); 192 } 193 } finally { 194 Log.d(TAG, "unregistering listener1: " + listener1); 195 mgr1.removeListener(listener1); 196 Log.v(TAG, "ok"); 197 } 198 } 199 assertStartUserEvent(List<UserLifecycleEvent> events, @UserIdInt int userId)200 private void assertStartUserEvent(List<UserLifecycleEvent> events, @UserIdInt int userId) { 201 assertWithMessage("events").that(events).hasSize(1); 202 203 UserLifecycleEvent event = events.get(0); 204 assertWithMessage("type").that(event.getEventType()) 205 .isEqualTo(USER_LIFECYCLE_EVENT_TYPE_STARTING); 206 assertWithMessage("user id on %s", event).that(event.getUserId()).isEqualTo(userId); 207 assertWithMessage("user handle on %s", event).that(event.getUserHandle().getIdentifier()) 208 .isEqualTo(userId); 209 assertWithMessage("previous user id on %s", event).that(event.getPreviousUserId()) 210 .isEqualTo(UserHandle.USER_NULL); 211 assertWithMessage("previous user handle on %s", event).that(event.getPreviousUserHandle()) 212 .isNull(); 213 } 214 215 /** 216 * Tests resume behavior when current user is ephemeral guest, a new guest user should be 217 * created and switched to. 218 */ 219 @Ignore("b/233164303") 220 @Test 221 @ApiTest(apis = { 222 "android.car.user.CarUserManager#USER_LIFECYCLE_EVENT_TYPE_UNLOCKED", 223 "android.car.user.CarUserManager#USER_LIFECYCLE_EVENT_TYPE_SWITCHING", 224 }) testGuestUserResumeToNewGuestUser()225 public void testGuestUserResumeToNewGuestUser() throws Exception { 226 // TODO(b/241837415): Create a host-side test and move this test there. 227 if (!isDeviceEmulator()) return; 228 229 // Create new guest user 230 UserInfo guestUser = createGuest(); 231 int guestUserId = guestUser.id; 232 233 // Wait for this user to be active 234 BlockingUserLifecycleListener listener1 = BlockingUserLifecycleListener 235 .forSpecificEvents() 236 .forUser(guestUserId) 237 .setTimeout(SWITCH_TIMEOUT_MS) 238 .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED) 239 .build(); 240 mCarUserManager.addListener(Runnable::run, listener1); 241 try { 242 switchUser(guestUserId); 243 listener1.waitForEvents(); 244 } finally { 245 mCarUserManager.removeListener(listener1); 246 } 247 248 BlockingUserLifecycleListener listener2 = BlockingUserLifecycleListener 249 .forSpecificEvents() 250 .forPreviousUser(guestUserId) 251 .setTimeout(SWITCH_TIMEOUT_MS) 252 .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_SWITCHING) 253 .build(); 254 // Make sure listener callback was executed in the proper thread 255 mCarUserManager.addListener(Runnable::run, listener2); 256 257 try { 258 // Emulate suspend to RAM 259 suspendToRamAndResume(); 260 UserLifecycleEvent event = listener2.waitForEvents().get(0); 261 262 int newGuestId = event.getUserId(); 263 264 assertWithMessage("userId on event %s", event).that(newGuestId) 265 .isNotEqualTo(guestUserId); 266 assertWithMessage("current user id").that(newGuestId).isEqualTo(getCurrentUserId()); 267 UserInfo newGuest = mUserManager.getUserInfo(newGuestId); 268 assertWithMessage("new user (%s) is a guest", newGuest.toFullString()) 269 .that(newGuest.isGuest()).isTrue(); 270 assertWithMessage("name of new guest(%s)", newGuest.toFullString()) 271 .that(newGuest.name).isNotEqualTo(guestUser.name); 272 } finally { 273 mCarUserManager.removeListener(listener2); 274 } 275 } 276 277 /** 278 * Tests resume behavior when current user is guest but with secured lock screen, 279 * resume to same guest user. 280 */ 281 @FlakyTest(bugId = 357135725) 282 @Test 283 @ApiTest(apis = { 284 "android.car.user.CarUserManager#USER_LIFECYCLE_EVENT_TYPE_UNLOCKED", 285 }) testSecuredGuestUserResumeToSameUser()286 public void testSecuredGuestUserResumeToSameUser() throws Exception { 287 // TODO(b/241837415): Create a host-side test and move this test there. 288 if (!isDeviceEmulator()) return; 289 290 // Create new guest user 291 UserInfo guestUser = createGuest(); 292 int guestUserId = guestUser.id; 293 294 // Wait for this user to be active 295 BlockingUserLifecycleListener listener = BlockingUserLifecycleListener 296 .forSpecificEvents() 297 .forUser(guestUserId) 298 .setTimeout(SWITCH_TIMEOUT_MS) 299 .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED) 300 .build(); 301 mCarUserManager.addListener(Runnable::run, listener); 302 303 try { 304 switchUser(guestUserId); 305 306 listener.waitForEvents(); 307 } finally { 308 mCarUserManager.removeListener(listener); 309 } 310 311 setUserLockCredentials(guestUserId, PIN); 312 try { 313 // Emulate suspend to RAM 314 suspendToRamAndResume(); 315 316 assertWithMessage("current user remains guest user (%s)", guestUser) 317 .that(getCurrentUserId()).isEqualTo(guestUserId); 318 } finally { 319 clearUserLockCredentials(guestUserId, PIN); 320 } 321 } 322 323 /** 324 * Tests resume behavior when current user is persistent user. 325 */ 326 @FlakyTest(bugId = 357135725) 327 @Test 328 @ApiTest(apis = { 329 "android.car.user.CarUserManager#USER_LIFECYCLE_EVENT_TYPE_UNLOCKED", 330 }) testPersistentUserResumeToUser()331 public void testPersistentUserResumeToUser() throws Exception { 332 // TODO(b/241837415): Create a host-side test and move this test there. 333 if (!isDeviceEmulator()) return; 334 335 int newUserId = createUser().id; 336 BlockingUserLifecycleListener listener = BlockingUserLifecycleListener 337 .forSpecificEvents() 338 .forUser(newUserId) 339 .setTimeout(SWITCH_TIMEOUT_MS) 340 .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED) 341 .build(); 342 mCarUserManager.addListener(Runnable::run, listener); 343 try { 344 switchUser(newUserId); 345 listener.waitForEvents(); 346 347 // Emulate suspend to RAM 348 suspendToRamAndResume(); 349 350 listener.waitForEvents(); 351 assertWithMessage("current user remains new user (%s)", newUserId) 352 .that(ActivityManager.getCurrentUser()).isEqualTo(newUserId); 353 } finally { 354 mCarUserManager.removeListener(listener); 355 } 356 } 357 358 @Test testSwitchUserUxRestrictionFailure()359 public void testSwitchUserUxRestrictionFailure() throws Exception { 360 SyncResultCallback<UserSwitchResult> userSwitchResultCallback = new SyncResultCallback<>(); 361 int initialUserId = getCurrentUserId(); 362 try { 363 Log.i(TAG, "Changing driving state to driving"); 364 executeShellCommand("cmd car_service emulate-driving-state drive"); 365 assertWithMessage("Waiting for driving state change").that( 366 waitForDrivingStateChanged("Current Driving State: 2", 367 TEST_TIMEOUT_MS)).isTrue(); 368 369 int newUserId = createUser().id; 370 mCarUserManager.switchUser( 371 new UserSwitchRequest.Builder(UserHandle.of(newUserId)).build(), Runnable::run, 372 userSwitchResultCallback); 373 UserSwitchResult userSwitchResult = userSwitchResultCallback.get( 374 DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 375 376 assertWithMessage("switchUser(%s) ", newUserId).that( 377 userSwitchResult.getStatus()).isEqualTo(STATUS_UX_RESTRICTION_FAILURE); 378 } finally { 379 Log.i(TAG, "Restoring driving state to parked"); 380 executeShellCommand("cmd car_service emulate-driving-state park"); 381 switchUser(initialUserId); 382 } 383 } 384 isDeviceEmulator()385 private static boolean isDeviceEmulator() { 386 return Objects.equals(SystemProperties.get("ro.product.system.device"), "generic"); 387 } 388 waitForDrivingStateChanged(String expected, long timeout)389 private boolean waitForDrivingStateChanged(String expected, long timeout) { 390 long start = System.currentTimeMillis(); 391 while (start + timeout > System.currentTimeMillis()) { 392 try { 393 String result = executeShellCommand( 394 "dumpsys car_service --services CarDrivingStateService"); 395 if (result.contains(expected)) { 396 return true; 397 } 398 Thread.sleep(TEST_WAIT_MS); 399 } catch (InterruptedException e) { 400 Log.e(TAG, "Test interrupted: " + e); 401 return false; 402 } catch (Exception e) { 403 Log.e(TAG, "executeCommand failed: " + e); 404 return false; 405 } 406 } 407 return false; 408 } 409 } 410