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

Commit 713e6faf authored by Oli Thompson's avatar Oli Thompson Committed by Android (Google) Code Review
Browse files

Merge "Add APIs in CrossProfileApps to obtain the profile type of target profiles" into main

parents 04fc961f c07cb1a9
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -12385,6 +12385,8 @@ package android.content.pm {
    method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle);
    method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle);
    method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle);
    method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle);
    method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
    method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
    method @FlaggedApi("android.app.admin.flags.allow_querying_profile_type") public boolean isManagedProfile(@NonNull android.os.UserHandle);
    method @FlaggedApi("android.app.admin.flags.allow_querying_profile_type") public boolean isProfile(@NonNull android.os.UserHandle);
    method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity);
    method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity);
    method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle);
    method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle);
    method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
    method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
+7 −0
Original line number Original line Diff line number Diff line
@@ -91,6 +91,13 @@ flag {
  bug: "304999634"
  bug: "304999634"
}
}


flag {
    name: "allow_querying_profile_type"
    namespace: "enterprise"
    description: "Public APIs to query if a user is a profile and what kind of profile type it is."
    bug: "323001115"
}

flag {
flag {
  name: "quiet_mode_credential_bug_fix"
  name: "quiet_mode_credential_bug_fix"
  namespace: "enterprise"
  namespace: "enterprise"
+42 −0
Original line number Original line Diff line number Diff line
@@ -19,7 +19,9 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static android.app.admin.flags.Flags.FLAG_ALLOW_QUERYING_PROFILE_TYPE;


import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.RequiresPermission;
@@ -314,6 +316,41 @@ public class CrossProfileApps {
        }
        }
    }
    }



    /**
     * Checks if the specified user is a profile, i.e. not the parent user.
     *
     * @param userHandle The UserHandle of the target profile, must be one of the users returned by
     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
     *        be thrown.
     * @return whether the specified user is a profile.
     */
    @FlaggedApi(FLAG_ALLOW_QUERYING_PROFILE_TYPE)
    public boolean isProfile(@NonNull UserHandle userHandle) {
        // Note that this is not a security check, but rather a check for correct use.
        // The actual security check is performed by UserManager.
        verifyCanAccessUser(userHandle);

        return mUserManager.isProfile(userHandle.getIdentifier());
    }

    /**
     * Checks if the specified user is a managed profile.
     *
     * @param userHandle The UserHandle of the target profile, must be one of the users returned by
     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
     *        be thrown.
     * @return whether the specified user is a managed profile.
     */
    @FlaggedApi(FLAG_ALLOW_QUERYING_PROFILE_TYPE)
    public boolean isManagedProfile(@NonNull UserHandle userHandle) {
        // Note that this is not a security check, but rather a check for correct use.
        // The actual security check is performed by UserManager.
        verifyCanAccessUser(userHandle);

        return mUserManager.isManagedProfile(userHandle.getIdentifier());
    }

    /**
    /**
     * Return a label that calling app can show to user for the semantic of profile switching --
     * Return a label that calling app can show to user for the semantic of profile switching --
     * launching its own activity in specified user profile. For example, it may return
     * launching its own activity in specified user profile. For example, it may return
@@ -677,6 +714,11 @@ public class CrossProfileApps {
        }
        }
    }
    }


    /**
     * A validation method to check that the methods in this class are only being applied to user
     * handles returned by {@link #getTargetUserProfiles()}. As this is run client-side for
     * input validation purposes, this should never replace a real security check service-side.
     */
    private void verifyCanAccessUser(UserHandle userHandle) {
    private void verifyCanAccessUser(UserHandle userHandle) {
        if (!getTargetUserProfiles().contains(userHandle)) {
        if (!getTargetUserProfiles().contains(userHandle)) {
            throw new SecurityException("Not allowed to access " + userHandle);
            throw new SecurityException("Not allowed to access " + userHandle);
+5 −1
Original line number Original line Diff line number Diff line
@@ -3258,7 +3258,11 @@ public class UserManager {
        return isProfile(mUserId);
        return isProfile(mUserId);
    }
    }


    private boolean isProfile(@UserIdInt int userId) {
    /**
     * Returns whether the specified user is a profile.
     * @hide
     */
    public boolean isProfile(@UserIdInt int userId) {
        final String profileType = getProfileType(userId);
        final String profileType = getProfileType(userId);
        return profileType != null && !profileType.equals("");
        return profileType != null && !profileType.equals("");
    }
    }
+45 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,8 @@ import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;


import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.app.admin.DevicePolicyResourcesManager;
@@ -118,11 +120,54 @@ public class CrossProfileAppsTest {
    public void initUsers() throws Exception {
    public void initUsers() throws Exception {
        when(mUserManager.isManagedProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false);
        when(mUserManager.isManagedProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false);
        when(mUserManager.isManagedProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true);
        when(mUserManager.isManagedProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true);
        when(mUserManager.isProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false);
        when(mUserManager.isProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true);


        mTargetProfiles = new ArrayList<>();
        mTargetProfiles = new ArrayList<>();
        when(mService.getTargetUserProfiles(MY_PACKAGE)).thenReturn(mTargetProfiles);
        when(mService.getTargetUserProfiles(MY_PACKAGE)).thenReturn(mTargetProfiles);
    }
    }


    @Test
    public void isProfile_managedProfile_returnsTrue() {
        setValidTargetProfile(MANAGED_PROFILE);

        boolean result = mCrossProfileApps.isProfile(MANAGED_PROFILE);

        assertTrue(result);
    }

    @Test
    public void isProfile_personalProfile_returnsFalse() {
        setValidTargetProfile(PERSONAL_PROFILE);

        boolean result = mCrossProfileApps.isProfile(PERSONAL_PROFILE);

        assertFalse(result);
    }

    @Test
    public void isManagedProfile_managedProfile_returnsTrue() {
        setValidTargetProfile(MANAGED_PROFILE);

        boolean result = mCrossProfileApps.isManagedProfile(MANAGED_PROFILE);

        assertTrue(result);
    }

    @Test
    public void isManagedProfile_personalProfile_returnsFalse() {
        setValidTargetProfile(PERSONAL_PROFILE);

        boolean result = mCrossProfileApps.isManagedProfile(PERSONAL_PROFILE);

        assertFalse(result);
    }

    @Test(expected = SecurityException.class)
    public void isManagedProfile_notValidTarget_throwsSecurityException() {
        mCrossProfileApps.isManagedProfile(PERSONAL_PROFILE);
    }

    @Test
    @Test
    public void getProfileSwitchingLabel_managedProfile() {
    public void getProfileSwitchingLabel_managedProfile() {
        setValidTargetProfile(MANAGED_PROFILE);
        setValidTargetProfile(MANAGED_PROFILE);