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

Commit 03bfe6f5 authored by Bookatz's avatar Bookatz
Browse files

ActivityManager start-user has wait flag

Previously, when "am start-user" was called, the call would return
immediately, even though it might take some time to actually start the
user. This caused some CTS failures.

Here, we add an option for the call to wait until the user has been
started.

Note that 'started' here means UserState.RUNNING_UNLOCKED.
This uses UserController.startUser()'s IProgressListener argument to
wait for completion, but that currently waits until the user reaches
RUNNING_UNLOCKED. If the user has a lock, then unless it is unlocked,
the call will wait until it times out and will declare it failed,
even though the user has actually started (albeit is locked).

Bug: 126263999
Test: manual test of am start-user with and without -w
Change-Id: I6cb5d13cafda0f8708061923654106c504757564
parent 229ae4a4
Loading
Loading
Loading
Loading
+49 −4
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ import android.opengl.GLES10;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IProgressListener;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallback;
import android.os.RemoteCallback.OnResultListener;
@@ -103,6 +104,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
@@ -114,6 +116,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
    public static final String NO_CLASS_ERROR_CODE = "Error type 3";
    private static final String SHELL_PACKAGE_NAME = "com.android.shell";

    private static final int USER_OPERATION_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes

    // IPC interface to activity manager -- don't need to do additional security checks.
    final IActivityManager mInterface;
    final IActivityTaskManager mTaskInterface;
@@ -377,6 +381,30 @@ final class ActivityManagerShellCommand extends ShellCommand {
        });
    }

    private class ProgressWaiter extends IProgressListener.Stub {
        private final CountDownLatch mFinishedLatch = new CountDownLatch(1);

        @Override
        public void onStarted(int id, Bundle extras) {}

        @Override
        public void onProgress(int id, int progress, Bundle extras) {}

        @Override
        public void onFinished(int id, Bundle extras) {
            mFinishedLatch.countDown();
        }

        public boolean waitForFinish(long timeoutMillis) {
            try {
                return mFinishedLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                System.err.println("Thread interrupted unexpectedly.");
                return false;
            }
        }
    }

    int runStartActivity(PrintWriter pw) throws RemoteException {
        Intent intent;
        try {
@@ -1696,8 +1724,24 @@ final class ActivityManagerShellCommand extends ShellCommand {
    }

    int runStartUser(PrintWriter pw) throws RemoteException {
        String user = getNextArgRequired();
        boolean success = mInterface.startUserInBackground(Integer.parseInt(user));
        boolean wait = false;
        String opt;
        while ((opt = getNextOption()) != null) {
            if ("-w".equals(opt)) {
                wait = true;
            } else {
                getErrPrintWriter().println("Error: unknown option: " + opt);
                return -1;
            }
        }
        int userId = Integer.parseInt(getNextArgRequired());

        final ProgressWaiter waiter = wait ? new ProgressWaiter() : null;
        boolean success = mInterface.startUserInBackgroundWithListener(userId, waiter);
        if (wait && success) {
            success = waiter.waitForFinish(USER_OPERATION_TIMEOUT_MS);
        }

        if (success) {
            pw.println("Success: user started");
        } else {
@@ -3027,9 +3071,10 @@ final class ActivityManagerShellCommand extends ShellCommand {
            pw.println("      execution of that user if it is currently stopped.");
            pw.println("  get-current-user");
            pw.println("      Returns id of the current foreground user.");
            pw.println("  start-user <USER_ID>");
            pw.println("  start-user [-w] <USER_ID>");
            pw.println("      Start USER_ID in background if it is currently stopped;");
            pw.println("      use switch-user if you want to start the user in foreground");
            pw.println("      use switch-user if you want to start the user in foreground.");
            pw.println("      -w: wait for start-user to complete and the user to be unlocked.");
            pw.println("  unlock-user <USER_ID> [TOKEN_HEX]");
            pw.println("      Attempt to unlock the given user using the given authorization token.");
            pw.println("  stop-user [-w] [-f] <USER_ID>");
+10 −0
Original line number Diff line number Diff line
@@ -938,6 +938,7 @@ class UserController implements Handler.Callback {
     *
     * @param userId ID of the user to start
     * @param foreground true if user should be brought to the foreground
     * @param unlockListener Listener to be informed when the user has started and unlocked.
     * @return true if the user has been successfully started
     */
    boolean startUser(
@@ -962,6 +963,15 @@ class UserController implements Handler.Callback {
        try {
            final int oldUserId = getCurrentUserId();
            if (oldUserId == userId) {
                final UserState state = getStartedUserState(userId);
                if (state != null && state.state == STATE_RUNNING_UNLOCKED) {
                    // We'll skip all later code, so we must tell listener it's already unlocked.
                    try {
                        unlockListener.onFinished(userId, null);
                    } catch (RemoteException ignore) {
                        // Ignore.
                    }
                }
                return true;
            }