Merge "Add test method UserLifecycleStressTest.switchToExistingGuestAndStartOverStressTest" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-05-19 02:09:28 +00:00
committed by Android (Google) Code Review

View File

@@ -15,11 +15,15 @@
*/ */
package com.android.server.pm; package com.android.server.pm;
import static org.junit.Assert.assertNotNull; import static android.os.UserHandle.USER_NULL;
import static org.junit.Assert.assertTrue; import static android.os.UserHandle.USER_SYSTEM;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.IStopUserCallback; import android.app.IStopUserCallback;
import android.app.UserSwitchObserver;
import android.content.Context; import android.content.Context;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.os.RemoteException; import android.os.RemoteException;
@@ -31,17 +35,18 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest; import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.FunctionalUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* To run the test: * To run the test:
* bit FrameworksServicesTests:com.android.server.pm.UserLifecycleStressTest * atest FrameworksServicesTests:com.android.server.pm.UserLifecycleStressTest
*/ */
@Postsubmit @Postsubmit
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@@ -49,8 +54,8 @@ import java.util.concurrent.TimeUnit;
public class UserLifecycleStressTest { public class UserLifecycleStressTest {
private static final String TAG = "UserLifecycleStressTest"; private static final String TAG = "UserLifecycleStressTest";
// TODO: Make this smaller once we have improved it. // TODO: Make this smaller once we have improved it.
private static final int MAX_TIME_STOP_USER_IN_SECOND = 30; private static final int TIMEOUT_IN_SECOND = 40;
private static final int NUM_ITERATIONS_STOP_USER = 10; private static final int NUM_ITERATIONS = 10;
private static final int WAIT_BEFORE_STOP_USER_IN_SECOND = 3; private static final int WAIT_BEFORE_STOP_USER_IN_SECOND = 3;
private Context mContext; private Context mContext;
@@ -65,20 +70,19 @@ public class UserLifecycleStressTest {
} }
/** /**
* Create and stop user 10 times in a row. Check stop user can be finished in a reasonable * Create and stop user {@link #NUM_ITERATIONS} times in a row. Check stop user can be finished
* amount of time. * in a reasonable amount of time.
*/ */
@Test @Test
public void stopManagedProfileStressTest() public void stopManagedProfileStressTest() throws RemoteException, InterruptedException {
throws IOException, RemoteException, InterruptedException { for (int i = 0; i < NUM_ITERATIONS; i++) {
for (int i = 0; i < NUM_ITERATIONS_STOP_USER; i++) {
final UserInfo userInfo = mUserManager.createProfileForUser("TestUser", final UserInfo userInfo = mUserManager.createProfileForUser("TestUser",
UserManager.USER_TYPE_PROFILE_MANAGED, 0, mActivityManager.getCurrentUser()); UserManager.USER_TYPE_PROFILE_MANAGED, 0, mActivityManager.getCurrentUser());
assertNotNull(userInfo); assertThat(userInfo).isNotNull();
try { try {
assertTrue( assertWithMessage("Failed to start the profile")
"Failed to start the profile", .that(ActivityManager.getService().startUserInBackground(userInfo.id))
ActivityManager.getService().startUserInBackground(userInfo.id)); .isTrue();
// Seems the broadcast queue is getting more busy if we wait a few seconds before // Seems the broadcast queue is getting more busy if we wait a few seconds before
// stopping the user. // stopping the user.
TimeUnit.SECONDS.sleep(WAIT_BEFORE_STOP_USER_IN_SECOND); TimeUnit.SECONDS.sleep(WAIT_BEFORE_STOP_USER_IN_SECOND);
@@ -89,28 +93,134 @@ public class UserLifecycleStressTest {
} }
} }
/**
* Starts over the guest user {@link #NUM_ITERATIONS} times in a row.
*
* Starting over the guest means the following:
* 1. While the guest user is in foreground, mark it for deletion.
* 2. Create a new guest. (This wouldn't be possible if the old one wasn't marked for deletion)
* 3. Switch to newly created guest.
* 4. Remove the previous guest before waiting for switch to complete.
**/
@Test
public void switchToExistingGuestAndStartOverStressTest() throws Exception {
if (ActivityManager.getCurrentUser() != USER_SYSTEM) {
switchUser(USER_SYSTEM);
}
final UserInfo foundGuest = mUserManager.findCurrentGuestUser();
int nextGuestId = foundGuest == null ? USER_NULL : foundGuest.id;
for (int i = 0; i < NUM_ITERATIONS; i++) {
final int currentGuestId = nextGuestId;
Log.d(TAG, "switchToExistingGuestAndStartOverStressTest"
+ " - Run " + (i + 1) + " / " + NUM_ITERATIONS);
if (currentGuestId != USER_NULL) {
Log.d(TAG, "Switching to the existing guest");
switchUser(currentGuestId);
Log.d(TAG, "Marking current guest for deletion");
assertWithMessage("Couldn't mark guest for deletion")
.that(mUserManager.markGuestForDeletion(currentGuestId))
.isTrue();
}
Log.d(TAG, "Creating a new guest");
final UserInfo newGuest = mUserManager.createGuest(mContext);
assertWithMessage("Couldn't create new guest")
.that(newGuest)
.isNotNull();
Log.d(TAG, "Switching to the new guest");
switchUserThenRun(newGuest.id, () -> {
if (currentGuestId != USER_NULL) {
Log.d(TAG, "Removing the previous guest before waiting for switch to complete");
assertWithMessage("Couldn't remove guest")
.that(mUserManager.removeUser(currentGuestId))
.isTrue();
}
});
Log.d(TAG, "Switching back to the system user");
switchUser(USER_SYSTEM);
nextGuestId = newGuest.id;
}
if (nextGuestId != USER_NULL) {
Log.d(TAG, "Removing the last created guest user");
mUserManager.removeUser(nextGuestId);
}
Log.d(TAG, "testSwitchToExistingGuestAndStartOver - End");
}
/** Stops the given user and waits for the stop to finish. */
private void stopUser(int userId) throws RemoteException, InterruptedException { private void stopUser(int userId) throws RemoteException, InterruptedException {
final long startTime = System.currentTimeMillis(); runWithLatch("stop user", countDownLatch -> {
CountDownLatch countDownLatch = new CountDownLatch(1); ActivityManager.getService()
ActivityManager.getService(). .stopUser(userId, /* force= */ true, new IStopUserCallback.Stub() {
stopUser(userId, true,
new IStopUserCallback.Stub() {
@Override @Override
public void userStopped(int userId) throws RemoteException { public void userStopped(int userId) {
countDownLatch.countDown(); countDownLatch.countDown();
} }
@Override @Override
public void userStopAborted(int userId) throws RemoteException { public void userStopAborted(int i) throws RemoteException {
} }
}); });
boolean stoppedBeforeTimeout = });
countDownLatch.await(MAX_TIME_STOP_USER_IN_SECOND, TimeUnit.SECONDS); }
assertTrue(
"Take more than " + MAX_TIME_STOP_USER_IN_SECOND + "s to stop user", /** Starts the given user in the foreground and waits for the switch to finish. */
stoppedBeforeTimeout); private void switchUser(int userId) throws RemoteException, InterruptedException {
Log.d(TAG, "stopUser takes " + (System.currentTimeMillis() - startTime) + " ms"); switchUserThenRun(userId, null);
}
/**
* Starts the given user in the foreground. And runs the given Runnable right after
* am.switchUser call, before waiting for the actual user switch to be complete.
**/
private void switchUserThenRun(int userId, Runnable runAfterSwitchBeforeWait)
throws RemoteException, InterruptedException {
runWithLatch("switch user", countDownLatch -> {
ActivityManager.getService().registerUserSwitchObserver(
new UserSwitchObserver() {
@Override
public void onUserSwitchComplete(int newUserId) {
if (userId == newUserId) {
countDownLatch.countDown();
}
}
}, TAG);
Log.d(TAG, "Switching to user " + userId);
assertWithMessage("Failed to switch to user")
.that(mActivityManager.switchUser(userId))
.isTrue();
if (runAfterSwitchBeforeWait != null) {
runAfterSwitchBeforeWait.run();
}
});
}
/**
* Calls the given consumer with a CountDownLatch parameter, and expects it's countDown() method
* to be called before timeout, or fails the test otherwise.
*/
private void runWithLatch(String tag,
FunctionalUtils.RemoteExceptionIgnoringConsumer<CountDownLatch> consumer)
throws RemoteException, InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
final long startTime = System.currentTimeMillis();
consumer.acceptOrThrow(countDownLatch);
final boolean doneBeforeTimeout = countDownLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
assertWithMessage("Took more than " + TIMEOUT_IN_SECOND + "s to " + tag)
.that(doneBeforeTimeout)
.isTrue();
final long elapsedTime = System.currentTimeMillis() - startTime;
Log.d(TAG, tag + " takes " + elapsedTime + " ms");
} }
} }