Merge "Add app clone profile and APIs." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
cbe66b17b9
@@ -8500,6 +8500,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();
|
||||
@@ -8513,6 +8514,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";
|
||||
@@ -8526,6 +8528,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";
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1506,6 +1506,26 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCloneProfile(@UserIdInt int userId) {
|
||||
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isCloneProfile");
|
||||
synchronized (mUsersLock) {
|
||||
UserInfo userInfo = getUserInfoLU(userId);
|
||||
return userInfo != null && userInfo.isCloneProfile();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sharesMediaWithParent(@UserIdInt int userId) {
|
||||
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
|
||||
"sharesMediaWithParent");
|
||||
synchronized (mUsersLock) {
|
||||
UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
|
||||
return userTypeDetails != null ? userTypeDetails.isProfile()
|
||||
&& userTypeDetails.sharesMediaWithParent() : false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
|
||||
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
|
||||
|
||||
@@ -149,6 +149,13 @@ public final class UserTypeDetails {
|
||||
*/
|
||||
private final @Nullable int[] mDarkThemeBadgeColors;
|
||||
|
||||
/**
|
||||
* Denotes if the user shares media with its parent user.
|
||||
*
|
||||
* <p> Default value is false
|
||||
*/
|
||||
private final boolean mSharesMediaWithParent;
|
||||
|
||||
private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
|
||||
@UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
|
||||
int maxAllowedPerParent,
|
||||
@@ -158,7 +165,8 @@ public final class UserTypeDetails {
|
||||
@Nullable Bundle defaultRestrictions,
|
||||
@Nullable Bundle defaultSystemSettings,
|
||||
@Nullable Bundle defaultSecureSettings,
|
||||
@Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters) {
|
||||
@Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
|
||||
boolean sharesMediaWithParent) {
|
||||
this.mName = name;
|
||||
this.mEnabled = enabled;
|
||||
this.mMaxAllowed = maxAllowed;
|
||||
@@ -177,6 +185,7 @@ public final class UserTypeDetails {
|
||||
this.mBadgeLabels = badgeLabels;
|
||||
this.mBadgeColors = badgeColors;
|
||||
this.mDarkThemeBadgeColors = darkThemeBadgeColors;
|
||||
this.mSharesMediaWithParent = sharesMediaWithParent;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,6 +300,13 @@ public final class UserTypeDetails {
|
||||
return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user has shared media with parent user or false otherwise.
|
||||
*/
|
||||
public boolean sharesMediaWithParent() {
|
||||
return mSharesMediaWithParent;
|
||||
}
|
||||
|
||||
/** Returns a {@link Bundle} representing the default user restrictions. */
|
||||
@NonNull Bundle getDefaultRestrictions() {
|
||||
return BundleUtils.clone(mDefaultRestrictions);
|
||||
@@ -318,7 +334,6 @@ public final class UserTypeDetails {
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
/** Dumps details of the UserTypeDetails. Do not parse this. */
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.print(prefix); pw.print("mName: "); pw.println(mName);
|
||||
@@ -383,6 +398,7 @@ public final class UserTypeDetails {
|
||||
private @DrawableRes int mIconBadge = Resources.ID_NULL;
|
||||
private @DrawableRes int mBadgePlain = Resources.ID_NULL;
|
||||
private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
|
||||
private boolean mSharesMediaWithParent = false;
|
||||
|
||||
public Builder setName(String name) {
|
||||
mName = name;
|
||||
@@ -473,6 +489,15 @@ public final class UserTypeDetails {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets shared media property for the user.
|
||||
* @param sharesMediaWithParent the value to be set, true or false
|
||||
*/
|
||||
public Builder setSharesMediaWithParent(boolean sharesMediaWithParent) {
|
||||
mSharesMediaWithParent = sharesMediaWithParent;
|
||||
return this;
|
||||
}
|
||||
|
||||
@UserInfoFlag int getBaseType() {
|
||||
return mBaseType;
|
||||
}
|
||||
@@ -502,7 +527,7 @@ public final class UserTypeDetails {
|
||||
mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
|
||||
mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
|
||||
mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
|
||||
mDefaultCrossProfileIntentFilters);
|
||||
mDefaultCrossProfileIntentFilters, mSharesMediaWithParent);
|
||||
}
|
||||
|
||||
private boolean hasBadge() {
|
||||
|
||||
@@ -29,6 +29,7 @@ import static android.os.UserManager.USER_TYPE_FULL_GUEST;
|
||||
import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
|
||||
import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
|
||||
import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
|
||||
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
|
||||
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
|
||||
import static android.os.UserManager.USER_TYPE_PROFILE_TEST;
|
||||
import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
|
||||
@@ -100,6 +101,7 @@ public final class UserTypeFactory {
|
||||
builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo());
|
||||
builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted());
|
||||
builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless());
|
||||
builders.put(USER_TYPE_PROFILE_CLONE, getDefaultTypeProfileClone());
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
builders.put(USER_TYPE_PROFILE_TEST, getDefaultTypeProfileTest());
|
||||
}
|
||||
@@ -107,6 +109,21 @@ public final class UserTypeFactory {
|
||||
return builders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_CLONE}
|
||||
* configuration.
|
||||
*/
|
||||
// TODO(b/182396009): Add default restrictions, if needed for clone user type.
|
||||
private static UserTypeDetails.Builder getDefaultTypeProfileClone() {
|
||||
return new UserTypeDetails.Builder()
|
||||
.setName(USER_TYPE_PROFILE_CLONE)
|
||||
.setBaseType(FLAG_PROFILE)
|
||||
.setMaxAllowedPerParent(1)
|
||||
.setLabel(0)
|
||||
.setDefaultRestrictions(null)
|
||||
.setSharesMediaWithParent(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED}
|
||||
* configuration.
|
||||
|
||||
@@ -58,6 +58,7 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
|
||||
@@ -158,6 +159,40 @@ public final class UserManagerTest {
|
||||
fail("Didn't find a guest: " + list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloneUser() throws Exception {
|
||||
// Test that only one clone user can be created
|
||||
final int primaryUserId = mUserManager.getPrimaryUser().id;
|
||||
UserInfo userInfo = createProfileForUser("Clone user1",
|
||||
UserManager.USER_TYPE_PROFILE_CLONE,
|
||||
primaryUserId);
|
||||
assertThat(userInfo).isNotNull();
|
||||
UserInfo userInfo2 = createProfileForUser("Clone user2",
|
||||
UserManager.USER_TYPE_PROFILE_CLONE,
|
||||
primaryUserId);
|
||||
assertThat(userInfo2).isNull();
|
||||
|
||||
final Context userContext = mContext.createPackageContextAsUser("system", 0,
|
||||
UserHandle.of(userInfo.id));
|
||||
assertThat(userContext.getSystemService(
|
||||
UserManager.class).sharesMediaWithParent()).isTrue();
|
||||
|
||||
List<UserInfo> list = mUserManager.getUsers();
|
||||
List<UserInfo> cloneUsers = list.stream().filter(
|
||||
user -> (user.id == userInfo.id && user.name.equals("Clone user1")
|
||||
&& user.isCloneProfile()))
|
||||
.collect(Collectors.toList());
|
||||
assertThat(cloneUsers.size()).isEqualTo(1);
|
||||
|
||||
// Verify clone user parent
|
||||
assertThat(mUserManager.getProfileParent(primaryUserId)).isNull();
|
||||
UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id);
|
||||
assertThat(parentProfileInfo).isNotNull();
|
||||
assertThat(primaryUserId).isEqualTo(parentProfileInfo.id);
|
||||
removeUser(userInfo.id);
|
||||
assertThat(mUserManager.getProfileParent(primaryUserId)).isNull();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testAdd2Users() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user