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

Commit ab75d2ce authored by Adam Bookatz's avatar Adam Bookatz
Browse files

Introduce Communal Profile user type [minimalist]

Introduces a new user type - the Communal Profile - which is a profile
that has no parent and which is created and starts starts on boot (for
devices so configured). The profile can be accessed by any person, even
over the lockscreen and even if all human users are locked, and  is
always regarded as visible and running.

This cl does NOT include the communal profile as being part of the
profile group of other users. It is minimalist, in that it simply
creates and starts the profile; SysUi can make use of it, but external
parties (like the Launcher) would require further work to be able to do
so.

This cl is introductory. Some future tasks:
* switching users does not preserve the communal profile windows. The
  profile is still available, but each user sees its own windows.

Test: atest com.android.server.pm.UserVisibilityMediatorSUSDTest#testStartVisibleBgProfile_communalProfile
Test: atest com.android.server.pm.UserManagerTest
Test: atest UserManagerServiceUserPropertiesTest
Test: atest UserManagerServiceUserTypeTest
Test: atest UserControllerTest UserVisibilityMediatorMUPANDTest
Bug: 274838657
Bug: 276473320
Change-Id: Ib0e4e52c10f0e95913ddfde606d6995de3538f27
parent de7843f5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1049,6 +1049,7 @@ package android.content.pm {
    method public android.os.UserHandle getUserHandle();
    method public boolean isAdmin();
    method public boolean isCloneProfile();
    method public boolean isCommunalProfile();
    method public boolean isDemo();
    method public boolean isEnabled();
    method public boolean isEphemeral();
+4 −0
Original line number Diff line number Diff line
@@ -386,6 +386,10 @@ public class UserInfo implements Parcelable {
        return UserManager.isUserTypeCloneProfile(userType);
    }

    public boolean isCommunalProfile() {
        return UserManager.isUserTypeCommunalProfile(userType);
    }

    @UnsupportedAppUsage
    public boolean isEnabled() {
        return (flags & FLAG_DISABLED) != FLAG_DISABLED;
+45 −2
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ public final class UserProperties implements Parcelable {
    private static final String ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT =
            "credentialShareableWithParent";
    private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent";
    private static final String ATTR_ALWAYS_VISIBLE = "alwaysVisible";

    /** Index values of each property (to indicate whether they are present in this object). */
    @IntDef(prefix = "INDEX_", value = {
@@ -76,6 +77,7 @@ public final class UserProperties implements Parcelable {
            INDEX_MEDIA_SHARED_WITH_PARENT,
            INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT,
            INDEX_DELETE_APP_WITH_PARENT,
            INDEX_ALWAYS_VISIBLE,
    })
    @Retention(RetentionPolicy.SOURCE)
    private @interface PropertyIndex {
@@ -91,6 +93,7 @@ public final class UserProperties implements Parcelable {
    private static final int INDEX_MEDIA_SHARED_WITH_PARENT = 8;
    private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
    private static final int INDEX_DELETE_APP_WITH_PARENT = 10;
    private static final int INDEX_ALWAYS_VISIBLE = 11;
    /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
    private long mPropertiesPresent = 0;

@@ -316,6 +319,7 @@ public final class UserProperties implements Parcelable {
                    orig.getCrossProfileIntentFilterAccessControl());
            setCrossProfileIntentResolutionStrategy(orig.getCrossProfileIntentResolutionStrategy());
            setDeleteAppWithParent(orig.getDeleteAppWithParent());
            setAlwaysVisible(orig.getAlwaysVisible());
        }
        if (hasManagePermission) {
            // Add items that require MANAGE_USERS or stronger.
@@ -439,6 +443,24 @@ public final class UserProperties implements Parcelable {
    }
    private boolean mDeleteAppWithParent;

    /**
     * Returns whether the user should always
     * be {@link android.os.UserManager#isUserVisible() visible}.
     * The intended usage is for the Communal Profile, which is running and accessible at all times.
     * @hide
     */
    public boolean getAlwaysVisible() {
        if (isPresent(INDEX_ALWAYS_VISIBLE)) return mAlwaysVisible;
        if (mDefaultProperties != null) return mDefaultProperties.mAlwaysVisible;
        throw new SecurityException("You don't have permission to query alwaysVisible");
    }
    /** @hide */
    public void setAlwaysVisible(boolean val) {
        this.mAlwaysVisible = val;
        setPresent(INDEX_ALWAYS_VISIBLE);
    }
    private boolean mAlwaysVisible;

    /**
     * Return whether, and how, select user restrictions or device policies should be inherited
     * from other user.
@@ -632,6 +654,7 @@ public final class UserProperties implements Parcelable {
                + ", mMediaSharedWithParent=" + isMediaSharedWithParent()
                + ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
                + ", mDeleteAppWithParent=" + getDeleteAppWithParent()
                + ", mAlwaysVisible=" + getAlwaysVisible()
                + "}";
    }

@@ -658,6 +681,7 @@ public final class UserProperties implements Parcelable {
        pw.println(prefix + "    mCredentialShareableWithParent="
                + isCredentialShareableWithParent());
        pw.println(prefix + "    mDeleteAppWithParent=" + getDeleteAppWithParent());
        pw.println(prefix + "    mAlwaysVisible=" + getAlwaysVisible());
    }

    /**
@@ -724,6 +748,9 @@ public final class UserProperties implements Parcelable {
                case ATTR_DELETE_APP_WITH_PARENT:
                    setDeleteAppWithParent(parser.getAttributeBoolean(i));
                    break;
                case ATTR_ALWAYS_VISIBLE:
                    setAlwaysVisible(parser.getAttributeBoolean(i));
                    break;
                default:
                    Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
            }
@@ -783,6 +810,10 @@ public final class UserProperties implements Parcelable {
            serializer.attributeBoolean(null, ATTR_DELETE_APP_WITH_PARENT,
                    mDeleteAppWithParent);
        }
        if (isPresent(INDEX_ALWAYS_VISIBLE)) {
            serializer.attributeBoolean(null, ATTR_ALWAYS_VISIBLE,
                    mAlwaysVisible);
        }
    }

    // For use only with an object that has already had any permission-lacking fields stripped out.
@@ -800,6 +831,7 @@ public final class UserProperties implements Parcelable {
        dest.writeBoolean(mMediaSharedWithParent);
        dest.writeBoolean(mCredentialShareableWithParent);
        dest.writeBoolean(mDeleteAppWithParent);
        dest.writeBoolean(mAlwaysVisible);
    }

    /**
@@ -821,6 +853,7 @@ public final class UserProperties implements Parcelable {
        mMediaSharedWithParent = source.readBoolean();
        mCredentialShareableWithParent = source.readBoolean();
        mDeleteAppWithParent = source.readBoolean();
        mAlwaysVisible = source.readBoolean();
    }

    @Override
@@ -859,6 +892,7 @@ public final class UserProperties implements Parcelable {
        private boolean mMediaSharedWithParent = false;
        private boolean mCredentialShareableWithParent = false;
        private boolean mDeleteAppWithParent = false;
        private boolean mAlwaysVisible = false;

        public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
            mShowInLauncher = showInLauncher;
@@ -926,6 +960,12 @@ public final class UserProperties implements Parcelable {
            return this;
        }

        /** Sets the value for {@link #mAlwaysVisible}*/
        public Builder setAlwaysVisible(boolean alwaysVisible) {
            mAlwaysVisible = alwaysVisible;
            return this;
        }

        /** Builds a UserProperties object with *all* values populated. */
        public UserProperties build() {
            return new UserProperties(
@@ -939,7 +979,8 @@ public final class UserProperties implements Parcelable {
                    mCrossProfileIntentResolutionStrategy,
                    mMediaSharedWithParent,
                    mCredentialShareableWithParent,
                    mDeleteAppWithParent);
                    mDeleteAppWithParent,
                    mAlwaysVisible);
        }
    } // end Builder

@@ -954,7 +995,8 @@ public final class UserProperties implements Parcelable {
            @CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy,
            boolean mediaSharedWithParent,
            boolean credentialShareableWithParent,
            boolean deleteAppWithParent) {
            boolean deleteAppWithParent,
            boolean alwaysVisible) {
        mDefaultProperties = null;
        setShowInLauncher(showInLauncher);
        setStartWithParent(startWithParent);
@@ -967,5 +1009,6 @@ public final class UserProperties implements Parcelable {
        setMediaSharedWithParent(mediaSharedWithParent);
        setCredentialShareableWithParent(credentialShareableWithParent);
        setDeleteAppWithParent(deleteAppWithParent);
        setAlwaysVisible(alwaysVisible);
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ interface IUserManager {
    ParcelFileDescriptor getUserIcon(int userId);
    UserInfo getPrimaryUser();
    int getMainUserId();
    int getCommunalProfileId();
    int getPreviousFullUserToEnterForeground();
    List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated);
    List<UserInfo> getProfiles(int userId, boolean enabledOnly);
+58 −0
Original line number Diff line number Diff line
@@ -165,6 +165,12 @@ public class UserManager {
     */
    public static final String USER_TYPE_PROFILE_TEST = "android.os.usertype.profile.TEST";

    /**
     * User type representing a communal profile, which is shared by all users of the device.
     * @hide
     */
    public static final String USER_TYPE_PROFILE_COMMUNAL = "android.os.usertype.profile.COMMUNAL";

    /**
     * User type representing a {@link UserHandle#USER_SYSTEM system} user that is <b>not</b> a
     * human user.
@@ -2131,6 +2137,16 @@ public class UserManager {
                .getBoolean(com.android.internal.R.bool.config_guestUserAllowEphemeralStateChange);
    }

    /**
     * Returns whether the device is configured to support a Communal Profile.
     * @hide
     */
    public static boolean isCommunalProfileEnabled() {
        return SystemProperties.getBoolean("persist.fw.omnipresent_communal_user",
                Resources.getSystem()
                        .getBoolean(com.android.internal.R.bool.config_omnipresentCommunalUser));
    }

    /**
     * Returns whether multiple admins are enabled on the device
     * @hide
@@ -2438,6 +2454,38 @@ public class UserManager {
            throw re.rethrowFromSystemServer();
        }
    }
    /**
     * Returns the designated "communal profile" of the device, or {@code null} if there is none.
     * @hide
     */
    @RequiresPermission(anyOf = {
            Manifest.permission.MANAGE_USERS,
            Manifest.permission.CREATE_USERS,
            Manifest.permission.QUERY_USERS})
    public @Nullable UserHandle getCommunalProfile() {
        try {
            final int userId = mService.getCommunalProfileId();
            if (userId == UserHandle.USER_NULL) {
                return null;
            }
            return UserHandle.of(userId);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Returns {@code true} if the given user is the designated "communal profile" of the device.
     * @hide
     */
    @RequiresPermission(anyOf = {
            Manifest.permission.MANAGE_USERS,
            Manifest.permission.CREATE_USERS,
            Manifest.permission.QUERY_USERS})
    public boolean isCommunalProfile(@UserIdInt int userId) {
        final UserInfo user = getUserInfo(userId);
        return user != null && user.isCommunalProfile();
    }

    /**
     * Used to check if the context user is an admin user. An admin user may be allowed to
@@ -2539,6 +2587,15 @@ public class UserManager {
        return USER_TYPE_PROFILE_CLONE.equals(userType);
    }

    /**
     * Returns whether the user type is a
     * {@link UserManager#USER_TYPE_PROFILE_COMMUNAL communal profile}.
     * @hide
     */
    public static boolean isUserTypeCommunalProfile(@Nullable String userType) {
        return USER_TYPE_PROFILE_COMMUNAL.equals(userType);
    }

    /**
     * @hide
     * @deprecated Use {@link #isRestrictedProfile()}
@@ -2986,6 +3043,7 @@ public class UserManager {
     *   <li>(Running) profiles of the current foreground user.
     *   <li>Background users assigned to secondary displays (for example, passenger users on
     *   automotive builds, using the display associated with their seats).
     *   <li>A communal profile, if present.
     * </ol>
     *
     * @return whether the user is visible at the moment, as defined above.
Loading