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

Commit 1c3c9487 authored by Felipe Leme's avatar Felipe Leme Committed by Android (Google) Code Review
Browse files

Merge "Initial implementation of "start bg user on secondary display"."

parents 413b5d9f 4264560e
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -760,4 +760,15 @@ interface IActivityManager {
     * </p>
     * </p>
     */
     */
    int getBackgroundRestrictionExemptionReason(int uid);
    int getBackgroundRestrictionExemptionReason(int uid);

    // Start (?) of T transactions
    /**
     * Similar to {@link #startUserInBackgroundWithListener(int userId, IProgressListener unlockProgressListener),
     * but setting the user as the visible user of that display (i.e., allowing the user and its
     * running profiles to launch activities on that display).
     *
     * <p>Typically used only by automotive builds when the vehicle has multiple displays.
     */
    boolean startUserInBackgroundOnSecondaryDisplay(int userid, int displayId, IProgressListener unlockProgressListener);

}
}
+7 −0
Original line number Original line Diff line number Diff line
@@ -16201,6 +16201,13 @@ public class ActivityManagerService extends IActivityManager.Stub
        return mUserController.startUser(userId, /* foreground */ true, unlockListener);
        return mUserController.startUser(userId, /* foreground */ true, unlockListener);
    }
    }
    @Override
    public boolean startUserInBackgroundOnSecondaryDisplay(int userId, int displayId,
            @Nullable IProgressListener unlockListener) {
        // Permission check done inside UserController.
        return mUserController.startUserOnSecondaryDisplay(userId, displayId, unlockListener);
    }
    /**
    /**
     * Unlocks the given user.
     * Unlocks the given user.
     *
     *
+38 −13
Original line number Original line Diff line number Diff line
@@ -892,6 +892,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
        }
        }
    }
    }


    // TODO(b/239982558): might need to support --displayId as well
    private int runProfile(PrintWriter pw) throws RemoteException {
    private int runProfile(PrintWriter pw) throws RemoteException {
        final PrintWriter err = getErrPrintWriter();
        final PrintWriter err = getErrPrintWriter();
        String profileFile = null;
        String profileFile = null;
@@ -2034,10 +2035,16 @@ final class ActivityManagerShellCommand extends ShellCommand {
    int runStartUser(PrintWriter pw) throws RemoteException {
    int runStartUser(PrintWriter pw) throws RemoteException {
        boolean wait = false;
        boolean wait = false;
        String opt;
        String opt;
        int displayId = Display.INVALID_DISPLAY;
        while ((opt = getNextOption()) != null) {
        while ((opt = getNextOption()) != null) {
            if ("-w".equals(opt)) {
            switch(opt) {
                case "-w":
                    wait = true;
                    wait = true;
            } else {
                    break;
                case "--display":
                    displayId = getDisplayIdFromNextArg();
                    break;
                default:
                    getErrPrintWriter().println("Error: unknown option: " + opt);
                    getErrPrintWriter().println("Error: unknown option: " + opt);
                    return -1;
                    return -1;
            }
            }
@@ -2045,15 +2052,25 @@ final class ActivityManagerShellCommand extends ShellCommand {
        int userId = Integer.parseInt(getNextArgRequired());
        int userId = Integer.parseInt(getNextArgRequired());


        final ProgressWaiter waiter = wait ? new ProgressWaiter() : null;
        final ProgressWaiter waiter = wait ? new ProgressWaiter() : null;
        boolean success = mInterface.startUserInBackgroundWithListener(userId, waiter);

        boolean success;
        String displaySuffix;

        if (displayId == Display.INVALID_DISPLAY) {
            success = mInterface.startUserInBackgroundWithListener(userId, waiter);
            displaySuffix = "";
        } else {
            success = mInterface.startUserInBackgroundOnSecondaryDisplay(userId, displayId, waiter);
            displaySuffix = " on display " + displayId;
        }
        if (wait && success) {
        if (wait && success) {
            success = waiter.waitForFinish(USER_OPERATION_TIMEOUT_MS);
            success = waiter.waitForFinish(USER_OPERATION_TIMEOUT_MS);
        }
        }


        if (success) {
        if (success) {
            pw.println("Success: user started");
            pw.println("Success: user started" + displaySuffix);
        } else {
        } else {
            getErrPrintWriter().println("Error: could not start user");
            getErrPrintWriter().println("Error: could not start user" + displaySuffix);
        }
        }
        return 0;
        return 0;
    }
    }
@@ -2506,6 +2523,14 @@ final class ActivityManagerShellCommand extends ShellCommand {
        }
        }
    }
    }


    private int getDisplayIdFromNextArg() {
        int displayId = Integer.parseInt(getNextArgRequired());
        if (displayId < 0) {
            throw new IllegalArgumentException("--display must be a non-negative integer");
        }
        return displayId;
    }

    int runGetConfig(PrintWriter pw) throws RemoteException {
    int runGetConfig(PrintWriter pw) throws RemoteException {
        int days = -1;
        int days = -1;
        int displayId = Display.DEFAULT_DISPLAY;
        int displayId = Display.DEFAULT_DISPLAY;
@@ -2524,10 +2549,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
            } else if (opt.equals("--device")) {
            } else if (opt.equals("--device")) {
                inclDevice = true;
                inclDevice = true;
            } else if (opt.equals("--display")) {
            } else if (opt.equals("--display")) {
                displayId = Integer.parseInt(getNextArgRequired());
                displayId = getDisplayIdFromNextArg();
                if (displayId < 0) {
                    throw new IllegalArgumentException("--display must be a non-negative integer");
                }
            } else {
            } else {
                getErrPrintWriter().println("Error: Unknown option: " + opt);
                getErrPrintWriter().println("Error: Unknown option: " + opt);
                return -1;
                return -1;
@@ -3714,10 +3736,13 @@ final class ActivityManagerShellCommand extends ShellCommand {
            pw.println("      execution of that user if it is currently stopped.");
            pw.println("      execution of that user if it is currently stopped.");
            pw.println("  get-current-user");
            pw.println("  get-current-user");
            pw.println("      Returns id of the current foreground user.");
            pw.println("      Returns id of the current foreground user.");
            pw.println("  start-user [-w] <USER_ID>");
            pw.println("  start-user [-w] [--display DISPLAY_ID] <USER_ID>");
            pw.println("      Start USER_ID in background if it is currently stopped;");
            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("      -w: wait for start-user to complete and the user to be unlocked.");
            pw.println("      --display <DISPLAY_ID>: allows the user to launch activities in the");
            pw.println("        given display, when supported (typically on automotive builds");
            pw.println("        wherethe vehicle has multiple displays)");
            pw.println("  unlock-user <USER_ID>");
            pw.println("  unlock-user <USER_ID>");
            pw.println("      Unlock the given user.  This will only work if the user doesn't");
            pw.println("      Unlock the given user.  This will only work if the user doesn't");
            pw.println("      have an LSKF (PIN/pattern/password).");
            pw.println("      have an LSKF (PIN/pattern/password).");
+47 −7
Original line number Original line Diff line number Diff line
@@ -100,6 +100,7 @@ import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoOutputStream;
import android.view.Display;


import com.android.internal.R;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
@@ -107,6 +108,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.FactoryResetter;
import com.android.server.FactoryResetter;
import com.android.server.FgThread;
import com.android.server.FgThread;
@@ -1071,6 +1073,12 @@ class UserController implements Handler.Callback {
                        Binder.getCallingPid(), UserHandle.USER_ALL);
                        Binder.getCallingPid(), UserHandle.USER_ALL);
            });
            });
        }
        }

        // TODO(b/239982558): for now we're just updating the user's visibility, but most likely
        // we'll need to remove this call and handle that as part of the user state workflow
        // instead.
        // TODO(b/240613396) also check if multi-display is supported
        mInjector.getUserManagerInternal().assignUserToDisplay(userId, Display.INVALID_DISPLAY);
    }
    }


    private void finishUserStopping(final int userId, final UserState uss,
    private void finishUserStopping(final int userId, final UserState uss,
@@ -1363,6 +1371,7 @@ class UserController implements Handler.Callback {
        }
        }
        final int profilesToStartSize = profilesToStart.size();
        final int profilesToStartSize = profilesToStart.size();
        int i = 0;
        int i = 0;
        // TODO(b/239982558): pass displayId
        for (; i < profilesToStartSize && i < (getMaxRunningUsers() - 1); ++i) {
        for (; i < profilesToStartSize && i < (getMaxRunningUsers() - 1); ++i) {
            startUser(profilesToStart.get(i).id, /* foreground= */ false);
            startUser(profilesToStart.get(i).id, /* foreground= */ false);
        }
        }
@@ -1371,6 +1380,7 @@ class UserController implements Handler.Callback {
        }
        }
    }
    }


    // TODO(b/239982558): might need to infer the display id based on parent user
    /**
    /**
     * Starts a user only if it's a profile, with a more relaxed permission requirement:
     * Starts a user only if it's a profile, with a more relaxed permission requirement:
     * {@link android.Manifest.permission#MANAGE_USERS} or
     * {@link android.Manifest.permission#MANAGE_USERS} or
@@ -1399,7 +1409,8 @@ class UserController implements Handler.Callback {
            return false;
            return false;
        }
        }


        return startUserNoChecks(userId, /* foreground= */ false, /* unlockListener= */ null);
        return startUserNoChecks(userId, Display.DEFAULT_DISPLAY, /* foreground= */ false,
                /* unlockListener= */ null);
    }
    }


    @VisibleForTesting
    @VisibleForTesting
@@ -1445,26 +1456,54 @@ class UserController implements Handler.Callback {
            @Nullable IProgressListener unlockListener) {
            @Nullable IProgressListener unlockListener) {
        checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "startUser");
        checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "startUser");


        return startUserNoChecks(userId, foreground, unlockListener);
        return startUserNoChecks(userId, Display.DEFAULT_DISPLAY, foreground, unlockListener);
    }

    // TODO(b/239982558): add javadoc
    boolean startUserOnSecondaryDisplay(@UserIdInt int userId, int displayId,
            @Nullable IProgressListener unlockListener) {
        checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "startUserOnSecondaryDisplay");

        return startUserNoChecks(userId, displayId, /* foreground= */ false, unlockListener);
    }
    }


    private boolean startUserNoChecks(final @UserIdInt int userId, final boolean foreground,
    private boolean startUserNoChecks(@UserIdInt int userId, int displayId, boolean foreground,
            @Nullable IProgressListener unlockListener) {
            @Nullable IProgressListener unlockListener) {
        TimingsTraceAndSlog t = new TimingsTraceAndSlog();
        TimingsTraceAndSlog t = new TimingsTraceAndSlog();


        t.traceBegin("UserController.startUser-" + userId + "-" + (foreground ? "fg" : "bg"));
        t.traceBegin("UserController.startUser-" + userId
                + (displayId == Display.DEFAULT_DISPLAY ? "" : "-display-" + displayId)
                + "-" + (foreground ? "fg" : "bg"));
        try {
        try {
            return startUserInternal(userId, foreground, unlockListener, t);
            return startUserInternal(userId, displayId, foreground, unlockListener, t);
        } finally {
        } finally {
            t.traceEnd();
            t.traceEnd();
        }
        }
    }
    }


    private boolean startUserInternal(@UserIdInt int userId, boolean foreground,
    private boolean startUserInternal(@UserIdInt int userId, int displayId, boolean foreground,
            @Nullable IProgressListener unlockListener, @NonNull TimingsTraceAndSlog t) {
            @Nullable IProgressListener unlockListener, @NonNull TimingsTraceAndSlog t) {
        if (DEBUG_MU) {
        if (DEBUG_MU) {
            Slogf.i(TAG, "Starting user %d%s", userId, foreground ? " in foreground" : "");
            Slogf.i(TAG, "Starting user %d on display %d %s", userId, displayId,
                    foreground ? " in foreground" : "");
        }
        }

        // TODO(b/240613396) also check if multi-display is supported
        if (displayId != Display.DEFAULT_DISPLAY) {
            // TODO(b/239982558): add unit test for the exceptional cases below
            Preconditions.checkArgument(displayId > 0, "Invalid display id (%d)", displayId);
            Preconditions.checkArgument(userId != UserHandle.USER_SYSTEM, "Cannot start system user"
                    + " on secondary display (%d)", displayId);
            Preconditions.checkArgument(!foreground, "Cannot start user %d in foreground AND "
                    + "on secondary display (%d)", userId, displayId);

            // TODO(b/239982558): for now we're just updating the user's visibility, but most likely
            // we'll need to remove this call and handle that as part of the user state workflow
            // instead.
            // TODO(b/239982558): check if display is valid
            mInjector.getUserManagerInternal().assignUserToDisplay(userId, displayId);
        }

        EventLog.writeEvent(EventLogTags.UC_START_USER_INTERNAL, userId);
        EventLog.writeEvent(EventLogTags.UC_START_USER_INTERNAL, userId);


        final int callingUid = Binder.getCallingUid();
        final int callingUid = Binder.getCallingUid();
@@ -1519,6 +1558,7 @@ class UserController implements Handler.Callback {
                return false;
                return false;
            }
            }


            // TODO(b/239982558): might need something similar for bg users on secondary display
            if (foreground && isUserSwitchUiEnabled()) {
            if (foreground && isUserSwitchUiEnabled()) {
                t.traceBegin("startFreezingScreen");
                t.traceBegin("startFreezingScreen");
                mInjector.getWindowManager().startFreezingScreen(
                mInjector.getWindowManager().startFreezingScreen(
+3 −0
Original line number Original line Diff line number Diff line
@@ -305,4 +305,7 @@ public abstract class UserManagerInternal {
     * for users that already existed on-disk from an older version of Android.
     * for users that already existed on-disk from an older version of Android.
     */
     */
    public abstract boolean shouldIgnorePrepareStorageErrors(int userId);
    public abstract boolean shouldIgnorePrepareStorageErrors(int userId);

    /** TODO(b/239982558): add javadoc / mention invalid_id is used to unassing */
    public abstract void assignUserToDisplay(@UserIdInt int userId, int displayId);
}
}
Loading