UserControllerTest.java revision 5c2cf03f5f8e26f715931370004d21ee217edcde
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
17package com.android.server.am;
18
19import android.app.ActivityManager;
20import android.app.IUserSwitchObserver;
21import android.content.Context;
22import android.content.IIntentReceiver;
23import android.content.Intent;
24import android.content.pm.UserInfo;
25import android.os.Binder;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.HandlerThread;
29import android.os.IRemoteCallback;
30import android.os.Looper;
31import android.os.Message;
32import android.os.RemoteException;
33import android.os.UserManagerInternal;
34import android.test.AndroidTestCase;
35import android.test.suitebuilder.annotation.SmallTest;
36import android.util.Log;
37
38import com.android.server.pm.UserManagerService;
39import com.android.server.wm.WindowManagerService;
40
41import org.mockito.Mockito;
42
43import java.util.ArrayList;
44import java.util.Arrays;
45import java.util.Collections;
46import java.util.HashSet;
47import java.util.LinkedHashSet;
48import java.util.List;
49import java.util.Set;
50
51import static android.content.pm.PackageManager.PERMISSION_GRANTED;
52import static com.android.server.am.ActivityManagerService.CONTINUE_USER_SWITCH_MSG;
53import static com.android.server.am.ActivityManagerService.REPORT_LOCKED_BOOT_COMPLETE_MSG;
54import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG;
55import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG;
56import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG;
57import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
58import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
59import static org.mockito.ArgumentMatchers.anyString;
60import static org.mockito.ArgumentMatchers.nullable;
61import static org.mockito.Matchers.any;
62import static org.mockito.Matchers.anyBoolean;
63import static org.mockito.Matchers.anyInt;
64import static org.mockito.Matchers.eq;
65import static org.mockito.Mockito.doAnswer;
66import static org.mockito.Mockito.doNothing;
67import static org.mockito.Mockito.mock;
68import static org.mockito.Mockito.never;
69import static org.mockito.Mockito.times;
70import static org.mockito.Mockito.when;
71
72public 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}