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

Commit aa804f97 authored by Adam Bookatz's avatar Adam Bookatz Committed by Android (Google) Code Review
Browse files

Merge "UserManager.getProfileType hidden API and caching"

parents f653a61f c9bc9adf
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -9710,7 +9710,7 @@ package android.os {
    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean hasRestrictedProfiles();
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser();
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
    method public boolean isCloneProfile();
    method public boolean isCredentialSharedWithParent();
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser();
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
+1 −3
Original line number Diff line number Diff line
@@ -107,9 +107,7 @@ interface IUserManager {
    void clearSeedAccountData(int userId);
    boolean someUserHasSeedAccount(in String accountName, in String accountType);
    boolean someUserHasAccount(in String accountName, in String accountType);
    boolean isProfile(int userId);
    boolean isManagedProfile(int userId);
    boolean isCloneProfile(int userId);
    String getProfileType(int userId);
    boolean isMediaSharedWithParent(int userId);
    boolean isCredentialSharedWithParent(int userId);
    boolean isDemoUser(int userId);
+77 −49
Original line number Diff line number Diff line
@@ -98,8 +98,8 @@ public class UserManager {
    /** The userId of the constructor param context. To be used instead of mContext.getUserId(). */
    private final @UserIdInt int mUserId;

    private Boolean mIsManagedProfileCached;
    private Boolean mIsProfileCached;
    /** The userType of UserHandle.myUserId(); empty string if not a profile; null until cached. */
    private String mProfileTypeOfProcessUser = null;

    /**
     * User type representing a {@link UserHandle#USER_SYSTEM system} user that is a human user.
@@ -2276,7 +2276,7 @@ public class UserManager {
     * {@link UserManager#USER_TYPE_PROFILE_MANAGED managed profile}.
     * @hide
     */
    public static boolean isUserTypeManagedProfile(String userType) {
    public static boolean isUserTypeManagedProfile(@Nullable String userType) {
        return USER_TYPE_PROFILE_MANAGED.equals(userType);
    }

@@ -2284,7 +2284,7 @@ public class UserManager {
     * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_GUEST guest user}.
     * @hide
     */
    public static boolean isUserTypeGuest(String userType) {
    public static boolean isUserTypeGuest(@Nullable String userType) {
        return USER_TYPE_FULL_GUEST.equals(userType);
    }

@@ -2293,7 +2293,7 @@ public class UserManager {
     * {@link UserManager#USER_TYPE_FULL_RESTRICTED restricted user}.
     * @hide
     */
    public static boolean isUserTypeRestricted(String userType) {
    public static boolean isUserTypeRestricted(@Nullable String userType) {
        return USER_TYPE_FULL_RESTRICTED.equals(userType);
    }

@@ -2301,7 +2301,7 @@ public class UserManager {
     * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_DEMO demo user}.
     * @hide
     */
    public static boolean isUserTypeDemo(String userType) {
    public static boolean isUserTypeDemo(@Nullable String userType) {
        return USER_TYPE_FULL_DEMO.equals(userType);
    }

@@ -2309,7 +2309,7 @@ public class UserManager {
     * Returns whether the user type is a {@link UserManager#USER_TYPE_PROFILE_CLONE clone user}.
     * @hide
     */
    public static boolean isUserTypeCloneProfile(String userType) {
    public static boolean isUserTypeCloneProfile(@Nullable String userType) {
        return USER_TYPE_PROFILE_CLONE.equals(userType);
    }

@@ -2525,25 +2525,50 @@ public class UserManager {
    }

    private boolean isProfile(@UserIdInt int userId) {
        if (userId == mUserId) {
        final String profileType = getProfileType(userId);
        return profileType != null && !profileType.equals("");
    }

    /**
     * Returns the user type of the context user if it is a profile.
     *
     * This is a more specific form of {@link #getUserType()} with relaxed permission requirements.
     *
     * @return the user type of the context user if it is a {@link #isProfile() profile},
     *         an empty string if it is not a profile,
     *         or null if the user doesn't exist.
     */
    @UserHandleAware(
            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
                    android.Manifest.permission.MANAGE_USERS,
                    android.Manifest.permission.QUERY_USERS,
                    android.Manifest.permission.INTERACT_ACROSS_USERS})
    private @Nullable String getProfileType() {
        return getProfileType(mUserId);
    }

    /** @see #getProfileType() */
    private @Nullable String getProfileType(@UserIdInt int userId) {
        // First, the typical case (i.e. the *process* user, not necessarily the context user).
        // This cache cannot be become invalidated since it's about the calling process itself.
        if (userId == UserHandle.myUserId()) {
            // No need for synchronization.  Once it becomes non-null, it'll be non-null forever.
            // Worst case we might end up calling the AIDL method multiple times but that's fine.
            if (mIsProfileCached != null) {
                return mIsProfileCached;
            if (mProfileTypeOfProcessUser != null) {
                return mProfileTypeOfProcessUser;
            }
            try {
                mIsProfileCached = mService.isProfile(mUserId);
                return mIsProfileCached;
            } catch (RemoteException re) {
                throw re.rethrowFromSystemServer();
                final String profileType = mService.getProfileType(userId);
                if (profileType != null) {
                    return mProfileTypeOfProcessUser = profileType.intern();
                }
        } else {
            try {
                return mService.isProfile(userId);
            } catch (RemoteException re) {
                throw re.rethrowFromSystemServer();
            }
        }

        // The userId is not for the process's user. Use a slower cache that handles invalidation.
        return mProfileTypeCache.query(userId);
    }

    /**
@@ -2577,50 +2602,26 @@ public class UserManager {
            android.Manifest.permission.QUERY_USERS,
            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
    public boolean isManagedProfile(@UserIdInt int userId) {
        if (userId == mUserId) {
            // No need for synchronization.  Once it becomes non-null, it'll be non-null forever.
            // Worst case we might end up calling the AIDL method multiple times but that's fine.
            if (mIsManagedProfileCached != null) {
                return mIsManagedProfileCached;
            }
            try {
                mIsManagedProfileCached = mService.isManagedProfile(mUserId);
                return mIsManagedProfileCached;
            } catch (RemoteException re) {
                throw re.rethrowFromSystemServer();
            }
        } else {
            try {
                return mService.isManagedProfile(userId);
            } catch (RemoteException re) {
                throw re.rethrowFromSystemServer();
            }
        }
        return isUserTypeManagedProfile(getProfileType(userId));
    }

    /**
     * Checks if the context user is a clone profile.
     *
     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
     * must be in the same profile group of the user.
     *
     * @return whether the context user is a clone profile.
     *
     * @see android.os.UserManager#USER_TYPE_PROFILE_CLONE
     * @hide
     */
    @SystemApi
    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
    @UserHandleAware
    @UserHandleAware(
            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
                    android.Manifest.permission.MANAGE_USERS,
                    android.Manifest.permission.QUERY_USERS,
                    android.Manifest.permission.INTERACT_ACROSS_USERS})
    @SuppressAutoDoc
    public boolean isCloneProfile() {
        try {
            return mService.isCloneProfile(mUserId);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
        return isUserTypeCloneProfile(getProfileType());
    }

    /**
@@ -5247,6 +5248,33 @@ public class UserManager {
        }
    }

    /* Cache key for anything that assumes that userIds cannot be re-used without rebooting. */
    private static final String CACHE_KEY_STATIC_USER_PROPERTIES = "cache_key.static_user_props";

    private final PropertyInvalidatedCache<Integer, String> mProfileTypeCache =
            new PropertyInvalidatedCache<Integer, String>(32, CACHE_KEY_STATIC_USER_PROPERTIES) {
                @Override
                public String recompute(Integer query) {
                    try {
                        // Will be null (and not cached) if invalid user; otherwise cache the type.
                        String profileType = mService.getProfileType(query);
                        if (profileType != null) profileType = profileType.intern();
                        return profileType;
                    } catch (RemoteException re) {
                        throw re.rethrowFromSystemServer();
                    }
                }
                @Override
                public boolean bypass(Integer query) {
                    return query < 0;
                }
            };

    /** {@hide} */
    public static final void invalidateStaticUserProperties() {
        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STATIC_USER_PROPERTIES);
    }

    /**
     * @hide
     * User that enforces a restriction.
+12 −13
Original line number Diff line number Diff line
@@ -1517,7 +1517,6 @@ public class UserManagerService extends IUserManager.Stub {
        return userTypeDetails.getBadgeNoBackground();
    }

    @Override
    public boolean isProfile(@UserIdInt int userId) {
        checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
        synchronized (mUsersLock) {
@@ -1526,21 +1525,19 @@ public class UserManagerService extends IUserManager.Stub {
        }
    }

    /**
     * Returns the user type (if it is a profile), empty string (if it isn't a profile),
     * or null (if the user doesn't exist).
     */
    @Override
    public boolean isManagedProfile(@UserIdInt int userId) {
        checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
    public @Nullable String getProfileType(@UserIdInt int userId) {
        checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getProfileType");
        synchronized (mUsersLock) {
            UserInfo userInfo = getUserInfoLU(userId);
            return userInfo != null && userInfo.isManagedProfile();
            if (userInfo != null) {
                return userInfo.isProfile() ? userInfo.userType : "";
            }
    }

    @Override
    public boolean isCloneProfile(@UserIdInt int userId) {
        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isCloneProfile");
        synchronized (mUsersLock) {
            UserInfo userInfo = getUserInfoLU(userId);
            return userInfo != null && userInfo.isCloneProfile();
            return null;
        }
    }

@@ -5163,6 +5160,8 @@ public class UserManagerService extends IUserManager.Stub {
                nextId = scanNextAvailableIdLocked();
            }
        }
        // If we got here, we probably recycled user ids, so invalidate any caches.
        UserManager.invalidateStaticUserProperties();
        if (nextId < 0) {
            throw new IllegalStateException("No user id available!");
        }
+2 −2
Original line number Diff line number Diff line
@@ -332,13 +332,13 @@ public final class UserTypeFactory {
                }

                String typeName = parser.getAttributeValue(null, "name");
                if (typeName == null) {
                if (typeName == null || typeName.equals("")) {
                    Slog.w(LOG_TAG, "Skipping user type with no name in "
                            + parser.getPositionDescription());
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
                typeName.intern();
                typeName = typeName.intern();

                UserTypeDetails.Builder builder;
                if (typeName.startsWith("android.")) {