Exclude running code for receivers and observers from timing.

Exclude time used by registering/unregistering broadcast receivers and
user switch observers from the time measuraments of the tests.

In this CL mRunner.resumeTiming(); calls are moved after the
broadcastWaiter initialisation. Also UserSwitchWaiter was not
unregistering UserSwitchObserver. That is imlemented and the class is
refactored to keep registering and unregistering out of the timed
portion by putting those logic into the setUp and the tearDown of the
test.

Bug: 169449457
Test: atest MultiUserPerfTests:android.multiuser.UserLifecycleTests --no-bazel-mode
Change-Id: I04fdaf04ba8440889d60d6cf6f7b5554539af13a
This commit is contained in:
Yasin Kilicdere
2022-08-17 15:52:43 +01:00
parent cb2ec7b31d
commit 94c30cbf9d
2 changed files with 60 additions and 33 deletions

View File

@@ -155,6 +155,7 @@ public class UserLifecycleTests {
public void tearDown() throws Exception {
setSystemProperty("debug.usercontroller.user_switch_timeout_ms", mUserSwitchTimeoutMs);
mBroadcastWaiter.close();
mUserSwitchWaiter.close();
for (int userId : mUsersToRemove) {
try {
mUm.removeUser(userId);
@@ -207,10 +208,10 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
mIam.startUserInBackground(userId);
}, Intent.ACTION_USER_STARTED);
@@ -362,10 +363,10 @@ public class UserLifecycleTests {
}, Intent.ACTION_MEDIA_MOUNTED);
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
mAm.switchUser(startUser);
}, Intent.ACTION_USER_STOPPED);

View File

@@ -17,61 +17,87 @@
package android.multiuser;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.IUserSwitchObserver;
import android.app.UserSwitchObserver;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.util.FunctionalUtils;
import java.util.concurrent.CountDownLatch;
import java.io.Closeable;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class UserSwitchWaiter {
public class UserSwitchWaiter implements Closeable {
private final String mTag;
private final int mTimeoutInSecond;
private final IActivityManager mActivityManager;
private final IUserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
@Override
public void onUserSwitchComplete(int newUserId) {
getSemaphoreSwitchComplete(newUserId).release();
}
public UserSwitchWaiter(String tag, int timeoutInSecond) {
@Override
public void onLockedBootComplete(int newUserId) {
getSemaphoreBootComplete(newUserId).release();
}
};
private final Map<Integer, Semaphore> mSemaphoresMapSwitchComplete = new ConcurrentHashMap<>();
private Semaphore getSemaphoreSwitchComplete(final int userId) {
return mSemaphoresMapSwitchComplete.computeIfAbsent(userId,
(Integer absentKey) -> new Semaphore(0));
}
private final Map<Integer, Semaphore> mSemaphoresMapBootComplete = new ConcurrentHashMap<>();
private Semaphore getSemaphoreBootComplete(final int userId) {
return mSemaphoresMapBootComplete.computeIfAbsent(userId,
(Integer absentKey) -> new Semaphore(0));
}
public UserSwitchWaiter(String tag, int timeoutInSecond) throws RemoteException {
mTag = tag;
mTimeoutInSecond = timeoutInSecond;
mActivityManager = ActivityManager.getService();
mActivityManager.registerUserSwitchObserver(mUserSwitchObserver, mTag);
}
@Override
public void close() throws IOException {
try {
mActivityManager.unregisterUserSwitchObserver(mUserSwitchObserver);
} catch (RemoteException e) {
Log.e(mTag, "Failed to unregister user switch observer", e);
}
}
public void runThenWaitUntilSwitchCompleted(int userId,
FunctionalUtils.ThrowingRunnable runnable, Runnable onFail) throws RemoteException {
final CountDownLatch latch = new CountDownLatch(1);
ActivityManager.getService().registerUserSwitchObserver(
new UserSwitchObserver() {
@Override
public void onUserSwitchComplete(int newUserId) throws RemoteException {
if (userId == newUserId) {
latch.countDown();
}
}
}, mTag);
final Semaphore semaphore = getSemaphoreSwitchComplete(userId);
semaphore.drainPermits();
runnable.run();
waitForLatch(latch, onFail);
waitForSemaphore(semaphore, onFail);
}
public void runThenWaitUntilBootCompleted(int userId,
FunctionalUtils.ThrowingRunnable runnable, Runnable onFail) throws RemoteException {
final CountDownLatch latch = new CountDownLatch(1);
ActivityManager.getService().registerUserSwitchObserver(
new UserSwitchObserver() {
@Override
public void onLockedBootComplete(int newUserId) {
if (userId == newUserId) {
latch.countDown();
}
}
}, mTag);
final Semaphore semaphore = getSemaphoreBootComplete(userId);
semaphore.drainPermits();
runnable.run();
waitForLatch(latch, onFail);
waitForSemaphore(semaphore, onFail);
}
private void waitForLatch(CountDownLatch latch, Runnable onFail) {
private void waitForSemaphore(Semaphore semaphore, Runnable onFail) {
boolean success = false;
try {
success = latch.await(mTimeoutInSecond, TimeUnit.SECONDS);
success = semaphore.tryAcquire(mTimeoutInSecond, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.e(mTag, "Thread interrupted unexpectedly.", e);
}