Home | History | Annotate | Download | only in am
      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 }