From 1226ec462d46494396173a1376b7a617129a96fa Mon Sep 17 00:00:00 2001 From: Adam Bookatz Date: Tue, 17 Aug 2021 12:43:28 -0700 Subject: [PATCH] UserLifecycleTests: wait on storage before restart When a user is stopped shortly after having started, the timing results depend heavily on whether StorageManagerService had managed to forge its connections yet. If it didn't, then stopping takes an unrealistically short time. If it did, but is still in the process of dealing with them, then stopping takes an unrealistically long time. The solution is, for all appliable tests, to wait until storage is ready before proceeding to stop or switch the user. Also, some javadoc comments are added and clarified. Bug: 169449457 Test: atest MultiUserPerfTests:android.multiuser.UserLifecycleTests Change-Id: Ide848685637abac43488c8d89a3d6f6567c7deca --- .../android/multiuser/UserLifecycleTests.java | 72 ++++++++++++++++--- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java index 42ef80c495b31..9971cd42fbb2e 100644 --- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java +++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java @@ -26,6 +26,7 @@ import android.app.IStopUserCallback; import android.app.UserSwitchObserver; import android.app.WaitResult; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.IIntentReceiver; import android.content.IIntentSender; @@ -142,6 +143,7 @@ public class UserLifecycleTests { } } + /** Tests creating a new user. */ @Test public void createUser() { while (mRunner.keepRunning()) { @@ -155,6 +157,7 @@ public class UserLifecycleTests { } } + /** Tests creating and starting a new user. */ @Test public void createAndStartUser() throws RemoteException { while (mRunner.keepRunning()) { @@ -176,6 +179,7 @@ public class UserLifecycleTests { } /** + * Tests starting an uninitialized user. * Measures the time until ACTION_USER_STARTED is received. */ @Test @@ -199,6 +203,7 @@ public class UserLifecycleTests { } /** + * Tests starting & unlocking an uninitialized user. * Measures the time until unlock listener is triggered and user is unlocked. */ @Test @@ -219,6 +224,7 @@ public class UserLifecycleTests { } } + /** Tests switching to an uninitialized user. */ @Test public void switchUser() throws RemoteException { while (mRunner.keepRunning()) { @@ -238,7 +244,7 @@ public class UserLifecycleTests { } } - /** Tests switching to an already-created, but no-longer-running, user. */ + /** Tests switching to a previously-started, but no-longer-running, user. */ @Test public void switchUser_stopped() throws RemoteException { while (mRunner.keepRunning()) { @@ -262,7 +268,7 @@ public class UserLifecycleTests { } } - /** Tests switching to an already-created already-running non-owner user. */ + /** Tests switching to an already-created already-running non-owner background user. */ @Test public void switchUser_running() throws RemoteException { while (mRunner.keepRunning()) { @@ -282,15 +288,19 @@ public class UserLifecycleTests { } } + /** Tests stopping a background user. */ @Test public void stopUser() throws RemoteException { while (mRunner.keepRunning()) { mRunner.pauseTiming(); final int userId = createUserNoFlags(); - final CountDownLatch latch = new CountDownLatch(1); - registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId); + final CountDownLatch latch1 = new CountDownLatch(1); + final CountDownLatch latch2 = new CountDownLatch(1); + registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch1, userId); + registerMediaBroadcastReceiver(latch2, userId); mIam.startUserInBackground(userId); - waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch); + waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch1); + waitForLatch("Failed to achieve ACTION_MEDIA_MOUNTED for user " + userId, latch2); Log.i(TAG, "Starting timer"); mRunner.resumeTiming(); @@ -303,6 +313,7 @@ public class UserLifecycleTests { } } + /** Tests reaching LOOKED_BOOT_COMPLETE when switching to uninitialized user. */ @Test public void lockedBootCompleted() throws RemoteException { while (mRunner.keepRunning()) { @@ -325,13 +336,17 @@ public class UserLifecycleTests { } } + /** Tests stopping an ephemeral foreground user. */ @Test 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); + final CountDownLatch prelatch = new CountDownLatch(1); + registerMediaBroadcastReceiver(prelatch, userId); switchUser(userId); + waitForLatch("Failed to achieve ACTION_MEDIA_MOUNTED for user " + userId, prelatch); final CountDownLatch latch = new CountDownLatch(1); InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() { @Override @@ -379,7 +394,7 @@ public class UserLifecycleTests { } } - /** Tests starting (unlocking) a newly-created profile. */ + /** Tests starting (unlocking) an uninitialized profile. */ @Test public void managedProfileUnlock() { assumeTrue(mHasManagedUserFeature); @@ -399,7 +414,7 @@ public class UserLifecycleTests { } } - /** Tests starting (unlocking) an already-created, but no-longer-running, profile. */ + /** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */ @Test public void managedProfileUnlock_stopped() throws RemoteException { assumeTrue(mHasManagedUserFeature); @@ -423,7 +438,7 @@ public class UserLifecycleTests { } /** - * Tests starting (unlocking) and launching an already-installed app in a newly-created profile. + * Tests starting (unlocking) & launching an already-installed app in an uninitialized profile. */ @Test public void managedProfileUnlockAndLaunchApp() throws RemoteException { @@ -449,7 +464,7 @@ public class UserLifecycleTests { /** * Tests starting (unlocking) and launching a previously-launched app - * in an already-created, but no-longer-running, profile. + * in a previously-started, but no-longer-running, profile. * A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and * {@link #managedProfileUnlock_stopped}}. */ @@ -479,7 +494,7 @@ public class UserLifecycleTests { } } - /** Tests installing a pre-existing app in a newly-created profile. */ + /** Tests installing a pre-existing app in an uninitialized profile. */ @Test public void managedProfileInstall() throws RemoteException { assumeTrue(mHasManagedUserFeature); @@ -533,7 +548,10 @@ public class UserLifecycleTests { while (mRunner.keepRunning()) { mRunner.pauseTiming(); final int userId = createManagedProfile(); + final CountDownLatch prelatch = new CountDownLatch(1); + registerMediaBroadcastReceiver(prelatch, userId); startUserInBackgroundAndWaitForUnlock(userId); + waitForLatch("Failed to achieve ACTION_MEDIA_MOUNTED for user " + userId, prelatch); Log.i(TAG, "Starting timer"); mRunner.resumeTiming(); @@ -689,9 +707,12 @@ public class UserLifecycleTests { // First, create and switch to testUser, waiting for its ACTION_USER_UNLOCKED final int testUser = createUserNoFlags(); final CountDownLatch latch1 = new CountDownLatch(1); + final CountDownLatch latch2 = new CountDownLatch(1); registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch1, testUser); + registerMediaBroadcastReceiver(latch2, testUser); mAm.switchUser(testUser); waitForLatch("Failed to achieve initial ACTION_USER_UNLOCKED for user " + testUser, latch1); + waitForLatch("Failed to achieve initial ACTION_MEDIA_MOUNTED for user " + testUser, latch2); // Second, switch back to origUser, waiting merely for switchUser() to finish switchUser(origUser); @@ -774,6 +795,37 @@ public class UserLifecycleTests { }, UserHandle.of(userId), new IntentFilter(action), null, null); } + /** + * Register for a broadcast to indicate that Storage has processed the given user. + * Without this as part of setup, for tests dealing with already-switched users, Storage may not + * have finished, making the resulting processing inconsistent. + * + * Strictly speaking, the receiver should always be unregistered afterwards, but we don't + * necessarily bother since receivers from failed tests will be removed on test uninstallation. + */ + private void registerMediaBroadcastReceiver(final CountDownLatch latch, final int userId) { + final String action = Intent.ACTION_MEDIA_MOUNTED; + + final IntentFilter filter = new IntentFilter(); + filter.addAction(action); + filter.addDataScheme(ContentResolver.SCHEME_FILE); + + final Context context = InstrumentationRegistry.getContext(); + context.registerReceiverAsUser(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String data = intent.getDataString(); + if (action.equals(intent.getAction())) { + Log.d(TAG, "Received ACTION_MEDIA_MOUNTED with " + data); + if (data != null && data.contains("/" + userId)) { + latch.countDown(); + context.unregisterReceiver(this); + } + } + } + }, UserHandle.of(userId), filter, null, null); + } + private class ProgressWaiter extends IProgressListener.Stub { private final CountDownLatch mFinishedLatch = new CountDownLatch(1);