/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.multiuser; import android.app.ActivityManager; import android.app.IActivityManager; import android.app.IStopUserCallback; import android.app.UserSwitchObserver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import android.support.test.InstrumentationRegistry; import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Perf tests for user life cycle events. * * Running the tests: * make MultiUserPerfTests && * adb install -r \ * ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk && * adb shell am instrument -e class android.multiuser.UserLifecycleTest \ * -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner */ @LargeTest @RunWith(AndroidJUnit4.class) public class UserLifecycleTest { private final int TIMEOUT_REMOVE_USER_MS = 4 * 1000; // 4 sec private final int CHECK_USER_REMOVED_INTERVAL_MS = 200; // 0.2 sec private final int TIMEOUT_USER_START_SEC = 4; // 4 sec private final int TIMEOUT_USER_SWITCH_SEC = 8; // 8 sec private final int TIMEOUT_USER_STOP_SEC = 1; // 1 sec private final int TIMEOUT_MANAGED_PROFILE_UNLOCK_SEC = 2; // 2 sec private final int TIMEOUT_LOCKED_BOOT_COMPLETE_MS = 5 * 1000; // 5 sec private final int TIMEOUT_EPHERMAL_USER_STOP_SEC = 6; // 6 sec private UserManager mUm; private ActivityManager mAm; private IActivityManager mIam; private BenchmarkState mState; private ArrayList mUsersToRemove; @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); @Before public void setUp() { final Context context = InstrumentationRegistry.getContext(); mUm = UserManager.get(context); mAm = context.getSystemService(ActivityManager.class); mIam = ActivityManager.getService(); mState = mPerfStatusReporter.getBenchmarkState(); mUsersToRemove = new ArrayList<>(); } @After public void tearDown() { for (int userId : mUsersToRemove) { try { mUm.removeUser(userId); } catch (Exception e) { // Ignore } } } @Test public void createAndStartUserPerf() throws Exception { while (mState.keepRunning()) { final UserInfo userInfo = mUm.createUser("TestUser", 0); final CountDownLatch latch = new CountDownLatch(1); registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id); mIam.startUserInBackground(userInfo.id); latch.await(TIMEOUT_USER_START_SEC, TimeUnit.SECONDS); mState.pauseTiming(); removeUser(userInfo.id); mState.resumeTiming(); } } @Test public void switchUserPerf() throws Exception { while (mState.keepRunning()) { mState.pauseTiming(); final int startUser = mAm.getCurrentUser(); final UserInfo userInfo = mUm.createUser("TestUser", 0); mState.resumeTiming(); switchUser(userInfo.id); mState.pauseTiming(); switchUser(startUser); removeUser(userInfo.id); mState.resumeTiming(); } } @Test public void stopUserPerf() throws Exception { while (mState.keepRunning()) { mState.pauseTiming(); final UserInfo userInfo = mUm.createUser("TestUser", 0); final CountDownLatch latch = new CountDownLatch(1); registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id); mIam.startUserInBackground(userInfo.id); latch.await(TIMEOUT_USER_START_SEC, TimeUnit.SECONDS); mState.resumeTiming(); stopUser(userInfo.id); mState.pauseTiming(); removeUser(userInfo.id); mState.resumeTiming(); } } @Test public void lockedBootCompletedPerf() throws Exception { while (mState.keepRunning()) { mState.pauseTiming(); final int startUser = mAm.getCurrentUser(); final UserInfo userInfo = mUm.createUser("TestUser", 0); final CountDownLatch latch = new CountDownLatch(1); registerUserSwitchObserver(null, latch, userInfo.id); mState.resumeTiming(); mAm.switchUser(userInfo.id); latch.await(TIMEOUT_LOCKED_BOOT_COMPLETE_MS, TimeUnit.SECONDS); mState.pauseTiming(); switchUser(startUser); removeUser(userInfo.id); mState.resumeTiming(); } } @Test public void managedProfileUnlockPerf() throws Exception { while (mState.keepRunning()) { mState.pauseTiming(); final UserInfo userInfo = mUm.createProfileForUser("TestUser", UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser()); final CountDownLatch latch = new CountDownLatch(1); registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id); mState.resumeTiming(); mIam.startUserInBackground(userInfo.id); latch.await(TIMEOUT_MANAGED_PROFILE_UNLOCK_SEC, TimeUnit.SECONDS); mState.pauseTiming(); removeUser(userInfo.id); mState.resumeTiming(); } } @Test public void ephemeralUserStoppedPerf() throws Exception { while (mState.keepRunning()) { mState.pauseTiming(); final int startUser = mAm.getCurrentUser(); final UserInfo userInfo = mUm.createUser("TestUser", UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO); switchUser(userInfo.id); final CountDownLatch latch = new CountDownLatch(1); InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_USER_STOPPED.equals(intent.getAction()) && intent.getIntExtra( Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userInfo.id) { latch.countDown(); } } }, new IntentFilter(Intent.ACTION_USER_STOPPED)); final CountDownLatch switchLatch = new CountDownLatch(1); registerUserSwitchObserver(switchLatch, null, startUser); mState.resumeTiming(); mAm.switchUser(startUser); latch.await(TIMEOUT_EPHERMAL_USER_STOP_SEC, TimeUnit.SECONDS); mState.pauseTiming(); switchLatch.await(TIMEOUT_USER_SWITCH_SEC, TimeUnit.SECONDS); removeUser(userInfo.id); mState.resumeTiming(); } } private void switchUser(int userId) throws Exception { final CountDownLatch latch = new CountDownLatch(1); registerUserSwitchObserver(latch, null, userId); mAm.switchUser(userId); latch.await(TIMEOUT_USER_SWITCH_SEC, TimeUnit.SECONDS); } private void stopUser(int userId) throws Exception { final CountDownLatch latch = new CountDownLatch(1); mIam.stopUser(userId, false /* force */, new IStopUserCallback.Stub() { @Override public void userStopped(int userId) throws RemoteException { latch.countDown(); } @Override public void userStopAborted(int userId) throws RemoteException { } }); latch.await(TIMEOUT_USER_STOP_SEC, TimeUnit.SECONDS); } private void registerUserSwitchObserver(final CountDownLatch switchLatch, final CountDownLatch bootCompleteLatch, final int userId) throws Exception { ActivityManager.getService().registerUserSwitchObserver( new UserSwitchObserver() { @Override public void onUserSwitchComplete(int newUserId) throws RemoteException { if (switchLatch != null && userId == newUserId) { switchLatch.countDown(); } } @Override public void onLockedBootComplete(int newUserId) { if (bootCompleteLatch != null && userId == newUserId) { bootCompleteLatch.countDown(); } } }, "UserLifecycleTest"); } private void registerBroadcastReceiver(final String action, final CountDownLatch latch, final int userId) { InstrumentationRegistry.getContext().registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (action.equals(intent.getAction()) && intent.getIntExtra( Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userId) { latch.countDown(); } } }, UserHandle.of(userId), new IntentFilter(action), null, null); } private void removeUser(int userId) { try { mUm.removeUser(userId); final long startTime = System.currentTimeMillis(); while (mUm.getUserInfo(userId) != null && System.currentTimeMillis() - startTime < TIMEOUT_REMOVE_USER_MS) { Thread.sleep(CHECK_USER_REMOVED_INTERVAL_MS); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (Exception e) { // Ignore } if (mUm.getUserInfo(userId) != null) { mUsersToRemove.add(userId); } } }