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 */
16package android.multiuser;
17
18import android.app.ActivityManager;
19import android.app.IActivityManager;
20import android.app.IStopUserCallback;
21import android.app.UserSwitchObserver;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.UserInfo;
27import android.os.RemoteException;
28import android.os.UserHandle;
29import android.os.UserManager;
30import android.support.test.InstrumentationRegistry;
31import android.support.test.filters.LargeTest;
32import android.support.test.runner.AndroidJUnit4;
33
34import org.junit.After;
35import org.junit.Before;
36import org.junit.Rule;
37import org.junit.Test;
38import org.junit.runner.RunWith;
39
40import java.util.ArrayList;
41import java.util.concurrent.CountDownLatch;
42import java.util.concurrent.TimeUnit;
43
44/**
45 * Perf tests for user life cycle events.
46 *
47 * Running the tests:
48 *
49 * make MultiUserPerfTests &&
50 * adb install -r \
51 *     ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
52 * adb shell am instrument -e class android.multiuser.UserLifecycleTests \
53 *     -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
54 *
55 * or
56 *
57 * bit MultiUserPerfTests:android.multiuser.UserLifecycleTests
58 *
59 * Note: If you use bit for running the tests, benchmark results won't be printed on the host side.
60 * But in either case, results can be checked on the device side 'adb logcat -s UserLifecycleTests'
61 */
62@LargeTest
63@RunWith(AndroidJUnit4.class)
64public class UserLifecycleTests {
65    private static final String TAG = UserLifecycleTests.class.getSimpleName();
66
67    private final int TIMEOUT_IN_SECOND = 30;
68    private final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
69
70    private UserManager mUm;
71    private ActivityManager mAm;
72    private IActivityManager mIam;
73    private ArrayList<Integer> mUsersToRemove;
74
75    private final BenchmarkRunner mRunner = new BenchmarkRunner();
76    @Rule
77    public BenchmarkResultsReporter mReporter = new BenchmarkResultsReporter(mRunner);
78
79    @Before
80    public void setUp() {
81        final Context context = InstrumentationRegistry.getContext();
82        mUm = UserManager.get(context);
83        mAm = context.getSystemService(ActivityManager.class);
84        mIam = ActivityManager.getService();
85        mUsersToRemove = new ArrayList<>();
86    }
87
88    @After
89    public void tearDown() {
90        for (int userId : mUsersToRemove) {
91            try {
92                mUm.removeUser(userId);
93            } catch (Exception e) {
94                // Ignore
95            }
96        }
97    }
98
99    @Test
100    public void createAndStartUser() throws Exception {
101        while (mRunner.keepRunning()) {
102            final UserInfo userInfo = mUm.createUser("TestUser", 0);
103
104            final CountDownLatch latch = new CountDownLatch(1);
105            registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
106            mIam.startUserInBackground(userInfo.id);
107            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
108
109            mRunner.pauseTiming();
110            removeUser(userInfo.id);
111            mRunner.resumeTiming();
112        }
113    }
114
115    @Test
116    public void switchUser() throws Exception {
117        while (mRunner.keepRunning()) {
118            mRunner.pauseTiming();
119            final int startUser = mAm.getCurrentUser();
120            final UserInfo userInfo = mUm.createUser("TestUser", 0);
121            mRunner.resumeTiming();
122
123            switchUser(userInfo.id);
124
125            mRunner.pauseTiming();
126            switchUser(startUser);
127            removeUser(userInfo.id);
128            mRunner.resumeTiming();
129        }
130    }
131
132    @Test
133    public void stopUser() throws Exception {
134        while (mRunner.keepRunning()) {
135            mRunner.pauseTiming();
136            final UserInfo userInfo = mUm.createUser("TestUser", 0);
137            final CountDownLatch latch = new CountDownLatch(1);
138            registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
139            mIam.startUserInBackground(userInfo.id);
140            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
141            mRunner.resumeTiming();
142
143            stopUser(userInfo.id, false);
144
145            mRunner.pauseTiming();
146            removeUser(userInfo.id);
147            mRunner.resumeTiming();
148        }
149    }
150
151    @Test
152    public void lockedBootCompleted() throws Exception {
153        while (mRunner.keepRunning()) {
154            mRunner.pauseTiming();
155            final int startUser = mAm.getCurrentUser();
156            final UserInfo userInfo = mUm.createUser("TestUser", 0);
157            final CountDownLatch latch = new CountDownLatch(1);
158            registerUserSwitchObserver(null, latch, userInfo.id);
159            mRunner.resumeTiming();
160
161            mAm.switchUser(userInfo.id);
162            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
163
164            mRunner.pauseTiming();
165            switchUser(startUser);
166            removeUser(userInfo.id);
167            mRunner.resumeTiming();
168        }
169    }
170
171    @Test
172    public void managedProfileUnlock() throws Exception {
173        while (mRunner.keepRunning()) {
174            mRunner.pauseTiming();
175            final UserInfo userInfo = mUm.createProfileForUser("TestUser",
176                    UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
177            final CountDownLatch latch = new CountDownLatch(1);
178            registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
179            mRunner.resumeTiming();
180
181            mIam.startUserInBackground(userInfo.id);
182            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
183
184            mRunner.pauseTiming();
185            removeUser(userInfo.id);
186            mRunner.resumeTiming();
187        }
188    }
189
190    @Test
191    public void ephemeralUserStopped() throws Exception {
192        while (mRunner.keepRunning()) {
193            mRunner.pauseTiming();
194            final int startUser = mAm.getCurrentUser();
195            final UserInfo userInfo = mUm.createUser("TestUser",
196                    UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
197            switchUser(userInfo.id);
198            final CountDownLatch latch = new CountDownLatch(1);
199            InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() {
200                @Override
201                public void onReceive(Context context, Intent intent) {
202                    if (Intent.ACTION_USER_STOPPED.equals(intent.getAction()) && intent.getIntExtra(
203                            Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userInfo.id) {
204                        latch.countDown();
205                    }
206                }
207            }, new IntentFilter(Intent.ACTION_USER_STOPPED));
208            final CountDownLatch switchLatch = new CountDownLatch(1);
209            registerUserSwitchObserver(switchLatch, null, startUser);
210            mRunner.resumeTiming();
211
212            mAm.switchUser(startUser);
213            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
214
215            mRunner.pauseTiming();
216            switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
217            removeUser(userInfo.id);
218            mRunner.resumeTiming();
219        }
220    }
221
222    @Test
223    public void managedProfileStopped() throws Exception {
224        while (mRunner.keepRunning()) {
225            mRunner.pauseTiming();
226            final UserInfo userInfo = mUm.createProfileForUser("TestUser",
227                    UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
228            final CountDownLatch latch = new CountDownLatch(1);
229            registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
230            mIam.startUserInBackground(userInfo.id);
231            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
232            mRunner.resumeTiming();
233
234            stopUser(userInfo.id, true);
235
236            mRunner.pauseTiming();
237            removeUser(userInfo.id);
238            mRunner.resumeTiming();
239        }
240    }
241
242    private void switchUser(int userId) throws Exception {
243        final CountDownLatch latch = new CountDownLatch(1);
244        registerUserSwitchObserver(latch, null, userId);
245        mAm.switchUser(userId);
246        latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
247    }
248
249    private void stopUser(int userId, boolean force) throws Exception {
250        final CountDownLatch latch = new CountDownLatch(1);
251        mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
252            @Override
253            public void userStopped(int userId) throws RemoteException {
254                latch.countDown();
255            }
256
257            @Override
258            public void userStopAborted(int userId) throws RemoteException {
259            }
260        });
261        latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
262    }
263
264    private void registerUserSwitchObserver(final CountDownLatch switchLatch,
265            final CountDownLatch bootCompleteLatch, final int userId) throws Exception {
266        ActivityManager.getService().registerUserSwitchObserver(
267                new UserSwitchObserver() {
268                    @Override
269                    public void onUserSwitchComplete(int newUserId) throws RemoteException {
270                        if (switchLatch != null && userId == newUserId) {
271                            switchLatch.countDown();
272                        }
273                    }
274
275                    @Override
276                    public void onLockedBootComplete(int newUserId) {
277                        if (bootCompleteLatch != null && userId == newUserId) {
278                            bootCompleteLatch.countDown();
279                        }
280                    }
281                }, TAG);
282    }
283
284    private void registerBroadcastReceiver(final String action, final CountDownLatch latch,
285            final int userId) {
286        InstrumentationRegistry.getContext().registerReceiverAsUser(new BroadcastReceiver() {
287            @Override
288            public void onReceive(Context context, Intent intent) {
289                if (action.equals(intent.getAction()) && intent.getIntExtra(
290                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userId) {
291                    latch.countDown();
292                }
293            }
294        }, UserHandle.of(userId), new IntentFilter(action), null, null);
295    }
296
297    private void removeUser(int userId) {
298        try {
299            mUm.removeUser(userId);
300            final long startTime = System.currentTimeMillis();
301            final long timeoutInMs = TIMEOUT_IN_SECOND * 1000;
302            while (mUm.getUserInfo(userId) != null &&
303                    System.currentTimeMillis() - startTime < timeoutInMs) {
304                TimeUnit.MILLISECONDS.sleep(CHECK_USER_REMOVED_INTERVAL_MS);
305            }
306        } catch (InterruptedException e) {
307            Thread.currentThread().interrupt();
308        } catch (Exception e) {
309            // Ignore
310        }
311        if (mUm.getUserInfo(userId) != null) {
312            mUsersToRemove.add(userId);
313        }
314    }
315}
316