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 package android.multiuser;
17 
18 import android.app.ActivityManager;
19 import android.app.IActivityManager;
20 import android.app.IStopUserCallback;
21 import android.app.UserSwitchObserver;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.UserInfo;
27 import android.os.RemoteException;
28 import android.os.UserHandle;
29 import android.os.UserManager;
30 import android.util.Log;
31 
32 import androidx.test.InstrumentationRegistry;
33 import androidx.test.filters.LargeTest;
34 import androidx.test.runner.AndroidJUnit4;
35 
36 import org.junit.After;
37 import org.junit.Before;
38 import org.junit.Rule;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 
42 import java.util.ArrayList;
43 import java.util.concurrent.CountDownLatch;
44 import java.util.concurrent.TimeUnit;
45 
46 /**
47  * Perf tests for user life cycle events.
48  *
49  * Running the tests:
50  *
51  * make MultiUserPerfTests &&
52  * adb install -r \
53  *     ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
54  * adb shell am instrument -e class android.multiuser.UserLifecycleTests \
55  *     -w com.android.perftests.multiuser/androidx.test.runner.AndroidJUnitRunner
56  *
57  * or
58  *
59  * bit MultiUserPerfTests:android.multiuser.UserLifecycleTests
60  *
61  * Note: If you use bit for running the tests, benchmark results won't be printed on the host side.
62  * But in either case, results can be checked on the device side 'adb logcat -s UserLifecycleTests'
63  */
64 @LargeTest
65 @RunWith(AndroidJUnit4.class)
66 public class UserLifecycleTests {
67     private static final String TAG = UserLifecycleTests.class.getSimpleName();
68 
69     private final int TIMEOUT_IN_SECOND = 30;
70     private final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
71 
72     private UserManager mUm;
73     private ActivityManager mAm;
74     private IActivityManager mIam;
75     private ArrayList<Integer> mUsersToRemove;
76 
77     private final BenchmarkRunner mRunner = new BenchmarkRunner();
78     @Rule
79     public BenchmarkResultsReporter mReporter = new BenchmarkResultsReporter(mRunner);
80 
81     @Before
setUp()82     public void setUp() {
83         final Context context = InstrumentationRegistry.getContext();
84         mUm = UserManager.get(context);
85         mAm = context.getSystemService(ActivityManager.class);
86         mIam = ActivityManager.getService();
87         mUsersToRemove = new ArrayList<>();
88     }
89 
90     @After
tearDown()91     public void tearDown() {
92         for (int userId : mUsersToRemove) {
93             try {
94                 mUm.removeUser(userId);
95             } catch (Exception e) {
96                 // Ignore
97             }
98         }
99     }
100 
101     @Test
createAndStartUser()102     public void createAndStartUser() throws Exception {
103         while (mRunner.keepRunning()) {
104             final UserInfo userInfo = mUm.createUser("TestUser", 0);
105 
106             final CountDownLatch latch = new CountDownLatch(1);
107             registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
108             mIam.startUserInBackground(userInfo.id);
109             latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
110 
111             mRunner.pauseTiming();
112             removeUser(userInfo.id);
113             mRunner.resumeTiming();
114         }
115     }
116 
117     @Test
switchUser()118     public void switchUser() throws Exception {
119         while (mRunner.keepRunning()) {
120             mRunner.pauseTiming();
121             final int startUser = mAm.getCurrentUser();
122             final UserInfo userInfo = mUm.createUser("TestUser", 0);
123             mRunner.resumeTiming();
124 
125             switchUser(userInfo.id);
126 
127             mRunner.pauseTiming();
128             switchUser(startUser);
129             removeUser(userInfo.id);
130             mRunner.resumeTiming();
131         }
132     }
133 
134     /** Tests switching to an already-created, but no-longer-running, user. */
135     @Test
switchUser_stopped()136     public void switchUser_stopped() throws Exception {
137         while (mRunner.keepRunning()) {
138             mRunner.pauseTiming();
139             final int startUser = mAm.getCurrentUser();
140             final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
141             final CountDownLatch latch = new CountDownLatch(1);
142             registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, testUser);
143             mRunner.resumeTiming();
144 
145             mAm.switchUser(testUser);
146             boolean success = latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
147 
148             mRunner.pauseTiming();
149             attestTrue("Failed to achieve 2nd ACTION_USER_UNLOCKED for user " + testUser, success);
150             switchUser(startUser);
151             removeUser(testUser);
152             mRunner.resumeTiming();
153         }
154     }
155 
156     /** Tests switching to an already-created already-running non-owner user. */
157     @Test
switchUser_running()158     public void switchUser_running() throws Exception {
159         while (mRunner.keepRunning()) {
160             mRunner.pauseTiming();
161             final int startUser = mAm.getCurrentUser();
162             final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
163             mRunner.resumeTiming();
164 
165             switchUser(testUser);
166 
167             mRunner.pauseTiming();
168             attestTrue("Failed to switch to user " + testUser, mAm.isUserRunning(testUser));
169             switchUser(startUser);
170             removeUser(testUser);
171             mRunner.resumeTiming();
172         }
173     }
174 
175     @Test
stopUser()176     public void stopUser() throws Exception {
177         while (mRunner.keepRunning()) {
178             mRunner.pauseTiming();
179             final UserInfo userInfo = mUm.createUser("TestUser", 0);
180             final CountDownLatch latch = new CountDownLatch(1);
181             registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
182             mIam.startUserInBackground(userInfo.id);
183             latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
184             mRunner.resumeTiming();
185 
186             stopUser(userInfo.id, false);
187 
188             mRunner.pauseTiming();
189             removeUser(userInfo.id);
190             mRunner.resumeTiming();
191         }
192     }
193 
194     @Test
lockedBootCompleted()195     public void lockedBootCompleted() throws Exception {
196         while (mRunner.keepRunning()) {
197             mRunner.pauseTiming();
198             final int startUser = mAm.getCurrentUser();
199             final UserInfo userInfo = mUm.createUser("TestUser", 0);
200             final CountDownLatch latch = new CountDownLatch(1);
201             registerUserSwitchObserver(null, latch, userInfo.id);
202             mRunner.resumeTiming();
203 
204             mAm.switchUser(userInfo.id);
205             latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
206 
207             mRunner.pauseTiming();
208             switchUser(startUser);
209             removeUser(userInfo.id);
210             mRunner.resumeTiming();
211         }
212     }
213 
214     @Test
managedProfileUnlock()215     public void managedProfileUnlock() throws Exception {
216         while (mRunner.keepRunning()) {
217             mRunner.pauseTiming();
218             final UserInfo userInfo = mUm.createProfileForUser("TestUser",
219                     UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
220             final CountDownLatch latch = new CountDownLatch(1);
221             registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
222             mRunner.resumeTiming();
223 
224             mIam.startUserInBackground(userInfo.id);
225             latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
226 
227             mRunner.pauseTiming();
228             removeUser(userInfo.id);
229             mRunner.resumeTiming();
230         }
231     }
232 
233     /** Tests starting an already-created, but no-longer-running, profile. */
234     @Test
managedProfileUnlock_stopped()235     public void managedProfileUnlock_stopped() throws Exception {
236         while (mRunner.keepRunning()) {
237             mRunner.pauseTiming();
238             final UserInfo userInfo = mUm.createProfileForUser("TestUser",
239                     UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
240             // Start the profile initially, then stop it. Similar to setQuietModeEnabled.
241             final CountDownLatch latch1 = new CountDownLatch(1);
242             registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch1, userInfo.id);
243             mIam.startUserInBackground(userInfo.id);
244             latch1.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
245             stopUser(userInfo.id, true);
246 
247             // Now we restart the profile.
248             final CountDownLatch latch2 = new CountDownLatch(1);
249             registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch2, userInfo.id);
250             mRunner.resumeTiming();
251 
252             mIam.startUserInBackground(userInfo.id);
253             latch2.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
254 
255             mRunner.pauseTiming();
256             removeUser(userInfo.id);
257             mRunner.resumeTiming();
258         }
259     }
260 
261     @Test
ephemeralUserStopped()262     public void ephemeralUserStopped() throws Exception {
263         while (mRunner.keepRunning()) {
264             mRunner.pauseTiming();
265             final int startUser = mAm.getCurrentUser();
266             final UserInfo userInfo = mUm.createUser("TestUser",
267                     UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
268             switchUser(userInfo.id);
269             final CountDownLatch latch = new CountDownLatch(1);
270             InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() {
271                 @Override
272                 public void onReceive(Context context, Intent intent) {
273                     if (Intent.ACTION_USER_STOPPED.equals(intent.getAction()) && intent.getIntExtra(
274                             Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userInfo.id) {
275                         latch.countDown();
276                     }
277                 }
278             }, new IntentFilter(Intent.ACTION_USER_STOPPED));
279             final CountDownLatch switchLatch = new CountDownLatch(1);
280             registerUserSwitchObserver(switchLatch, null, startUser);
281             mRunner.resumeTiming();
282 
283             mAm.switchUser(startUser);
284             latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
285 
286             mRunner.pauseTiming();
287             switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
288             removeUser(userInfo.id);
289             mRunner.resumeTiming();
290         }
291     }
292 
293     @Test
managedProfileStopped()294     public void managedProfileStopped() throws Exception {
295         while (mRunner.keepRunning()) {
296             mRunner.pauseTiming();
297             final UserInfo userInfo = mUm.createProfileForUser("TestUser",
298                     UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
299             final CountDownLatch latch = new CountDownLatch(1);
300             registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
301             mIam.startUserInBackground(userInfo.id);
302             latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
303             mRunner.resumeTiming();
304 
305             stopUser(userInfo.id, true);
306 
307             mRunner.pauseTiming();
308             removeUser(userInfo.id);
309             mRunner.resumeTiming();
310         }
311     }
312 
switchUser(int userId)313     private void switchUser(int userId) throws Exception {
314         final CountDownLatch latch = new CountDownLatch(1);
315         registerUserSwitchObserver(latch, null, userId);
316         mAm.switchUser(userId);
317         latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
318     }
319 
stopUser(int userId, boolean force)320     private void stopUser(int userId, boolean force) throws Exception {
321         final CountDownLatch latch = new CountDownLatch(1);
322         mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
323             @Override
324             public void userStopped(int userId) throws RemoteException {
325                 latch.countDown();
326             }
327 
328             @Override
329             public void userStopAborted(int userId) throws RemoteException {
330             }
331         });
332         latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
333     }
334 
335     /**
336      * Creates a user and waits for its ACTION_USER_UNLOCKED.
337      * Then switches to back to the original user and waits for its switchUser() to finish.
338      *
339      * @param stopNewUser whether to stop the new user after switching to otherUser.
340      * @return userId of the newly created user.
341      */
initializeNewUserAndSwitchBack(boolean stopNewUser)342     private int initializeNewUserAndSwitchBack(boolean stopNewUser) throws Exception {
343         final int origUser = mAm.getCurrentUser();
344         // First, create and switch to testUser, waiting for its ACTION_USER_UNLOCKED
345         final int testUser = mUm.createUser("TestUser", 0).id;
346         final CountDownLatch latch1 = new CountDownLatch(1);
347         registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch1, testUser);
348         mAm.switchUser(testUser);
349         attestTrue("Failed to achieve initial ACTION_USER_UNLOCKED for user " + testUser,
350                 latch1.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS));
351 
352         // Second, switch back to origUser, waiting merely for switchUser() to finish
353         switchUser(origUser);
354         attestTrue("Didn't switch back to user, " + origUser, origUser == mAm.getCurrentUser());
355 
356         if (stopNewUser) {
357             stopUser(testUser, true);
358             attestFalse("Failed to stop user " + testUser, mAm.isUserRunning(testUser));
359         }
360 
361         return testUser;
362     }
363 
registerUserSwitchObserver(final CountDownLatch switchLatch, final CountDownLatch bootCompleteLatch, final int userId)364     private void registerUserSwitchObserver(final CountDownLatch switchLatch,
365             final CountDownLatch bootCompleteLatch, final int userId) throws Exception {
366         ActivityManager.getService().registerUserSwitchObserver(
367                 new UserSwitchObserver() {
368                     @Override
369                     public void onUserSwitchComplete(int newUserId) throws RemoteException {
370                         if (switchLatch != null && userId == newUserId) {
371                             switchLatch.countDown();
372                         }
373                     }
374 
375                     @Override
376                     public void onLockedBootComplete(int newUserId) {
377                         if (bootCompleteLatch != null && userId == newUserId) {
378                             bootCompleteLatch.countDown();
379                         }
380                     }
381                 }, TAG);
382     }
383 
registerBroadcastReceiver(final String action, final CountDownLatch latch, final int userId)384     private void registerBroadcastReceiver(final String action, final CountDownLatch latch,
385             final int userId) {
386         InstrumentationRegistry.getContext().registerReceiverAsUser(new BroadcastReceiver() {
387             @Override
388             public void onReceive(Context context, Intent intent) {
389                 if (action.equals(intent.getAction()) && intent.getIntExtra(
390                         Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userId) {
391                     latch.countDown();
392                 }
393             }
394         }, UserHandle.of(userId), new IntentFilter(action), null, null);
395     }
396 
removeUser(int userId)397     private void removeUser(int userId) {
398         try {
399             mUm.removeUser(userId);
400             final long startTime = System.currentTimeMillis();
401             final long timeoutInMs = TIMEOUT_IN_SECOND * 1000;
402             while (mUm.getUserInfo(userId) != null &&
403                     System.currentTimeMillis() - startTime < timeoutInMs) {
404                 TimeUnit.MILLISECONDS.sleep(CHECK_USER_REMOVED_INTERVAL_MS);
405             }
406         } catch (InterruptedException e) {
407             Thread.currentThread().interrupt();
408         } catch (Exception e) {
409             // Ignore
410         }
411         if (mUm.getUserInfo(userId) != null) {
412             mUsersToRemove.add(userId);
413         }
414     }
415 
attestTrue(String message, boolean attestion)416     private void attestTrue(String message, boolean attestion) {
417         if (!attestion) {
418             Log.w(TAG, message);
419         }
420     }
421 
attestFalse(String message, boolean attestion)422     private void attestFalse(String message, boolean attestion) {
423         attestTrue(message, !attestion);
424     }
425 }
426