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

Commit 5a4fc52f authored by Saumya Pathak's avatar Saumya Pathak
Browse files

Add app clone profile and APIs.

This change adds a new hidden user type "android.os.usertype.profile.CLONE"
and two new system APIs "isCloneProfile" and "hasSharedMedia". To support App cloning, we need to support sharing media between the owner user and the clone user. Adding this as a property in UserTypeDetails.

Bug: 182396009
Test: atest android.multiuser.cts.UserManagerTest#testCloneUser, atest com.android.server.pm.UserManagerTest#testCloneUser and manually ran shell commands on the clone user.

Change-Id: I49ddcbd499944ca9ec05ef4dc10d2e2b231d8b88
(cherry picked from commit db930667a21dac9f0de7cb2fe99175b3b4545bf4)
parent 28653587
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -8495,6 +8495,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(android.Manifest.permission.MANAGE_USERS) public boolean isAdminUser();
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isGuestUser();
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isPrimaryUser();
@@ -8508,6 +8509,7 @@ package android.os {
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean sharesMediaWithParent();
    field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
    field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
    field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -8521,6 +8523,7 @@ package android.os {
    field public static final int SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED = 2; // 0x2
    field public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
    field public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
    field public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";
    field public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
    field public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
  }
+1 −0
Original line number Diff line number Diff line
@@ -817,6 +817,7 @@ package android.content.pm {
    method public int describeContents();
    method public android.os.UserHandle getUserHandle();
    method public boolean isAdmin();
    method public boolean isCloneProfile();
    method public boolean isDemo();
    method public boolean isEnabled();
    method public boolean isEphemeral();
+4 −0
Original line number Diff line number Diff line
@@ -321,6 +321,10 @@ public class UserInfo implements Parcelable {
        return UserManager.isUserTypeManagedProfile(userType);
    }

    public boolean isCloneProfile() {
        return UserManager.isUserTypeCloneProfile(userType);
    }

    @UnsupportedAppUsage
    public boolean isEnabled() {
        return (flags & FLAG_DISABLED) != FLAG_DISABLED;
+2 −0
Original line number Diff line number Diff line
@@ -99,6 +99,8 @@ interface IUserManager {
    boolean someUserHasSeedAccount(in String accountName, in String accountType);
    boolean isProfile(int userId);
    boolean isManagedProfile(int userId);
    boolean isCloneProfile(int userId);
    boolean sharesMediaWithParent(int userId);
    boolean isDemoUser(int userId);
    boolean isPreCreated(int userId);
    UserInfo createProfileForUserEvenWhenDisallowedWithThrow(in String name, in String userType, int flags,
+69 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.StringDef;
import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -135,6 +136,16 @@ public class UserManager {
    @SystemApi
    public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";

    /**
     * User type representing a clone profile. Clone profile is a user profile type used to run
     * second instance of an otherwise single user App (eg, messengers). Only the primary user
     * is allowed to have a clone profile.
     *
     * @hide
     */
    @SystemApi
    public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";

    /**
     * User type representing a generic profile for testing purposes. Only on debuggable builds.
     * @hide
@@ -1983,6 +1994,14 @@ public class UserManager {
        return USER_TYPE_FULL_DEMO.equals(userType);
    }

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

    /**
     * Returns the enum defined in the statsd UserLifecycleJourneyReported atom corresponding to the
     * user type.
@@ -2232,6 +2251,31 @@ public class UserManager {
        }
    }

    /**
     * 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
    @SuppressAutoDoc
    public boolean isCloneProfile() {
        try {
            return mService.isCloneProfile(mUserId);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Checks if the calling app is running as an ephemeral user.
     *
@@ -4063,6 +4107,31 @@ public class UserManager {
        }
    }

    /**
     * If the user is a {@link UserManager#isProfile profile}, checks if the user
     * shares media with its parent user (the user that created this profile).
     * Returns false for any other type of user.
     *
     * <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 as the user.
     *
     * @return true if the user shares media with its parent user, false otherwise.
     * @hide
     */
    @SystemApi
    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
    @UserHandleAware
    @SuppressAutoDoc
    public boolean sharesMediaWithParent() {
        try {
            return mService.sharesMediaWithParent(mUserId);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Removes a user and all associated data.
     * @param userId the integer handle of the user.
Loading