| /* |
| * 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 static android.app.WallpaperManager.FLAG_LOCK; |
| import static android.app.WallpaperManager.FLAG_SYSTEM; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assume.assumeTrue; |
| |
| import android.annotation.NonNull; |
| import android.app.ActivityManager; |
| import android.app.AppGlobals; |
| import android.app.IActivityManager; |
| import android.app.IStopUserCallback; |
| import android.app.WallpaperManager; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.IIntentReceiver; |
| import android.content.IIntentSender; |
| import android.content.Intent; |
| import android.content.IntentSender; |
| import android.content.pm.IPackageInstaller; |
| import android.content.pm.PackageManager; |
| import android.content.pm.UserInfo; |
| import android.graphics.Bitmap; |
| import android.os.Bundle; |
| import android.os.IBinder; |
| import android.os.IProgressListener; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.perftests.utils.ShellHelper; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.WindowManagerGlobal; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.filters.LargeTest; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import com.android.internal.util.FunctionalUtils; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| |
| /** |
| * Perf tests for user life cycle events. |
| * |
| * To run the tests: atest UserLifecycleTests |
| * |
| * |
| * Old methods for running the tests: |
| * |
| * make MultiUserPerfTests && |
| * adb install -r \ |
| * ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk && |
| * adb shell am instrument -e class android.multiuser.UserLifecycleTests \ |
| * -w com.android.perftests.multiuser/androidx.test.runner.AndroidJUnitRunner |
| * |
| * or |
| * |
| * bit MultiUserPerfTests:android.multiuser.UserLifecycleTests |
| * |
| * Note: If you use bit for running the tests, benchmark results won't be printed on the host side. |
| * But in either case, results can be checked on the device side 'adb logcat -s UserLifecycleTests' |
| */ |
| @LargeTest |
| @RunWith(AndroidJUnit4.class) |
| public class UserLifecycleTests { |
| private static final String TAG = UserLifecycleTests.class.getSimpleName(); |
| |
| /** Max runtime for each test (including all runs within that test). */ |
| // Must be less than the AndroidTest.xml test-timeout to avoid being considered non-responsive. |
| private static final long TIMEOUT_MAX_TEST_TIME_MS = 24 * 60_000; |
| |
| private static final int TIMEOUT_IN_SECOND = 30; |
| |
| /** Name of users/profiles in the test. Users with this name may be freely removed. */ |
| private static final String TEST_USER_NAME = "UserLifecycleTests_test_user"; |
| |
| /** Name of dummy package used when timing how long app launches take. */ |
| private static final String DUMMY_PACKAGE_NAME = "perftests.multiuser.apps.dummyapp"; |
| |
| // Copy of UserSystemPackageInstaller whitelist mode constants. |
| private static final String PACKAGE_WHITELIST_MODE_PROP = |
| "persist.debug.user.package_whitelist_mode"; |
| private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0; |
| private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b001; |
| private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b100; |
| private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1; |
| |
| private UserManager mUm; |
| private ActivityManager mAm; |
| private IActivityManager mIam; |
| private PackageManager mPm; |
| private WallpaperManager mWm; |
| private ArrayList<Integer> mUsersToRemove; |
| private boolean mHasManagedUserFeature; |
| private BroadcastWaiter mBroadcastWaiter; |
| private UserSwitchWaiter mUserSwitchWaiter; |
| private String mUserSwitchTimeoutMs; |
| private String mDisableUserSwitchingDialogAnimations; |
| |
| private final BenchmarkRunner mRunner = new BenchmarkRunner(); |
| @Rule |
| public BenchmarkResultsReporter mReporter = new BenchmarkResultsReporter(mRunner); |
| |
| @Before |
| public void setUp() throws Exception { |
| final Context context = InstrumentationRegistry.getContext(); |
| mUm = UserManager.get(context); |
| mAm = context.getSystemService(ActivityManager.class); |
| mIam = ActivityManager.getService(); |
| mUsersToRemove = new ArrayList<>(); |
| mPm = context.getPackageManager(); |
| mWm = WallpaperManager.getInstance(context); |
| mHasManagedUserFeature = mPm.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS); |
| mBroadcastWaiter = new BroadcastWaiter(context, TAG, TIMEOUT_IN_SECOND, |
| Intent.ACTION_USER_STARTED, |
| Intent.ACTION_MEDIA_MOUNTED, |
| Intent.ACTION_USER_UNLOCKED, |
| Intent.ACTION_USER_STOPPED); |
| mUserSwitchWaiter = new UserSwitchWaiter(TAG, TIMEOUT_IN_SECOND); |
| removeAnyPreviousTestUsers(); |
| if (mAm.getCurrentUser() != UserHandle.USER_SYSTEM) { |
| Log.w(TAG, "WARNING: Tests are being run from user " + mAm.getCurrentUser() |
| + " rather than the system user"); |
| } |
| mUserSwitchTimeoutMs = setSystemProperty( |
| "debug.usercontroller.user_switch_timeout_ms", "100000"); |
| mDisableUserSwitchingDialogAnimations = setSystemProperty( |
| "debug.usercontroller.disable_user_switching_dialog_animations", "true"); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| setSystemProperty("debug.usercontroller.user_switch_timeout_ms", mUserSwitchTimeoutMs); |
| setSystemProperty("debug.usercontroller.disable_user_switching_dialog_animations", |
| mDisableUserSwitchingDialogAnimations); |
| mBroadcastWaiter.close(); |
| mUserSwitchWaiter.close(); |
| for (int userId : mUsersToRemove) { |
| try { |
| mUm.removeUser(userId); |
| } catch (Exception e) { |
| // Ignore |
| } |
| } |
| } |
| |
| /** Tests creating a new user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void createUser() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| Log.i(TAG, "Starting timer"); |
| final int userId = createUserNoFlags(); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests creating a new user, with wait times between iterations. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void createUser_realistic() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| Log.i(TAG, "Starting timer"); |
| final int userId = createUserNoFlags(); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests creating and starting a new user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void createAndStartUser() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| Log.i(TAG, "Starting timer"); |
| final int userId = createUserNoFlags(); |
| |
| // Don't use this.startUserInBackgroundAndWaitForUnlock() since only waiting until |
| // ACTION_USER_STARTED. |
| runThenWaitForBroadcasts(userId, () -> { |
| mIam.startUserInBackground(userId); |
| }, Intent.ACTION_USER_STARTED); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests creating and starting a new user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void createAndStartUser_realistic() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| Log.d(TAG, "Starting timer"); |
| final int userId = createUserNoFlags(); |
| |
| // Don't use this.startUserInBackgroundAndWaitForUnlock() since only waiting until |
| // ACTION_USER_STARTED. |
| runThenWaitForBroadcasts(userId, () -> { |
| mIam.startUserInBackground(userId); |
| }, Intent.ACTION_USER_STARTED); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| removeUser(userId); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests starting an uninitialized user. |
| * Measures the time until ACTION_USER_STARTED is received. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void startUser() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createUserNoFlags(); |
| |
| waitForBroadcastIdle(); |
| runThenWaitForBroadcasts(userId, () -> { |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| mIam.startUserInBackground(userId); |
| }, Intent.ACTION_USER_STARTED); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests starting an uninitialized user, with wait times in between iterations. |
| * |
| * The first iteration will take longer due to the process of setting policy permissions for |
| * a new user. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void startUser_uninitializedUser() throws RemoteException { |
| startUser_measuresAfterFirstIterations(/* numberOfIterationsToSkip */0); |
| } |
| |
| /** |
| * Tests the second iteration of start user that has a problem that it takes too long to run, a |
| * bug has been created (b/266574680) and after investigating or fix this problem, |
| * this test can be removed. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void startUser_startedOnceBefore() throws RemoteException { |
| startUser_measuresAfterFirstIterations(/* numberOfIterationsToSkip */1); |
| } |
| |
| /** |
| * Tests a specific iteration of the start user process. |
| * Measures the time until ACTION_USER_STARTED is received. |
| * @param numberOfIterationsToSkip number of iterations that must be skipped in the preStartUser |
| * method. |
| */ |
| private void startUser_measuresAfterFirstIterations(int numberOfIterationsToSkip) |
| throws RemoteException { |
| /** |
| * Run start user and stop for the next iteration, measures time while mRunner.keepRunning() |
| * return true. |
| */ |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| |
| final int userId = createUserNoFlags(); |
| |
| preStartUser(userId, numberOfIterationsToSkip); |
| |
| waitForBroadcastIdle(); |
| waitCoolDownPeriod(); |
| |
| runThenWaitForBroadcasts(userId, () -> { |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| mIam.startUserInBackground(userId); |
| }, Intent.ACTION_USER_STARTED); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests starting an initialized user, with wait times in between iterations stopping between |
| * iterations,this test will skip the first two iterations and only measure the next ones. |
| * |
| * The first iteration will take longer due to the process of setting policy permissions for |
| * a new user. |
| * |
| * The second iteration takes longer than expected and has a bug (b/266574680) to investigate |
| * it. |
| * |
| * The next iterations take the expected time to start a user. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void startUser_startedTwiceBefore() throws RemoteException { |
| final int userId = createUserNoFlags(); |
| |
| //TODO(b/266681181) Reduce iteration number by 1 after investigation and possible fix. |
| preStartUser(userId, /* numberOfIterations */2); |
| |
| /** |
| * Run start user and stop for the next iteration, measures time while mRunner.keepRunning() |
| * return true. |
| */ |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| |
| waitForBroadcastIdle(); |
| waitCoolDownPeriod(); |
| |
| runThenWaitForBroadcasts(userId, () -> { |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| mIam.startUserInBackground(userId); |
| }, Intent.ACTION_USER_STARTED); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| |
| stopUser(userId, /* force */true); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| |
| removeUser(userId); |
| } |
| |
| |
| /** |
| * Tests starting & unlocking an uninitialized user. |
| * Measures the time until unlock listener is triggered and user is unlocked. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void startAndUnlockUser() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createUserNoFlags(); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| // Waits for UserState.mUnlockProgress.finish(). |
| startUserInBackgroundAndWaitForUnlock(userId); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests starting & unlocking an initialized user, stopping the user at the end simulating real |
| * usage where the user is not removed after created and initialized. |
| * Measures the time until unlock listener is triggered and user is unlocked. |
| * This test will skip the first two iterations and only measure the next ones. |
| * |
| * The first iteration will take longer due to the process of setting policy permissions for a |
| * new user. |
| * |
| * The second iteration takes longer than expected and has a bug (b/266574680) to investigate |
| * it. |
| * |
| * The next iterations take the expected time to start a user. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void startAndUnlockUser_startedTwiceBefore() throws RemoteException { |
| final int userId = createUserNoFlags(); |
| |
| //TODO(b/266681181) Reduce iteration number by 1 after investigation and possible fix. |
| preStartUser(userId, /* numberOfIterations */2); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| |
| waitCoolDownPeriod(); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| // Waits for UserState.mUnlockProgress.finish(). |
| startUserInBackgroundAndWaitForUnlock(userId); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| stopUser(userId, /* force */true); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| |
| removeUser(userId); |
| } |
| |
| /** |
| * Tests starting & unlocking an uninitialized user. |
| * Measures the time until unlock listener is triggered and user is unlocked. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void startAndUnlockUser_realistic() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createUserNoFlags(); |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| |
| // Waits for UserState.mUnlockProgress.finish(). |
| startUserInBackgroundAndWaitForUnlock(userId); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| removeUser(userId); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests switching to an uninitialized user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void switchUser() throws Exception { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int startUser = mAm.getCurrentUser(); |
| final int userId = createUserNoFlags(); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| switchUser(userId); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| switchUserNoCheck(startUser); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests switching to an uninitialized user with wait times between iterations. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void switchUser_realistic() throws Exception { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int startUser = ActivityManager.getCurrentUser(); |
| final int userId = createUserNoFlags(); |
| waitCoolDownPeriod(); |
| Log.d(TAG, "Starting timer"); |
| mRunner.resumeTiming(); |
| |
| switchUser(userId); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| switchUserNoCheck(startUser); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests switching to a previously-started, but no-longer-running, user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void switchUser_stopped() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int startUser = mAm.getCurrentUser(); |
| final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| switchUser(testUser); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| switchUserNoCheck(startUser); |
| removeUser(testUser); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests switching to a previously-started, but no-longer-running, user with wait |
| * times between iterations |
| **/ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void switchUser_stopped_realistic() throws RemoteException { |
| final int currentUserId = ActivityManager.getCurrentUser(); |
| final int userId = initializeNewUserAndSwitchBack(/* stopNewUser */ true); |
| |
| /** |
| * Skip the second iteration of start user process that is taking a long time to finish. |
| */ |
| preStartUser(userId, /* numberOfIterations */1); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| waitCoolDownPeriod(); |
| Log.d(TAG, "Starting timer"); |
| mRunner.resumeTiming(); |
| |
| switchUser(userId); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| switchUserNoCheck(currentUserId); |
| stopUserAfterWaitingForBroadcastIdle(userId, /* force */true); |
| attestFalse("Failed to stop user " + userId, mAm.isUserRunning(userId)); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| removeUser(userId); |
| } |
| |
| /** Tests switching to a previously-started, but no-longer-running, user with wait |
| * times between iterations and using a static wallpaper */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void switchUser_stopped_staticWallpaper() throws RemoteException { |
| assumeTrue(mWm.isWallpaperSupported() && mWm.isSetWallpaperAllowed()); |
| final int startUser = ActivityManager.getCurrentUser(); |
| final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true, |
| /* useStaticWallpaper */true); |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| waitCoolDownPeriod(); |
| Log.d(TAG, "Starting timer"); |
| mRunner.resumeTiming(); |
| |
| switchUser(testUser); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| switchUserNoCheck(startUser); |
| stopUserAfterWaitingForBroadcastIdle(testUser, true); |
| attestFalse("Failed to stop user " + testUser, mAm.isUserRunning(testUser)); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| removeUser(testUser); |
| } |
| |
| /** Tests switching to an already-created already-running non-owner background user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void switchUser_running() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int startUser = mAm.getCurrentUser(); |
| final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| switchUser(testUser); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| switchUserNoCheck(startUser); |
| removeUser(testUser); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests switching to an already-created already-running non-owner background user, with wait |
| * times between iterations */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void switchUser_running_initializedUser() throws RemoteException { |
| final int startUser = ActivityManager.getCurrentUser(); |
| final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false); |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| waitCoolDownPeriod(); |
| Log.d(TAG, "Starting timer"); |
| mRunner.resumeTiming(); |
| |
| switchUser(testUser); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| waitForBroadcastIdle(); |
| switchUserNoCheck(startUser); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| removeUser(testUser); |
| } |
| |
| /** Tests switching to an already-created already-running non-owner background user, with wait |
| * times between iterations and using a default static wallpaper */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void switchUser_running_staticWallpaper() throws RemoteException { |
| assumeTrue(mWm.isWallpaperSupported() && mWm.isSetWallpaperAllowed()); |
| final int startUser = ActivityManager.getCurrentUser(); |
| final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false, |
| /* useStaticWallpaper */ true); |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| waitCoolDownPeriod(); |
| Log.d(TAG, "Starting timer"); |
| mRunner.resumeTiming(); |
| |
| switchUser(testUser); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| waitForBroadcastIdle(); |
| switchUserNoCheck(startUser); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| removeUser(testUser); |
| } |
| |
| /** Tests stopping a background user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void stopUser() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createUserNoFlags(); |
| |
| runThenWaitForBroadcasts(userId, ()-> { |
| mIam.startUserInBackground(userId); |
| }, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED); |
| |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| stopUser(userId, false); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests stopping a background user, with wait times between iterations. The hypothesis is |
| * that the effects of the user creation could impact the measured times, so in this variant we |
| * create one user per run, instead of one per iteration */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void stopUser_realistic() throws RemoteException { |
| final int userId = createUserNoFlags(); |
| waitCoolDownPeriod(); |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| runThenWaitForBroadcasts(userId, ()-> { |
| mIam.startUserInBackground(userId); |
| }, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED); |
| waitCoolDownPeriod(); |
| Log.d(TAG, "Starting timer"); |
| mRunner.resumeTiming(); |
| |
| stopUser(userId, false); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| |
| mRunner.resumeTimingForNextIteration(); |
| } |
| removeUser(userId); |
| } |
| |
| /** Tests reaching LOCKED_BOOT_COMPLETE when switching to uninitialized user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void lockedBootCompleted() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int startUser = mAm.getCurrentUser(); |
| final int userId = createUserNoFlags(); |
| |
| waitForBroadcastIdle(); |
| mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> { |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| mAm.switchUser(userId); |
| }, () -> fail("Failed to achieve onLockedBootComplete for user " + userId)); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| switchUserNoCheck(startUser); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests reaching LOCKED_BOOT_COMPLETE when switching to uninitialized user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void lockedBootCompleted_realistic() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int startUser = ActivityManager.getCurrentUser(); |
| final int userId = createUserNoFlags(); |
| |
| waitCoolDownPeriod(); |
| mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> { |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| mAm.switchUser(userId); |
| }, () -> fail("Failed to achieve onLockedBootComplete for user " + userId)); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| switchUserNoCheck(startUser); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests stopping an ephemeral foreground user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void ephemeralUserStopped() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int startUser = mAm.getCurrentUser(); |
| final int userId = createUserWithFlags(UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO); |
| runThenWaitForBroadcasts(userId, () -> { |
| switchUser(userId); |
| }, Intent.ACTION_MEDIA_MOUNTED); |
| |
| waitForBroadcastIdle(); |
| mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> { |
| runThenWaitForBroadcasts(userId, () -> { |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| mAm.switchUser(startUser); |
| }, Intent.ACTION_USER_STOPPED); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| }, null); |
| |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests stopping an ephemeral foreground user. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void ephemeralUserStopped_realistic() throws RemoteException { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int startUser = ActivityManager.getCurrentUser(); |
| final int userId = createUserWithFlags(UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO); |
| runThenWaitForBroadcasts(userId, () -> { |
| switchUser(userId); |
| }, Intent.ACTION_MEDIA_MOUNTED); |
| |
| waitCoolDownPeriod(); |
| mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> { |
| runThenWaitForBroadcasts(userId, () -> { |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| |
| mAm.switchUser(startUser); |
| }, Intent.ACTION_USER_STOPPED); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| }, null); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests creating a new profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileCreate() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| Log.i(TAG, "Starting timer"); |
| final int userId = createManagedProfile(); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId)); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests creating a new profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileCreate_realistic() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| Log.d(TAG, "Starting timer"); |
| final int userId = createManagedProfile(); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId)); |
| removeUser(userId); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests starting (unlocking) an uninitialized profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlock() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests starting (unlocking) an uninitialized profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlock_realistic() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| removeUser(userId); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlock_stopped() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| // Start the profile initially, then stop it. Similar to setQuietModeEnabled. |
| startUserInBackgroundAndWaitForUnlock(userId); |
| stopUserAfterWaitingForBroadcastIdle(userId, true); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlock_stopped_realistic() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| final int userId = createManagedProfile(); |
| // Start the profile initially, then stop it. Similar to setQuietModeEnabled. |
| startUserInBackgroundAndWaitForUnlock(userId); |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| stopUserAfterWaitingForBroadcastIdle(userId, true); |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| removeUser(userId); |
| } |
| |
| /** |
| * Tests starting (unlocking) & launching an already-installed app in an uninitialized profile. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlockAndLaunchApp() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null); |
| installPreexistingApp(userId, DUMMY_PACKAGE_NAME); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| startApp(userId, DUMMY_PACKAGE_NAME); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests starting (unlocking) & launching an already-installed app in an uninitialized profile. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlockAndLaunchApp_realistic() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null); |
| installPreexistingApp(userId, DUMMY_PACKAGE_NAME); |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| startApp(userId, DUMMY_PACKAGE_NAME); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| removeUser(userId); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests starting (unlocking) and launching a previously-launched app |
| * in a previously-started, but no-longer-running, profile. |
| * A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and |
| * {@link #managedProfileUnlock_stopped}}. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlockAndLaunchApp_stopped() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null); |
| installPreexistingApp(userId, DUMMY_PACKAGE_NAME); |
| startUserInBackgroundAndWaitForUnlock(userId); |
| startApp(userId, DUMMY_PACKAGE_NAME); |
| stopUserAfterWaitingForBroadcastIdle(userId, true); |
| SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile. |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| startApp(userId, DUMMY_PACKAGE_NAME); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests starting (unlocking) and launching a previously-launched app |
| * in a previously-started, but no-longer-running, profile. |
| * A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and |
| * {@link #managedProfileUnlock_stopped}}. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlockAndLaunchApp_stopped_realistic() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null); |
| installPreexistingApp(userId, DUMMY_PACKAGE_NAME); |
| startUserInBackgroundAndWaitForUnlock(userId); |
| startApp(userId, DUMMY_PACKAGE_NAME); |
| stopUserAfterWaitingForBroadcastIdle(userId, true); |
| SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile. |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| startApp(userId, DUMMY_PACKAGE_NAME); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| removeUser(userId); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests installing a pre-existing app in an uninitialized profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileInstall() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| installPreexistingApp(userId, DUMMY_PACKAGE_NAME); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests installing a pre-existing app in an uninitialized profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileInstall_realistic() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| |
| installPreexistingApp(userId, DUMMY_PACKAGE_NAME); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| removeUser(userId); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests creating a new profile, starting (unlocking) it, installing an app, |
| * and launching that app in it. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileCreateUnlockInstallAndLaunchApp() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| final int userId = createManagedProfile(); |
| startUserInBackgroundAndWaitForUnlock(userId); |
| installPreexistingApp(userId, DUMMY_PACKAGE_NAME); |
| startApp(userId, DUMMY_PACKAGE_NAME); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** |
| * Tests creating a new profile, starting (unlocking) it, installing an app, |
| * and launching that app in it. |
| */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileCreateUnlockInstallAndLaunchApp_realistic() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null); |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| |
| final int userId = createManagedProfile(); |
| startUserInBackgroundAndWaitForUnlock(userId); |
| installPreexistingApp(userId, DUMMY_PACKAGE_NAME); |
| startApp(userId, DUMMY_PACKAGE_NAME); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| removeUser(userId); |
| waitCoolDownPeriod(); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests stopping a profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileStopped() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| runThenWaitForBroadcasts(userId, () -> { |
| startUserInBackgroundAndWaitForUnlock(userId); |
| }, Intent.ACTION_MEDIA_MOUNTED); |
| |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| stopUser(userId, true); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } |
| |
| /** Tests stopping a profile. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileStopped_realistic() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| final int userId = createManagedProfile(); |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| |
| runThenWaitForBroadcasts(userId, () -> { |
| startUserInBackgroundAndWaitForUnlock(userId); |
| }, Intent.ACTION_MEDIA_MOUNTED); |
| waitCoolDownPeriod(); |
| mRunner.resumeTiming(); |
| Log.d(TAG, "Starting timer"); |
| |
| stopUser(userId, true); |
| |
| mRunner.pauseTiming(); |
| Log.d(TAG, "Stopping timer"); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| removeUser(userId); |
| } |
| |
| // TODO: This is just a POC. Do this properly and add more. |
| /** Tests starting (unlocking) a newly-created profile using the user-type-pkg-whitelist. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlock_usingWhitelist() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| final int origMode = getUserTypePackageWhitelistMode(); |
| setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE |
| | USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST); |
| |
| try { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } finally { |
| setUserTypePackageWhitelistMode(origMode); |
| } |
| } |
| /** Tests starting (unlocking) a newly-created profile NOT using the user-type-pkg-whitelist. */ |
| @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS) |
| public void managedProfileUnlock_notUsingWhitelist() throws RemoteException { |
| assumeTrue(mHasManagedUserFeature); |
| final int origMode = getUserTypePackageWhitelistMode(); |
| setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE); |
| |
| try { |
| while (mRunner.keepRunning()) { |
| mRunner.pauseTiming(); |
| final int userId = createManagedProfile(); |
| mRunner.resumeTiming(); |
| Log.i(TAG, "Starting timer"); |
| |
| startUserInBackgroundAndWaitForUnlock(userId); |
| |
| mRunner.pauseTiming(); |
| Log.i(TAG, "Stopping timer"); |
| removeUser(userId); |
| mRunner.resumeTimingForNextIteration(); |
| } |
| } finally { |
| setUserTypePackageWhitelistMode(origMode); |
| } |
| } |
| |
| /** Creates a new user, returning its userId. */ |
| private int createUserNoFlags() { |
| return createUserWithFlags(/* flags= */ 0); |
| } |
| |
| /** Creates a new user with the given flags, returning its userId. */ |
| private int createUserWithFlags(int flags) { |
| int userId = mUm.createUser(TEST_USER_NAME, flags).id; |
| mUsersToRemove.add(userId); |
| return userId; |
| } |
| |
| /** Creates a managed (work) profile under the current user, returning its userId. */ |
| private int createManagedProfile() { |
| final UserInfo userInfo = mUm.createProfileForUser(TEST_USER_NAME, |
| UserManager.USER_TYPE_PROFILE_MANAGED, /* flags */ 0, mAm.getCurrentUser()); |
| attestFalse("Creating managed profile failed. Most likely there is " |
| + "already a pre-existing profile on the device.", userInfo == null); |
| mUsersToRemove.add(userInfo.id); |
| return userInfo.id; |
| } |
| |
| /** |
| * Start user in background and wait for it to unlock by waiting for |
| * UserState.mUnlockProgress.finish(). |
| * <p> To start in foreground instead, see {@link #switchUser(int)}. |
| * <p> This should always be used for profiles since profiles cannot be started in foreground. |
| */ |
| private void startUserInBackgroundAndWaitForUnlock(int userId) { |
| try { |
| attestTrue("Failed to start user " + userId + " in background.", |
| ShellHelper.runShellCommandWithTimeout("am start-user -w " + userId, |
| TIMEOUT_IN_SECOND).startsWith("Success:")); |
| } catch (TimeoutException e) { |
| fail("Could not start user " + userId + " in " + TIMEOUT_IN_SECOND + " seconds"); |
| } |
| } |
| |
| /** Starts the given user in the foreground. */ |
| private void switchUser(int userId) throws RemoteException { |
| boolean success = switchUserNoCheck(userId); |
| attestTrue("Failed to properly switch to user " + userId, success); |
| } |
| |
| /** |
| * Starts the given user in the foreground. |
| * Returns true if successful. Does not fail the test if unsuccessful. |
| * If lack of success should fail the test, use {@link #switchUser(int)} instead. |
| */ |
| private boolean switchUserNoCheck(int userId) throws RemoteException { |
| final boolean[] success = {true}; |
| mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(userId, () -> { |
| mAm.switchUser(userId); |
| }, () -> success[0] = false); |
| return success[0]; |
| } |
| |
| /** |
| * Waits for broadcast idle before stopping a user, to prevent timeouts on stop user. |
| * Stopping a user heavily depends on broadcast queue, and that gets crowded after user creation |
| * or user switches, which leads to a timeout on stopping user and cause the tests to be flaky. |
| * Do not call this method while timing is on. i.e. between mRunner.resumeTiming() and |
| * mRunner.pauseTiming(). Otherwise it would cause the test results to be spiky. |
| */ |
| private void stopUserAfterWaitingForBroadcastIdle(int userId, boolean force) |
| throws RemoteException { |
| waitForBroadcastIdle(); |
| stopUser(userId, force); |
| } |
| |
| private void stopUser(int userId, boolean force) throws RemoteException { |
| final CountDownLatch latch = new CountDownLatch(1); |
| mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() { |
| @Override |
| public void userStopped(int userId) throws RemoteException { |
| latch.countDown(); |
| } |
| |
| @Override |
| public void userStopAborted(int userId) throws RemoteException { |
| } |
| }); |
| waitForLatch("Failed to properly stop user " + userId, latch); |
| } |
| |
| private int initializeNewUserAndSwitchBack(boolean stopNewUser) throws RemoteException { |
| return initializeNewUserAndSwitchBack(stopNewUser, /* useStaticWallpaper */ false); |
| } |
| |
| /** |
| * Creates a user and waits for its ACTION_USER_UNLOCKED. |
| * Then switches to back to the original user and waits for its switchUser() to finish. |
| * |
| * @param stopNewUser whether to stop the new user after switching to otherUser. |
| * @param useStaticWallpaper whether to switch the wallpaper of the default user to a static. |
| * @return userId of the newly created user. |
| */ |
| private int initializeNewUserAndSwitchBack(boolean stopNewUser, boolean useStaticWallpaper) |
| throws RemoteException { |
| final int origUser = mAm.getCurrentUser(); |
| // First, create and switch to testUser, waiting for its ACTION_USER_UNLOCKED |
| final int testUser = createUserNoFlags(); |
| runThenWaitForBroadcasts(testUser, () -> { |
| mAm.switchUser(testUser); |
| }, Intent.ACTION_USER_UNLOCKED, Intent.ACTION_MEDIA_MOUNTED); |
| |
| if (useStaticWallpaper) { |
| assertTrue(mWm.isWallpaperSupported() && mWm.isSetWallpaperAllowed()); |
| try { |
| Bitmap blank = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); |
| mWm.setBitmap(blank, /* visibleCropHint */ null, /* allowBackup */ true, |
| /* which */ FLAG_SYSTEM | FLAG_LOCK, testUser); |
| } catch (IOException exception) { |
| fail("Unable to set static wallpaper."); |
| } |
| } |
| |
| // Second, switch back to origUser, waiting merely for switchUser() to finish |
| switchUser(origUser); |
| attestTrue("Didn't switch back to user, " + origUser, origUser == mAm.getCurrentUser()); |
| |
| if (stopNewUser) { |
| stopUserAfterWaitingForBroadcastIdle(testUser, true); |
| attestFalse("Failed to stop user " + testUser, mAm.isUserRunning(testUser)); |
| } |
| |
| return testUser; |
| } |
| |
| /** |
| * Installs the given package in the given user. |
| */ |
| private void installPreexistingApp(int userId, String packageName) throws RemoteException { |
| final CountDownLatch latch = new CountDownLatch(1); |
| |
| final IntentSender sender = new IntentSender((IIntentSender) new IIntentSender.Stub() { |
| @Override |
| public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, |
| IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { |
| latch.countDown(); |
| } |
| }); |
| |
| final IPackageInstaller installer = AppGlobals.getPackageManager().getPackageInstaller(); |
| installer.installExistingPackage(packageName, |
| PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, |
| PackageManager.INSTALL_REASON_UNKNOWN, sender, userId, null); |
| |
| waitForLatch("Failed to install app " + packageName + " on user " + userId, latch); |
| } |
| |
| /** |
| * Launches the given package in the given user. |
| * Make sure the keyguard has been dismissed prior to calling. |
| */ |
| private void startApp(int userId, String packageName) { |
| final String failMessage = "User " + userId + " failed to start " + packageName; |
| final String component = InstrumentationRegistry.getContext().getPackageManager() |
| .getLaunchIntentForPackage(packageName).getComponent().flattenToShortString(); |
| try { |
| final String result = ShellHelper.runShellCommandWithTimeout( |
| "am start -W -n " + component + " --user " + userId, TIMEOUT_IN_SECOND); |
| assertTrue(failMessage + ", component=" + component + ", result=" + result, |
| result.contains("Status: ok") |
| && !result.contains("Warning:") |
| && !result.contains("Error:")); |
| } catch (TimeoutException e) { |
| fail(failMessage + " in " + TIMEOUT_IN_SECOND + " seconds"); |
| } |
| } |
| |
| private class ProgressWaiter extends IProgressListener.Stub { |
| private final CountDownLatch mFinishedLatch = new CountDownLatch(1); |
| |
| @Override |
| public void onStarted(int id, Bundle extras) {} |
| |
| @Override |
| public void onProgress(int id, int progress, Bundle extras) {} |
| |
| @Override |
| public void onFinished(int id, Bundle extras) { |
| mFinishedLatch.countDown(); |
| } |
| |
| public boolean waitForFinish(long timeoutSecs) { |
| try { |
| return mFinishedLatch.await(timeoutSecs, TimeUnit.SECONDS); |
| } catch (InterruptedException e) { |
| Log.e(TAG, "Thread interrupted unexpectedly.", e); |
| return false; |
| } |
| } |
| } |
| |
| /** |
| * Waits TIMEOUT_IN_SECOND for the broadcast to be received, otherwise declares the given error. |
| * It only works for the broadcasts provided in {@link #mBroadcastWaiter}'s instantiation above. |
| * @param userId userId associated with the broadcast. It is {@link Intent#EXTRA_USER_HANDLE} |
| * or in case that is null, then it is {@link BroadcastReceiver#getSendingUserId}. |
| * @param runnable function to be run after clearing any possible previously received broadcasts |
| * and before waiting for the new broadcasts. This function should typically do |
| * something to trigger broadcasts to be sent. Like starting or stopping a user. |
| * @param actions actions of the broadcasts, i.e. {@link Intent#ACTION_USER_STARTED}. |
| * If multiple actions are provided, they will be waited in given order. |
| */ |
| private void runThenWaitForBroadcasts(int userId, FunctionalUtils.ThrowingRunnable runnable, |
| String... actions) { |
| final String unreceivedAction = |
| mBroadcastWaiter.runThenWaitForBroadcasts(userId, runnable, actions); |
| |
| attestTrue("Failed to achieve " + unreceivedAction + " for user " + userId, |
| unreceivedAction == null); |
| } |
| |
| /** Waits TIMEOUT_IN_SECOND for the latch to complete, otherwise declares the given error. */ |
| private void waitForLatch(String errMsg, CountDownLatch latch) { |
| boolean success = false; |
| try { |
| success = latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS); |
| } catch (InterruptedException e) { |
| Log.e(TAG, "Thread interrupted unexpectedly.", e); |
| } |
| attestTrue(errMsg, success); |
| } |
| |
| /** Gets the PACKAGE_WHITELIST_MODE_PROP System Property. */ |
| private int getUserTypePackageWhitelistMode() { |
| return SystemProperties.getInt(PACKAGE_WHITELIST_MODE_PROP, |
| USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT); |
| } |
| |
| /** Sets the PACKAGE_WHITELIST_MODE_PROP System Property to the given value. */ |
| private void setUserTypePackageWhitelistMode(int mode) { |
| String result = ShellHelper.runShellCommand( |
| String.format("setprop %s %d", PACKAGE_WHITELIST_MODE_PROP, mode)); |
| attestFalse("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ": " + result, |
| result != null && result.contains("Failed")); |
| } |
| |
| private void removeUser(int userId) throws RemoteException { |
| stopUserAfterWaitingForBroadcastIdle(userId, true); |
| try { |
| ShellHelper.runShellCommandWithTimeout("pm remove-user -w " + userId, |
| TIMEOUT_IN_SECOND); |
| } catch (TimeoutException e) { |
| Log.e(TAG, String.format("Could not remove user %d in %d seconds", |
| userId, TIMEOUT_IN_SECOND), e); |
| } |
| if (mUm.getUserInfo(userId) != null) { |
| mUsersToRemove.add(userId); |
| } |
| } |
| |
| private void removeAnyPreviousTestUsers() { |
| for (UserInfo user : mUm.getUsers()) { |
| if (TEST_USER_NAME.equals(user.name)) { |
| Log.i(TAG, "Found previous test user " + user.id + ". Removing it."); |
| if (mAm.getCurrentUser() == user.id) { |
| try { |
| switchUserNoCheck(UserHandle.USER_SYSTEM); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Failed to correctly switch to system user", e); |
| } |
| } |
| mUm.removeUser(user.id); |
| } |
| } |
| } |
| |
| /** |
| * Start the user and stop after that, will repeat numberOfIterations times. |
| * Make sure the user is started before proceeding with the test. |
| * @param userId identifier of the user that will be started. |
| * @param numberOfIterations number of iterations that must be skipped. |
| */ |
| private void preStartUser(int userId, int numberOfIterations) throws RemoteException { |
| for (int i = 0; i < numberOfIterations; i++) { |
| final ProgressWaiter preWaiter = new ProgressWaiter(); |
| |
| final boolean preStartComplete = mIam.startUserInBackgroundWithListener(userId, |
| preWaiter) && preWaiter.waitForFinish(TIMEOUT_IN_SECOND * 1000); |
| stopUserAfterWaitingForBroadcastIdle(userId, /* force */true); |
| |
| assertTrue("Pre start was not performed for user" + userId, preStartComplete); |
| } |
| } |
| |
| private void fail(@NonNull String message) { |
| Log.e(TAG, "Test failed on iteration #" + mRunner.getIteration() + ": " + message); |
| mRunner.markAsFailed(new AssertionError(message)); |
| } |
| |
| private void attestTrue(@NonNull String message, boolean assertion) { |
| if (!assertion) { |
| fail(message); |
| } |
| } |
| |
| private void attestFalse(@NonNull String message, boolean assertion) { |
| attestTrue(message, !assertion); |
| } |
| |
| private String setSystemProperty(String name, String value) throws Exception { |
| final String oldValue = ShellHelper.runShellCommand("getprop " + name); |
| assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value)); |
| return TextUtils.firstNotEmpty(oldValue, "invalid"); |
| } |
| |
| private void waitForBroadcastIdle() { |
| try { |
| ShellHelper.runShellCommandWithTimeout("am wait-for-broadcast-idle", TIMEOUT_IN_SECOND); |
| } catch (TimeoutException e) { |
| Log.e(TAG, "Ending waitForBroadcastIdle because it is taking too long", e); |
| } |
| } |
| |
| private void sleep(long ms) { |
| try { |
| Thread.sleep(ms); |
| } catch (InterruptedException e) { |
| // Ignore |
| } |
| } |
| |
| private void waitCoolDownPeriod() { |
| final int tenSeconds = 1000 * 10; |
| waitForBroadcastIdle(); |
| sleep(tenSeconds); |
| } |
| } |