1 /* 2 * Copyright (C) 2016 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.server.am; 18 19 import android.app.ActivityManager; 20 import android.app.IUserSwitchObserver; 21 import android.content.Context; 22 import android.content.IIntentReceiver; 23 import android.content.Intent; 24 import android.content.pm.UserInfo; 25 import android.os.Binder; 26 import android.os.Bundle; 27 import android.os.Handler; 28 import android.os.HandlerThread; 29 import android.os.IRemoteCallback; 30 import android.os.Looper; 31 import android.os.Message; 32 import android.os.RemoteException; 33 import android.os.UserManagerInternal; 34 import android.test.AndroidTestCase; 35 import android.test.suitebuilder.annotation.SmallTest; 36 import android.util.Log; 37 38 import com.android.server.pm.UserManagerService; 39 import com.android.server.wm.WindowManagerService; 40 41 import org.mockito.Mockito; 42 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Collections; 46 import java.util.HashSet; 47 import java.util.LinkedHashSet; 48 import java.util.List; 49 import java.util.Set; 50 51 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 52 import static com.android.server.am.ActivityManagerService.CONTINUE_USER_SWITCH_MSG; 53 import static com.android.server.am.ActivityManagerService.REPORT_LOCKED_BOOT_COMPLETE_MSG; 54 import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG; 55 import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG; 56 import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG; 57 import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG; 58 import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG; 59 import static org.mockito.ArgumentMatchers.anyString; 60 import static org.mockito.ArgumentMatchers.nullable; 61 import static org.mockito.Matchers.any; 62 import static org.mockito.Matchers.anyBoolean; 63 import static org.mockito.Matchers.anyInt; 64 import static org.mockito.Matchers.eq; 65 import static org.mockito.Mockito.doAnswer; 66 import static org.mockito.Mockito.doNothing; 67 import static org.mockito.Mockito.mock; 68 import static org.mockito.Mockito.never; 69 import static org.mockito.Mockito.times; 70 import static org.mockito.Mockito.when; 71 72 public class UserControllerTest extends AndroidTestCase { 73 private static final int TEST_USER_ID = 10; 74 private static final int NONEXIST_USER_ID = 2; 75 private static String TAG = UserControllerTest.class.getSimpleName(); 76 private UserController mUserController; 77 private TestInjector mInjector; 78 79 private static final List<String> START_FOREGROUND_USER_ACTIONS = 80 Arrays.asList( 81 Intent.ACTION_USER_STARTED, 82 Intent.ACTION_USER_SWITCHED, 83 Intent.ACTION_USER_STARTING); 84 85 private static final List<String> START_BACKGROUND_USER_ACTIONS = 86 Arrays.asList( 87 Intent.ACTION_USER_STARTED, 88 Intent.ACTION_LOCKED_BOOT_COMPLETED, 89 Intent.ACTION_USER_STARTING); 90 91 private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = 92 new HashSet<>(Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG, 93 SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG)); 94 95 private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = 96 new HashSet<>(Arrays.asList(SYSTEM_USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG)); 97 98 @Override 99 public void setUp() throws Exception { 100 super.setUp(); 101 System.setProperty("dexmaker.share_classloader", "true"); 102 mInjector = new TestInjector(getContext()); 103 mUserController = new UserController(mInjector); 104 setUpUser(TEST_USER_ID, 0); 105 } 106 107 @Override 108 protected void tearDown() throws Exception { 109 super.tearDown(); 110 mInjector.handlerThread.quit(); 111 } 112 113 @SmallTest 114 public void testStartUser_foreground() throws RemoteException { 115 mUserController.startUser(TEST_USER_ID, true /* foreground */); 116 Mockito.verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt()); 117 Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); 118 Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean()); 119 Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true); 120 Mockito.verify(mInjector.getActivityStackSupervisor()).setLockTaskModeLocked( 121 nullable(TaskRecord.class), 122 eq(ActivityManager.LOCK_TASK_MODE_NONE), 123 anyString(), 124 anyBoolean()); 125 startForegroundUserAssertions(); 126 } 127 128 @SmallTest 129 public void testStartUser_background() throws RemoteException { 130 mUserController.startUser(TEST_USER_ID, false /* foreground */); 131 Mockito.verify( 132 mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt()); 133 Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean()); 134 Mockito.verify(mInjector.getActivityStackSupervisor(), never()).setLockTaskModeLocked( 135 nullable(TaskRecord.class), 136 eq(ActivityManager.LOCK_TASK_MODE_NONE), 137 anyString(), 138 anyBoolean()); 139 startBackgroundUserAssertions(); 140 } 141 142 @SmallTest 143 public void testStartUserUIDisabled() throws RemoteException { 144 mUserController.mUserSwitchUiEnabled = false; 145 mUserController.startUser(TEST_USER_ID, true /* foreground */); 146 Mockito.verify(mInjector.getWindowManager(), never()) 147 .startFreezingScreen(anyInt(), anyInt()); 148 Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); 149 Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean()); 150 startForegroundUserAssertions(); 151 } 152 153 private void startUserAssertions( 154 List<String> expectedActions, Set<Integer> expectedMessageCodes) 155 throws RemoteException { 156 assertEquals(expectedActions, getActions(mInjector.sentIntents)); 157 Set<Integer> actualCodes = mInjector.handler.getMessageCodes(); 158 assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes); 159 } 160 161 private void startBackgroundUserAssertions() throws RemoteException { 162 startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES); 163 } 164 165 private void startForegroundUserAssertions() throws RemoteException { 166 startUserAssertions(START_FOREGROUND_USER_ACTIONS, START_FOREGROUND_USER_MESSAGE_CODES); 167 Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG); 168 assertNotNull(reportMsg); 169 UserState userState = (UserState) reportMsg.obj; 170 assertNotNull(userState); 171 assertEquals(TEST_USER_ID, userState.mHandle.getIdentifier()); 172 assertEquals("User must be in STATE_BOOTING", UserState.STATE_BOOTING, userState.state); 173 assertEquals("Unexpected old user id", 0, reportMsg.arg1); 174 assertEquals("Unexpected new user id", TEST_USER_ID, reportMsg.arg2); 175 } 176 177 @SmallTest 178 public void testFailedStartUserInForeground() throws RemoteException { 179 mUserController.mUserSwitchUiEnabled = false; 180 mUserController.startUserInForeground(NONEXIST_USER_ID); 181 Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean()); 182 Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(false); 183 } 184 185 @SmallTest 186 public void testDispatchUserSwitch() throws RemoteException { 187 // Prepare mock observer and register it 188 IUserSwitchObserver observer = mock(IUserSwitchObserver.class); 189 when(observer.asBinder()).thenReturn(new Binder()); 190 doAnswer(invocation -> { 191 IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1]; 192 callback.sendResult(null); 193 return null; 194 }).when(observer).onUserSwitching(anyInt(), any()); 195 mUserController.registerUserSwitchObserver(observer, "mock"); 196 // Start user -- this will update state of mUserController 197 mUserController.startUser(TEST_USER_ID, true); 198 Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG); 199 assertNotNull(reportMsg); 200 UserState userState = (UserState) reportMsg.obj; 201 int oldUserId = reportMsg.arg1; 202 int newUserId = reportMsg.arg2; 203 // Call dispatchUserSwitch and verify that observer was called only once 204 mInjector.handler.clearAllRecordedMessages(); 205 mUserController.dispatchUserSwitch(userState, oldUserId, newUserId); 206 Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any()); 207 Set<Integer> expectedCodes = Collections.singleton(CONTINUE_USER_SWITCH_MSG); 208 Set<Integer> actualCodes = mInjector.handler.getMessageCodes(); 209 assertEquals("Unexpected message sent", expectedCodes, actualCodes); 210 Message conMsg = mInjector.handler.getMessageForCode(CONTINUE_USER_SWITCH_MSG); 211 assertNotNull(conMsg); 212 userState = (UserState) conMsg.obj; 213 assertNotNull(userState); 214 assertEquals(TEST_USER_ID, userState.mHandle.getIdentifier()); 215 assertEquals("User must be in STATE_BOOTING", UserState.STATE_BOOTING, userState.state); 216 assertEquals("Unexpected old user id", 0, conMsg.arg1); 217 assertEquals("Unexpected new user id", TEST_USER_ID, conMsg.arg2); 218 } 219 220 @SmallTest 221 public void testDispatchUserSwitchBadReceiver() throws RemoteException { 222 // Prepare mock observer which doesn't notify the callback and register it 223 IUserSwitchObserver observer = mock(IUserSwitchObserver.class); 224 when(observer.asBinder()).thenReturn(new Binder()); 225 mUserController.registerUserSwitchObserver(observer, "mock"); 226 // Start user -- this will update state of mUserController 227 mUserController.startUser(TEST_USER_ID, true); 228 Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG); 229 assertNotNull(reportMsg); 230 UserState userState = (UserState) reportMsg.obj; 231 int oldUserId = reportMsg.arg1; 232 int newUserId = reportMsg.arg2; 233 // Call dispatchUserSwitch and verify that observer was called only once 234 mInjector.handler.clearAllRecordedMessages(); 235 mUserController.dispatchUserSwitch(userState, oldUserId, newUserId); 236 Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any()); 237 // Verify that CONTINUE_USER_SWITCH_MSG is not sent (triggers timeout) 238 Set<Integer> actualCodes = mInjector.handler.getMessageCodes(); 239 assertTrue("No messages should be sent", actualCodes.isEmpty()); 240 } 241 242 @SmallTest 243 public void testContinueUserSwitch() throws RemoteException { 244 // Start user -- this will update state of mUserController 245 mUserController.startUser(TEST_USER_ID, true); 246 Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG); 247 assertNotNull(reportMsg); 248 UserState userState = (UserState) reportMsg.obj; 249 int oldUserId = reportMsg.arg1; 250 int newUserId = reportMsg.arg2; 251 mInjector.handler.clearAllRecordedMessages(); 252 // Verify that continueUserSwitch worked as expected 253 mUserController.continueUserSwitch(userState, oldUserId, newUserId); 254 Mockito.verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen(); 255 continueUserSwitchAssertions(); 256 } 257 258 @SmallTest 259 public void testContinueUserSwitchUIDisabled() throws RemoteException { 260 mUserController.mUserSwitchUiEnabled = false; 261 // Start user -- this will update state of mUserController 262 mUserController.startUser(TEST_USER_ID, true); 263 Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG); 264 assertNotNull(reportMsg); 265 UserState userState = (UserState) reportMsg.obj; 266 int oldUserId = reportMsg.arg1; 267 int newUserId = reportMsg.arg2; 268 mInjector.handler.clearAllRecordedMessages(); 269 // Verify that continueUserSwitch worked as expected 270 mUserController.continueUserSwitch(userState, oldUserId, newUserId); 271 Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); 272 continueUserSwitchAssertions(); 273 } 274 275 private void continueUserSwitchAssertions() throws RemoteException { 276 Set<Integer> expectedCodes = Collections.singleton(REPORT_USER_SWITCH_COMPLETE_MSG); 277 Set<Integer> actualCodes = mInjector.handler.getMessageCodes(); 278 assertEquals("Unexpected message sent", expectedCodes, actualCodes); 279 Message msg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_COMPLETE_MSG); 280 assertNotNull(msg); 281 assertEquals("Unexpected userId", TEST_USER_ID, msg.arg1); 282 } 283 284 @SmallTest 285 public void testDispatchUserSwitchComplete() throws RemoteException { 286 // Prepare mock observer and register it 287 IUserSwitchObserver observer = mock(IUserSwitchObserver.class); 288 when(observer.asBinder()).thenReturn(new Binder()); 289 mUserController.registerUserSwitchObserver(observer, "mock"); 290 // Start user -- this will update state of mUserController 291 mUserController.startUser(TEST_USER_ID, true); 292 Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG); 293 assertNotNull(reportMsg); 294 int newUserId = reportMsg.arg2; 295 mInjector.handler.clearAllRecordedMessages(); 296 // Mockito can't reset only interactions, so just verify that this hasn't been 297 // called with 'false' until after dispatchUserSwitchComplete. 298 Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(false); 299 // Call dispatchUserSwitchComplete 300 mUserController.dispatchUserSwitchComplete(newUserId); 301 Mockito.verify(observer, times(1)).onUserSwitchComplete(anyInt()); 302 Mockito.verify(observer).onUserSwitchComplete(TEST_USER_ID); 303 Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false); 304 } 305 306 private void setUpUser(int userId, int flags) { 307 UserInfo userInfo = new UserInfo(userId, "User" + userId, flags); 308 when(mInjector.userManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo); 309 } 310 311 private static List<String> getActions(List<Intent> intents) { 312 List<String> result = new ArrayList<>(); 313 for (Intent intent : intents) { 314 result.add(intent.getAction()); 315 } 316 return result; 317 } 318 319 private static class TestInjector extends UserController.Injector { 320 final Object lock = new Object(); 321 TestHandler handler; 322 HandlerThread handlerThread; 323 UserManagerService userManagerMock; 324 UserManagerInternal userManagerInternalMock; 325 WindowManagerService windowManagerMock; 326 ActivityStackSupervisor activityStackSupervisor; 327 private Context mCtx; 328 List<Intent> sentIntents = new ArrayList<>(); 329 330 TestInjector(Context ctx) { 331 super(null); 332 mCtx = ctx; 333 handlerThread = new HandlerThread(TAG); 334 handlerThread.start(); 335 handler = new TestHandler(handlerThread.getLooper()); 336 userManagerMock = mock(UserManagerService.class); 337 userManagerInternalMock = mock(UserManagerInternal.class); 338 windowManagerMock = mock(WindowManagerService.class); 339 activityStackSupervisor = mock(ActivityStackSupervisor.class); 340 } 341 342 @Override 343 protected Object getLock() { 344 return lock; 345 } 346 347 @Override 348 protected Handler getHandler() { 349 return handler; 350 } 351 352 @Override 353 protected UserManagerService getUserManager() { 354 return userManagerMock; 355 } 356 357 @Override 358 UserManagerInternal getUserManagerInternal() { 359 return userManagerInternalMock; 360 } 361 362 @Override 363 protected Context getContext() { 364 return mCtx; 365 } 366 367 @Override 368 int checkCallingPermission(String permission) { 369 Log.i(TAG, "checkCallingPermission " + permission); 370 return PERMISSION_GRANTED; 371 } 372 373 @Override 374 WindowManagerService getWindowManager() { 375 return windowManagerMock; 376 } 377 378 @Override 379 void updateUserConfigurationLocked() { 380 Log.i(TAG, "updateUserConfigurationLocked"); 381 } 382 383 @Override 384 protected int broadcastIntentLocked(Intent intent, String resolvedType, 385 IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, 386 String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, 387 boolean sticky, int callingPid, int callingUid, int userId) { 388 Log.i(TAG, "broadcastIntentLocked " + intent); 389 sentIntents.add(intent); 390 return 0; 391 } 392 393 @Override 394 void startHomeActivityLocked(int userId, String reason) { 395 Log.i(TAG, "startHomeActivityLocked " + userId); 396 } 397 398 @Override 399 ActivityStackSupervisor getActivityStackSupervisor() { 400 return activityStackSupervisor; 401 } 402 } 403 404 private static class TestHandler extends Handler { 405 private final List<Message> mMessages = new ArrayList<>(); 406 407 TestHandler(Looper looper) { 408 super(looper); 409 } 410 411 Set<Integer> getMessageCodes() { 412 Set<Integer> result = new LinkedHashSet<>(); 413 for (Message msg : mMessages) { 414 result.add(msg.what); 415 } 416 return result; 417 } 418 419 Message getMessageForCode(int what) { 420 for (Message msg : mMessages) { 421 if (msg.what == what) { 422 return msg; 423 } 424 } 425 return null; 426 } 427 428 void clearAllRecordedMessages() { 429 mMessages.clear(); 430 } 431 432 @Override 433 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 434 Message copy = new Message(); 435 copy.copyFrom(msg); 436 mMessages.add(copy); 437 return super.sendMessageAtTime(msg, uptimeMillis); 438 } 439 } 440 }