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