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

Commit 80a44eb8 authored by Clara Thomas's avatar Clara Thomas
Browse files

Allow IMMS to switch to an "always visible" user profile

Profiles with "getAlwaysVisible" set should be able to show UI even when
they are not in the foreground, and/or do not have the current user as a
parent. This CL includes always visible profiles for cross-profile
switching.

Bug: 389712089
Test: atest
FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceMockedTest
Flag: android.multiuser.allow_supervising_profile
Change-Id: If2b77ab05f4899c79e9c749a98fc976bad12f5ce
parent 69b9b50f
Loading
Loading
Loading
Loading
+11 −5
Original line number Diff line number Diff line
@@ -3799,8 +3799,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                            scheduleSwitchUserTaskLocked(userId, cs.mClient);
                            return InputBindResult.USER_SWITCHING;
                        }
                        final int[] profileIdsWithDisabled = mUserManagerInternal.getProfileIds(
                                mCurrentImeUserId, false /* enabledOnly */);
                        final int[] profileIdsWithDisabled = getProfileIds(mCurrentImeUserId);
                        for (int profileId : profileIdsWithDisabled) {
                            if (profileId == userId) {
                                scheduleSwitchUserTaskLocked(userId, cs.mClient);
@@ -3847,9 +3846,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.

                    // Verify if caller is a background user.
                    if (!mConcurrentMultiUserModeEnabled && userId != mCurrentImeUserId) {
                        if (ArrayUtils.contains(
                                mUserManagerInternal.getProfileIds(mCurrentImeUserId, false),
                                userId)) {
                        if (ArrayUtils.contains(getProfileIds(mCurrentImeUserId), userId)) {
                            // cross-profile access is always allowed here to allow
                            // profile-switching.
                            scheduleSwitchUserTaskLocked(userId, cs.mClient);
@@ -4023,6 +4020,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
        return res;
    }

    @NonNull
    private int[] getProfileIds(@UserIdInt int userId) {
        if (android.multiuser.Flags.allowSupervisingProfile()) {
            return mUserManagerInternal.getProfileIds(userId, /* enabledOnly */ false,
                    /* includeAlwaysVisible */ true);
        }
        return mUserManagerInternal.getProfileIds(userId, /* enabledOnly */ false);
    }

    @GuardedBy("ImfLock.class")
    private boolean canInteractWithImeLocked(int uid, IInputMethodClient client, String methodName,
            @Nullable ImeTracker.Token statsToken, @UserIdInt int userId) {
+16 −0
Original line number Diff line number Diff line
@@ -328,6 +328,22 @@ public abstract class UserManagerInternal {
     */
    public abstract @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly);

    /**
     * Returns an array of ids for profiles associated with the specified user including the user
     * itself.
     * <p>Note that this includes all profile types (not including Restricted profiles), and,
     * optionally, any {@link UserProperties#getAlwaysVisible() always visible} profiles.
     *
     * @param userId      id of the user to return profiles for
     * @param enabledOnly whether return only {@link UserInfo#isEnabled() enabled} profiles
     * @param includeAlwaysVisible whether to also include {@link UserProperties#getAlwaysVisible()
     *        visible} profiles, even if userId is not their parent
     * @return A non-empty array of ids of profiles associated with the specified user if the user
     *         exists. Otherwise, an empty array.
     */
    public abstract @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly,
            boolean includeAlwaysVisible);

    /**
     * Returns a list of the users that are associated with the specified user, including the user
     * itself. This includes the user, its profiles, its parent, and its parent's other profiles,
+35 −3
Original line number Diff line number Diff line
@@ -1652,6 +1652,13 @@ public class UserManagerService extends IUserManager.Stub {
        return users;
    }

    @GuardedBy("mUsersLock")
    private IntArray getProfileIdsLU(@UserIdInt int userId, @Nullable String userType,
            boolean enabledOnly, boolean excludeHidden) {
        return getProfileIdsLU(userId, userType, enabledOnly, excludeHidden,
                /* includeAlwaysVisible */ false);
    }

    /**
     *  Assume permissions already checked and caller's identity cleared
     *  <p>If userType is {@code null}, returns all profiles for user; else, only returns
@@ -1659,7 +1666,7 @@ public class UserManagerService extends IUserManager.Stub {
     */
    @GuardedBy("mUsersLock")
    private IntArray getProfileIdsLU(@UserIdInt int userId, @Nullable String userType,
            boolean enabledOnly, boolean excludeHidden) {
            boolean enabledOnly, boolean excludeHidden, boolean includeAlwaysVisible) {
        UserInfo user = getUserInfoLU(userId);
        IntArray result = new IntArray(mUsers.size());
        if (user == null) {
@@ -1669,7 +1676,10 @@ public class UserManagerService extends IUserManager.Stub {
        final int userSize = mUsers.size();
        for (int i = 0; i < userSize; i++) {
            UserInfo profile = mUsers.valueAt(i).info;
            if (!isSameProfileGroup(user, profile)) {
            if (!includeAlwaysVisible && !isSameProfileGroup(user, profile)) {
                continue;
            }
            if (includeAlwaysVisible && !isSameProfileGroupOrIsAlwaysVisible(user, profile)) {
                continue;
            }
            if (enabledOnly && !profile.isEnabled()) {
@@ -1741,6 +1751,22 @@ public class UserManagerService extends IUserManager.Stub {
        return isSameProfileGroupNoChecks(userId, otherUserId);
    }

    /**
     * Returns whether the users are in the same profile group, or if either the asker or the
     * target is an always visible profile.
     */
    @GuardedBy("mUsersLock")
    private boolean isSameProfileGroupOrIsAlwaysVisible(UserInfo asker, UserInfo target) {
        if (asker.id == target.id) return true;
        UserData askerData = getUserDataLU(asker.id);
        if (askerData != null && askerData.userProperties.getAlwaysVisible()) {
            return true;
        }
        UserData targetData = getUserDataLU(target.id);
        return (targetData != null && targetData.userProperties.getAlwaysVisible())
                || isSameProfileGroup(asker, target);
    }

    /** Returns whether users are in the same profile group. */
    private boolean isSameProfileGroupNoChecks(@UserIdInt int userId, int otherUserId) {
        synchronized (mUsersLock) {
@@ -8046,9 +8072,15 @@ public class UserManagerService extends IUserManager.Stub {

        @Override
        public @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
            return getProfileIds(userId, enabledOnly, /* includeAlwaysVisible */ false);
        }

        @Override
        public @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly,
                boolean includeAlwaysVisible) {
            synchronized (mUsersLock) {
                return getProfileIdsLU(userId, null /* userType */, enabledOnly, /* excludeHidden */
                        false).toArray();
                        false, includeAlwaysVisible).toArray();
            }
        }

+2 −0
Original line number Diff line number Diff line
@@ -225,6 +225,8 @@ public class InputMethodManagerServiceTestBase {
        when(mMockUserManagerInternal.isUserRunning(anyInt())).thenReturn(true);
        when(mMockUserManagerInternal.getProfileIds(anyInt(), anyBoolean()))
                .thenReturn(new int[] {0});
        when(mMockUserManagerInternal.getProfileIds(anyInt(), anyBoolean(), anyBoolean()))
                .thenReturn(new int[] {0});
        when(mMockUserManagerInternal.getUserIds()).thenReturn(new int[] {0});
        when(mMockActivityManagerInternal.isSystemReady()).thenReturn(true);
        when(mMockActivityManagerInternal.getCurrentUserId()).thenReturn(mUserId);
+17 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static android.os.UserManager.DISALLOW_SMS;
import static android.os.UserManager.DISALLOW_USER_SWITCH;
import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import static android.os.UserManager.USER_TYPE_PROFILE_SUPERVISING;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -828,6 +829,22 @@ public final class UserManagerServiceMockedTest {
        }
    }

    @Test
    @EnableFlags({android.multiuser.Flags.FLAG_ALLOW_SUPERVISING_PROFILE})
    public void testGetProfileIdsIncludingAlwaysVisible_supervisingProfile() {
        UserInfo supervisingProfile = mUms.createUserWithThrow("Supervising",
                USER_TYPE_PROFILE_SUPERVISING, 0);
        int mainUserId = mUms.getMainUserId();
        assertThat(mUmi.getProfileIds(mainUserId, /* enabledOnly */ true,
                /* includeAlwaysVisible */ true)).asList().containsExactly(
                        mainUserId, supervisingProfile.id);
        assertThat(mUmi.getProfileIds(mainUserId, /* enabledOnly */ true,
                /* includeAlwaysVisible */ false)).asList().containsExactly(mainUserId);
        assertThat(mUmi.getProfileIds(supervisingProfile.id, /* enabledOnly */ true,
                /* includeAlwaysVisible */ true)).asList().containsExactly(
                        mainUserId, supervisingProfile.id);
    }

    @Test
    @RequiresFlagsEnabled({
        FLAG_ALLOW_PRIVATE_PROFILE,