UserControllerTest.java revision 9bc8543d45fc7f2edf857181940d3719e480a597
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.IUserSwitchObserver;
20import android.content.Context;
21import android.content.IIntentReceiver;
22import android.content.Intent;
23import android.content.pm.UserInfo;
24import android.os.Binder;
25import android.os.Bundle;
26import android.os.Handler;
27import android.os.HandlerThread;
28import android.os.IRemoteCallback;
29import android.os.Looper;
30import android.os.Message;
31import android.os.RemoteException;
32import android.os.UserManagerInternal;
33import android.test.AndroidTestCase;
34import android.test.suitebuilder.annotation.SmallTest;
35import android.util.Log;
36
37import com.android.server.pm.UserManagerService;
38import com.android.server.wm.WindowManagerService;
39
40import org.mockito.Mockito;
41
42import java.util.ArrayList;
43import java.util.Arrays;
44import java.util.Collections;
45import java.util.HashSet;
46import java.util.LinkedHashSet;
47import java.util.List;
48import java.util.Set;
49
50import static android.content.pm.PackageManager.PERMISSION_GRANTED;
51import static com.android.server.am.ActivityManagerService.CONTINUE_USER_SWITCH_MSG;
52import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG;
53import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG;
54import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG;
55import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
56import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
57import static org.mockito.Matchers.any;
58import static org.mockito.Matchers.anyInt;
59import static org.mockito.Matchers.eq;
60import static org.mockito.Mockito.doAnswer;
61import static org.mockito.Mockito.mock;
62import static org.mockito.Mockito.never;
63import static org.mockito.Mockito.times;
64import static org.mockito.Mockito.when;
65
66public class UserControllerTest extends AndroidTestCase {
67    private static final int TEST_USER_ID = 10;
68    private static String TAG = UserControllerTest.class.getSimpleName();
69    private UserController mUserController;
70    private TestInjector mInjector;
71
72    @Override
73    public void setUp() throws Exception {
74        super.setUp();
75        mInjector = new TestInjector(getContext());
76        mUserController = new UserController(mInjector);
77        setUpUser(TEST_USER_ID, 0);
78    }
79
80    @Override
81    protected void tearDown() throws Exception {
82        super.tearDown();
83        mInjector.handlerThread.quit();
84
85    }
86
87    @SmallTest
88    public void testStartUser() throws RemoteException {
89        mUserController.startUser(TEST_USER_ID, true);
90        Mockito.verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
91        Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
92        List<String> expectedActions = Arrays.asList(Intent.ACTION_USER_STARTED,
93                Intent.ACTION_USER_SWITCHED, Intent.ACTION_USER_STARTING);
94        assertEquals(expectedActions, getActions(mInjector.sentIntents));
95        Set<Integer> expectedCodes = new HashSet<>(
96                Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG,
97                        SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG));
98        Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
99        assertEquals("Unexpected message sent", expectedCodes, actualCodes);
100        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
101        assertNotNull(reportMsg);
102        UserState userState = (UserState) reportMsg.obj;
103        assertNotNull(userState);
104        assertEquals(TEST_USER_ID, userState.mHandle.getIdentifier());
105        assertEquals("User must be in STATE_BOOTING", UserState.STATE_BOOTING, userState.state);
106        assertEquals("Unexpected old user id", 0, reportMsg.arg1);
107        assertEquals("Unexpected new user id", TEST_USER_ID, reportMsg.arg2);
108    }
109
110    @SmallTest
111    public void testDispatchUserSwitch() throws RemoteException {
112        // Prepare mock observer and register it
113        IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
114        when(observer.asBinder()).thenReturn(new Binder());
115        doAnswer(invocation -> {
116            IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
117            callback.sendResult(null);
118            return null;
119        }).when(observer).onUserSwitching(anyInt(), any());
120        mUserController.registerUserSwitchObserver(observer, "mock");
121        // Start user -- this will update state of mUserController
122        mUserController.startUser(TEST_USER_ID, true);
123        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
124        assertNotNull(reportMsg);
125        UserState userState = (UserState) reportMsg.obj;
126        int oldUserId = reportMsg.arg1;
127        int newUserId = reportMsg.arg2;
128        // Call dispatchUserSwitch and verify that observer was called only once
129        mInjector.handler.clearAllRecordedMessages();
130        mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
131        Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
132        Set<Integer> expectedCodes = Collections.singleton(CONTINUE_USER_SWITCH_MSG);
133        Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
134        assertEquals("Unexpected message sent", expectedCodes, actualCodes);
135        Message conMsg = mInjector.handler.getMessageForCode(CONTINUE_USER_SWITCH_MSG);
136        assertNotNull(conMsg);
137        userState = (UserState) conMsg.obj;
138        assertNotNull(userState);
139        assertEquals(TEST_USER_ID, userState.mHandle.getIdentifier());
140        assertEquals("User must be in STATE_BOOTING", UserState.STATE_BOOTING, userState.state);
141        assertEquals("Unexpected old user id", 0, conMsg.arg1);
142        assertEquals("Unexpected new user id", TEST_USER_ID, conMsg.arg2);
143    }
144
145    @SmallTest
146    public void testDispatchUserSwitchBadReceiver() throws RemoteException {
147        // Prepare mock observer which doesn't notify the callback and register it
148        IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
149        when(observer.asBinder()).thenReturn(new Binder());
150        mUserController.registerUserSwitchObserver(observer, "mock");
151        // Start user -- this will update state of mUserController
152        mUserController.startUser(TEST_USER_ID, true);
153        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
154        assertNotNull(reportMsg);
155        UserState userState = (UserState) reportMsg.obj;
156        int oldUserId = reportMsg.arg1;
157        int newUserId = reportMsg.arg2;
158        // Call dispatchUserSwitch and verify that observer was called only once
159        mInjector.handler.clearAllRecordedMessages();
160        mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
161        Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
162        // Verify that CONTINUE_USER_SWITCH_MSG is not sent (triggers timeout)
163        Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
164        assertTrue("No messages should be sent", actualCodes.isEmpty());
165    }
166
167    @SmallTest
168    public void testContinueUserSwitch() throws RemoteException {
169        // Start user -- this will update state of mUserController
170        mUserController.startUser(TEST_USER_ID, true);
171        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
172        assertNotNull(reportMsg);
173        UserState userState = (UserState) reportMsg.obj;
174        int oldUserId = reportMsg.arg1;
175        int newUserId = reportMsg.arg2;
176        mInjector.handler.clearAllRecordedMessages();
177        // Verify that continueUserSwitch worked as expected
178        mUserController.continueUserSwitch(userState, oldUserId, newUserId);
179        Mockito.verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
180        Set<Integer> expectedCodes = Collections.singleton(REPORT_USER_SWITCH_COMPLETE_MSG);
181        Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
182        assertEquals("Unexpected message sent", expectedCodes, actualCodes);
183        Message msg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_COMPLETE_MSG);
184        assertNotNull(msg);
185        assertEquals("Unexpected userId", TEST_USER_ID, msg.arg1);
186    }
187
188    private void setUpUser(int userId, int flags) {
189        UserInfo userInfo = new UserInfo(userId, "User" + userId, flags);
190        when(mInjector.userManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
191    }
192
193    private static List<String> getActions(List<Intent> intents) {
194        List<String> result = new ArrayList<>();
195        for (Intent intent : intents) {
196            result.add(intent.getAction());
197        }
198        return result;
199    }
200
201    private static class TestInjector extends UserController.Injector {
202        final Object lock = new Object();
203        TestHandler handler;
204        HandlerThread handlerThread;
205        UserManagerService userManagerMock;
206        UserManagerInternal userManagerInternalMock;
207        WindowManagerService windowManagerMock;
208        private Context mCtx;
209        List<Intent> sentIntents = new ArrayList<>();
210
211        TestInjector(Context ctx) {
212            super(null);
213            mCtx = ctx;
214            handlerThread = new HandlerThread(TAG);
215            handlerThread.start();
216            handler = new TestHandler(handlerThread.getLooper());
217            userManagerMock = mock(UserManagerService.class);
218            userManagerInternalMock = mock(UserManagerInternal.class);
219            windowManagerMock = mock(WindowManagerService.class);
220        }
221
222        @Override
223        protected Object getLock() {
224            return lock;
225        }
226
227        @Override
228        protected Handler getHandler() {
229            return handler;
230        }
231
232        @Override
233        protected UserManagerService getUserManager() {
234            return userManagerMock;
235        }
236
237        @Override
238        UserManagerInternal getUserManagerInternal() {
239            return userManagerInternalMock;
240        }
241
242        @Override
243        protected Context getContext() {
244            return mCtx;
245        }
246
247        @Override
248        int checkCallingPermission(String permission) {
249            Log.i(TAG, "checkCallingPermission " + permission);
250            return PERMISSION_GRANTED;
251        }
252
253        @Override
254        void stackSupervisorSetLockTaskModeLocked(TaskRecord task, int lockTaskModeState,
255                String reason, boolean andResume) {
256            Log.i(TAG, "stackSupervisorSetLockTaskModeLocked");
257        }
258
259        @Override
260        WindowManagerService getWindowManager() {
261            return windowManagerMock;
262        }
263
264        @Override
265        void updateUserConfigurationLocked() {
266            Log.i(TAG, "updateUserConfigurationLocked");
267        }
268
269        @Override
270        protected int broadcastIntentLocked(Intent intent, String resolvedType,
271                IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
272                String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered,
273                boolean sticky, int callingPid, int callingUid, int userId) {
274            Log.i(TAG, "broadcastIntentLocked " + intent);
275            sentIntents.add(intent);
276            return 0;
277        }
278
279        @Override
280        boolean stackSupervisorSwitchUserLocked(int userId, UserState uss) {
281            Log.i(TAG, "stackSupervisorSwitchUserLocked " + userId);
282            return true;
283        }
284
285        @Override
286        void startHomeActivityLocked(int userId, String reason) {
287            Log.i(TAG, "startHomeActivityLocked " + userId);
288        }
289   }
290
291    private static class TestHandler extends Handler {
292        private final List<Message> mMessages = new ArrayList<>();
293
294        TestHandler(Looper looper) {
295            super(looper);
296        }
297
298        Set<Integer> getMessageCodes() {
299            Set<Integer> result = new LinkedHashSet<>();
300            for (Message msg : mMessages) {
301                result.add(msg.what);
302            }
303            return result;
304        }
305
306        Message getMessageForCode(int what) {
307            for (Message msg : mMessages) {
308                if (msg.what == what) {
309                    return msg;
310                }
311            }
312            return null;
313        }
314
315        void clearAllRecordedMessages() {
316            mMessages.clear();
317        }
318
319        @Override
320        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
321            Message copy = new Message();
322            copy.copyFrom(msg);
323            mMessages.add(copy);
324            return super.sendMessageAtTime(msg, uptimeMillis);
325        }
326    }
327}