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

Commit 29a55b03 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Changes AM.startUserInBgOnSecondaryDisplay() to check if display is valid."

parents 5897b777 23fbfb82
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -121,6 +121,7 @@ package android.app {
  public class ActivityManager {
    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
    method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int[] getSecondaryDisplayIdsForStartingBackgroundUsers();
    method public long getTotalRam();
    method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessCapabilities(int);
    method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessState(int);
+21 −1
Original line number Diff line number Diff line
@@ -4387,13 +4387,15 @@ public class ActivityManager {
     * a profile, the {@link #getCurrentUser()}, the {@link UserHandle#SYSTEM system user}, or
     * does not exist.
     *
     * @param displayId id of the display, it must exist.
     * @param displayId id of the display.
     *
     * @return whether the operation succeeded. Notice that if the user was already started in such
     * display before, it will return {@code false}.
     *
     * @throws UnsupportedOperationException if the device does not support background users on
     * secondary displays.
     * @throws IllegalArgumentException if the display doesn't exist or is not a valid display to
     * start secondary users on.
     *
     * @hide
     */
@@ -4413,6 +4415,24 @@ public class ActivityManager {
        }
    }

    /**
     * Gets the id of displays that can be used by
     * {@link #startUserInBackgroundOnSecondaryDisplay(int, int)}.
     *
     * @hide
     */
    @TestApi
    @Nullable
    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
            android.Manifest.permission.INTERACT_ACROSS_USERS})
    public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
        try {
            return getService().getSecondaryDisplayIdsForStartingBackgroundUsers();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Gets the message that is shown when a user is switched from.
     *
+6 −0
Original line number Diff line number Diff line
@@ -770,4 +770,10 @@ interface IActivityManager {
            "@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional = true)")
    boolean startUserInBackgroundOnSecondaryDisplay(int userid, int displayId);

    /**
     * Gets the ids of displays that can be used on {@link #startUserInBackgroundOnSecondaryDisplay(int userId, int displayId)}.
     *
     * <p>Typically used only by automotive builds when the vehicle has multiple displays.
     */
    @nullable int[] getSecondaryDisplayIdsForStartingBackgroundUsers();
}
+150 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.Manifest.permission.FILTER_EVENTS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.Manifest.permission.MANAGE_USERS;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
@@ -258,6 +259,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.media.audiofx.AudioEffect;
import android.net.ConnectivityManager;
@@ -332,6 +334,7 @@ import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -2366,6 +2369,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        mUiHandler = injector.getUiHandler(null /* service */);
        mUidObserverController = new UidObserverController(mUiHandler);
        mUserController = new UserController(this);
        mInjector.mUserController = mUserController;
        mPendingIntentController =
                new PendingIntentController(handlerThread.getLooper(), mUserController, mConstants);
        mAppRestrictionController = new AppRestrictionController(mContext, this);
@@ -2480,6 +2484,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
        mUserController = new UserController(this);
        mInjector.mUserController = mUserController;
        mPendingIntentController = new PendingIntentController(
                mHandlerThread.getLooper(), mUserController, mConstants);
@@ -6057,6 +6062,24 @@ public class ActivityManagerService extends IActivityManager.Stub
     * This can be called with or without the global lock held.
     */
    @PermissionMethod
    private void enforceCallingHasAtLeastOnePermission(String func, String... permissions) {
        for (String permission : permissions) {
            if (checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
                return;
            }
        }
        String msg = "Permission Denial: " + func + " from pid="
                + Binder.getCallingPid()
                + ", uid=" + Binder.getCallingUid()
                + " requires one of " + Arrays.toString(permissions);
        Slog.w(TAG, msg);
        throw new SecurityException(msg);
    }
    /**
     * This can be called with or without the global lock held.
     */
    void enforcePermission(String permission, int pid, int uid, String func) {
        if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) {
            return;
@@ -16333,8 +16356,34 @@ public class ActivityManagerService extends IActivityManager.Stub
    @Override
    public boolean startUserInBackgroundOnSecondaryDisplay(int userId, int displayId) {
        int[] displayIds = getSecondaryDisplayIdsForStartingBackgroundUsers();
        boolean validDisplay = false;
        if (displayIds != null) {
            for (int i = 0; i < displayIds.length; i++) {
                if (displayId == displayIds[i]) {
                    validDisplay = true;
                    break;
                }
            }
        }
        if (!validDisplay) {
            throw new IllegalArgumentException("Invalid display (" + displayId + ") to start user. "
                    + "Valid options are: " + Arrays.toString(displayIds));
        }
        if (DEBUG_MU) {
            Slogf.d(TAG_MU, "Calling startUserOnSecondaryDisplay(%d, %d) using injector %s", userId,
                    displayId, mInjector);
        }
        // Permission check done inside UserController.
        return mUserController.startUserOnSecondaryDisplay(userId, displayId);
        return mInjector.startUserOnSecondaryDisplay(userId, displayId);
    }
    @Override
    public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
        enforceCallingHasAtLeastOnePermission("getSecondaryDisplayIdsForStartingBackgroundUsers()",
                MANAGE_USERS, INTERACT_ACROSS_USERS);
        return mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
    }
    /**
@@ -18343,8 +18392,10 @@ public class ActivityManagerService extends IActivityManager.Stub
    @VisibleForTesting
    public static class Injector {
        private final Context mContext;
        private NetworkManagementInternal mNmi;
        private Context mContext;
        private UserController mUserController;
        public Injector(Context context) {
            mContext = context;
@@ -18369,6 +18420,103 @@ public class ActivityManagerService extends IActivityManager.Stub
            return false;
        }
        /**
         * Called by {@code AMS.getSecondaryDisplayIdsForStartingBackgroundUsers()}.
         */
        // NOTE: ideally Injector should have no complex logic, but if this logic was moved to AMS,
        // it could not be tested with the existing ActivityManagerServiceTest (as DisplayManager,
        // DisplayInfo, etc... are final and UserManager.isUsersOnSecondaryDisplaysEnabled is
        // static).
        // So, the logic was added here, and tested on ActivityManagerServiceInjectorTest (which
        // was added on FrameworksMockingServicesTests and hence uses Extended Mockito to mock
        // final and static stuff)
        @Nullable
        public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
            if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) {
                Slogf.w(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): not supported");
                return null;
            }
            // NOTE: DisplayManagerInternal doesn't have a method to list all displays
            DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
            Display[] allDisplays = displayManager.getDisplays();
            // allDisplays should contain at least Display.DEFAULT_DISPLAY, but it's better to
            // double check, just in case...
            if (allDisplays == null || allDisplays.length == 0) {
                Slogf.wtf(TAG, "displayManager (%s) returned no displays", displayManager);
                return null;
            }
            boolean hasDefaultDisplay = false;
            for (Display display : allDisplays) {
                if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
                    hasDefaultDisplay = true;
                    break;
                }
            }
            if (!hasDefaultDisplay) {
                Slogf.wtf(TAG, "displayManager (%s) has %d displays (%s), but none has id "
                        + "DEFAULT_DISPLAY (%d)", displayManager, allDisplays.length,
                        Arrays.toString(allDisplays), Display.DEFAULT_DISPLAY);
                return null;
            }
            // Starts with all displays but DEFAULT_DISPLAY
            int[] displayIds = new int[allDisplays.length - 1];
            // TODO(b/247592632): check for other properties like isSecure or proper display type
            int numberValidDisplays = 0;
            for (Display display : allDisplays) {
                int displayId = display.getDisplayId();
                if (display.isValid() && displayId != Display.DEFAULT_DISPLAY) {
                    displayIds[numberValidDisplays++] = displayId;
                }
            }
            if (numberValidDisplays == 0) {
                // TODO(b/247580038): remove this workaround once a virtual display on Car's
                // KitchenSink (or other app) can be used while running CTS tests on devices that
                // don't have a real display.
                // STOPSHIP: if not removed, it should at least be unit tested
                String testingProp = "fw.secondary_display_for_starting_users_for_testing_purposes";
                int displayId = SystemProperties.getInt(testingProp, Display.DEFAULT_DISPLAY);
                if (displayId != Display.DEFAULT_DISPLAY && displayId > 0) {
                    Slogf.w(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): no valid "
                            + "display found, but returning %d as set by property %s", displayId,
                            testingProp);
                    return new int[] { displayId };
                }
                Slogf.e(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): no valid display"
                        + " on %s", Arrays.toString(allDisplays));
                return null;
            }
            if (numberValidDisplays != displayIds.length) {
                int[] validDisplayIds = new int[numberValidDisplays];
                System.arraycopy(displayIds, 0, validDisplayIds, 0, numberValidDisplays);
                if (DEBUG_MU) {
                    Slogf.d(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): returning "
                            + "only valid displays (%d instead of %d): %s", numberValidDisplays,
                            displayIds.length, Arrays.toString(validDisplayIds));
                }
                return validDisplayIds;
            }
            if (DEBUG_MU) {
                Slogf.d(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): returning all "
                        + "(but DEFAULT_DISPLAY) displays : %s", Arrays.toString(displayIds));
            }
            return displayIds;
        }
        /**
         * Called by {@code AMS.startUserOnSecondaryDisplay()}.
         */
        public boolean startUserOnSecondaryDisplay(int userId, int displayId) {
            return mUserController.startUserOnSecondaryDisplay(userId, displayId);
        }
        /**
         * Return the process list instance
         */
+17 −0
Original line number Diff line number Diff line
@@ -367,6 +367,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
                    return runGetCurrentForegroundProcess(pw, mInternal, mTaskInterface);
                case "reset-dropbox-rate-limiter":
                    return runResetDropboxRateLimiter();
                case "list-secondary-displays-for-starting-users":
                    return runListSecondaryDisplaysForStartingUsers(pw);
                default:
                    return handleDefaultCommands(cmd);
            }
@@ -2068,6 +2070,10 @@ final class ActivityManagerShellCommand extends ShellCommand {
            success = mInterface.startUserInBackgroundWithListener(userId, waiter);
            displaySuffix = "";
        } else {
            if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) {
                pw.println("Not supported");
                return -1;
            }
            success = mInterface.startUserInBackgroundOnSecondaryDisplay(userId, displayId);
            displaySuffix = " on display " + displayId;
        }
@@ -3591,6 +3597,14 @@ final class ActivityManagerShellCommand extends ShellCommand {
        return 0;
    }

    int runListSecondaryDisplaysForStartingUsers(PrintWriter pw) throws RemoteException {
        int[] displayIds = mInterface.getSecondaryDisplayIdsForStartingBackgroundUsers();
        pw.println(displayIds == null || displayIds.length == 0
                ? "none"
                : Arrays.toString(displayIds));
        return 0;
    }

    private Resources getResources(PrintWriter pw) throws RemoteException {
        // system resources does not contain all the device configuration, construct it manually.
        Configuration config = mInterface.getConfiguration();
@@ -3951,6 +3965,9 @@ final class ActivityManagerShellCommand extends ShellCommand {
            pw.println("         Set an app's background restriction level which in turn map to a app standby bucket.");
            pw.println("  get-bg-restriction-level [--user <USER_ID>] <PACKAGE>");
            pw.println("         Get an app's background restriction level.");
            pw.println("  list-secondary-displays-for-starting-users");
            pw.println("         Lists the id of displays that can be used to start users on "
                    + "background.");
            pw.println();
            Intent.printIntentArgsHelp(pw, "");
        }
Loading