Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 94c30cbf authored by Yasin Kilicdere's avatar Yasin Kilicdere
Browse files

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
parent cb2ec7b3
Loading
Loading
Loading
Loading
+7 −6
Original line number Diff line number Diff line
@@ -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();
            runThenWaitForBroadcasts(userId, () -> {
                mRunner.resumeTiming();
                Log.i(TAG, "Starting timer");

            runThenWaitForBroadcasts(userId, () -> {
                mIam.startUserInBackground(userId);
            }, Intent.ACTION_USER_STARTED);

@@ -362,10 +363,10 @@ public class UserLifecycleTests {
            }, Intent.ACTION_MEDIA_MOUNTED);

            mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
                runThenWaitForBroadcasts(userId, () -> {
                    mRunner.resumeTiming();
                    Log.i(TAG, "Starting timer");

                runThenWaitForBroadcasts(userId, () -> {
                    mAm.switchUser(startUser);
                }, Intent.ACTION_USER_STOPPED);

+53 −27
Original line number Diff line number Diff line
@@ -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();
        }

        @Override
        public void onLockedBootComplete(int newUserId) {
            getSemaphoreBootComplete(newUserId).release();
        }
    };

    public UserSwitchWaiter(String tag, int timeoutInSecond) {
    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);
    }

    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();
    public void close() throws IOException {
        try {
            mActivityManager.unregisterUserSwitchObserver(mUserSwitchObserver);
        } catch (RemoteException e) {
            Log.e(mTag, "Failed to unregister user switch observer", e);
        }
    }
                }, mTag);

    public void runThenWaitUntilSwitchCompleted(int userId,
            FunctionalUtils.ThrowingRunnable runnable, Runnable onFail) throws RemoteException {
        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);
        }