Add test method UserLifecycleStressTest.switchToExistingGuestAndStartOverStressTest
This test method tests the following scenario consecutively 10 times: 1. Switch to existing guest 2. Mark it for deletion 3. Create and switch to a new guest 4. Remove previous guest 5. Switch to owner Bug: 228022618 Test: atest com.android.server.pm.UserLifecycleStressTest Change-Id: I38c3f57e8b008ca406813f0a5c82d9afa27aba6c
This commit is contained in:
@@ -15,11 +15,15 @@
|
||||
*/
|
||||
package com.android.server.pm;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static android.os.UserHandle.USER_NULL;
|
||||
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.IStopUserCallback;
|
||||
import android.app.UserSwitchObserver;
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.RemoteException;
|
||||
@@ -31,17 +35,18 @@ import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.util.FunctionalUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* To run the test:
|
||||
* bit FrameworksServicesTests:com.android.server.pm.UserLifecycleStressTest
|
||||
* atest FrameworksServicesTests:com.android.server.pm.UserLifecycleStressTest
|
||||
*/
|
||||
@Postsubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@@ -49,8 +54,8 @@ import java.util.concurrent.TimeUnit;
|
||||
public class UserLifecycleStressTest {
|
||||
private static final String TAG = "UserLifecycleStressTest";
|
||||
// TODO: Make this smaller once we have improved it.
|
||||
private static final int MAX_TIME_STOP_USER_IN_SECOND = 30;
|
||||
private static final int NUM_ITERATIONS_STOP_USER = 10;
|
||||
private static final int TIMEOUT_IN_SECOND = 40;
|
||||
private static final int NUM_ITERATIONS = 10;
|
||||
private static final int WAIT_BEFORE_STOP_USER_IN_SECOND = 3;
|
||||
|
||||
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
|
||||
* amount of time.
|
||||
* Create and stop user {@link #NUM_ITERATIONS} times in a row. Check stop user can be finished
|
||||
* in a reasonable amount of time.
|
||||
*/
|
||||
@Test
|
||||
public void stopManagedProfileStressTest()
|
||||
throws IOException, RemoteException, InterruptedException {
|
||||
for (int i = 0; i < NUM_ITERATIONS_STOP_USER; i++) {
|
||||
public void stopManagedProfileStressTest() throws RemoteException, InterruptedException {
|
||||
for (int i = 0; i < NUM_ITERATIONS; i++) {
|
||||
final UserInfo userInfo = mUserManager.createProfileForUser("TestUser",
|
||||
UserManager.USER_TYPE_PROFILE_MANAGED, 0, mActivityManager.getCurrentUser());
|
||||
assertNotNull(userInfo);
|
||||
assertThat(userInfo).isNotNull();
|
||||
try {
|
||||
assertTrue(
|
||||
"Failed to start the profile",
|
||||
ActivityManager.getService().startUserInBackground(userInfo.id));
|
||||
assertWithMessage("Failed to start the profile")
|
||||
.that(ActivityManager.getService().startUserInBackground(userInfo.id))
|
||||
.isTrue();
|
||||
// Seems the broadcast queue is getting more busy if we wait a few seconds before
|
||||
// stopping the user.
|
||||
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 {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
ActivityManager.getService().
|
||||
stopUser(userId, true,
|
||||
new IStopUserCallback.Stub() {
|
||||
@Override
|
||||
public void userStopped(int userId) throws RemoteException {
|
||||
runWithLatch("stop user", countDownLatch -> {
|
||||
ActivityManager.getService()
|
||||
.stopUser(userId, /* force= */ true, new IStopUserCallback.Stub() {
|
||||
@Override
|
||||
public void userStopped(int userId) {
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userStopAborted(int i) throws RemoteException {
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Starts the given user in the foreground and waits for the switch to finish. */
|
||||
private void switchUser(int userId) throws RemoteException, InterruptedException {
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userStopAborted(int userId) throws RemoteException {
|
||||
/**
|
||||
* 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();
|
||||
|
||||
}
|
||||
});
|
||||
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",
|
||||
stoppedBeforeTimeout);
|
||||
Log.d(TAG, "stopUser takes " + (System.currentTimeMillis() - startTime) + " ms");
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user