diff options
author | 2024-10-10 14:36:01 -0700 | |
---|---|---|
committer | 2024-10-31 22:22:27 +0000 | |
commit | 2258d6daf8744e3e7598bd34d1b76c657b5bf58e (patch) | |
tree | 90597d0cd1f653ff490bf06691dd494bd07020bf | |
parent | e489d3025d1019f5f7d373b617ddc03c38842dcf (diff) |
Add Cross-user role support xml parsing
Add roles.xml parsing logic for cross-user role support.
Adds new exclusivity logic for none, user, and profile.
LOW_COVERAGE_REASON=FLAG_NOT_ENABLED
Relnote: N/A
Flag: com.android.permission.flags.cross_user_role_enabled
Test: RoleParserTest
Bug: 372746603
Change-Id: I23b22ff8c4a7b446190af96de1ca76d0121c584a
4 files changed, 146 insertions, 29 deletions
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml index 9bd675ba0..470b6ddc3 100644 --- a/PermissionController/res/xml/roles.xml +++ b/PermissionController/res/xml/roles.xml @@ -107,6 +107,7 @@ defaultHolders="config_defaultAssistant" description="@string/role_assistant_description" exclusive="true" + exclusivity="user" fallBackToDefaultHolder="true" showNone="true" label="@string/role_assistant_label" @@ -173,6 +174,7 @@ defaultHolders="config_defaultBrowser" description="@string/role_browser_description" exclusive="true" + exclusivity="user" label="@string/role_browser_label" overrideUserWhenGranting="true" requestDescription="@string/role_browser_request_description" @@ -216,6 +218,7 @@ defaultHolders="config_defaultDialer" description="@string/role_dialer_description" exclusive="true" + exclusivity="user" fallBackToDefaultHolder="true" label="@string/role_dialer_label" overrideUserWhenGranting="true" @@ -304,6 +307,7 @@ defaultHolders="config_defaultSms" description="@string/role_sms_description" exclusive="true" + exclusivity="user" label="@string/role_sms_label" overrideUserWhenGranting="true" requestDescription="@string/role_sms_request_description" @@ -394,6 +398,7 @@ behavior="EmergencyRoleBehavior" description="@string/role_emergency_description" exclusive="true" + exclusivity="user" label="@string/role_emergency_label" overrideUserWhenGranting="true" requestDescription="@string/role_emergency_request_description" @@ -426,6 +431,7 @@ behavior="HomeRoleBehavior" description="@string/role_home_description" exclusive="true" + exclusivity="user" label="@string/role_home_label" overrideUserWhenGranting="true" requestDescription="@string/role_home_request_description" @@ -472,6 +478,7 @@ defaultHolders="config_defaultCallRedirection" description="@string/role_call_redirection_description" exclusive="true" + exclusivity="user" label="@string/role_call_redirection_label" overrideUserWhenGranting="true" requestDescription="@string/role_call_redirection_request_description" @@ -493,6 +500,7 @@ defaultHolders="config_defaultCallScreening" description="@string/role_call_screening_description" exclusive="true" + exclusivity="user" label="@string/role_call_screening_label" overrideUserWhenGranting="true" requestDescription="@string/role_call_screening_request_description" @@ -518,6 +526,7 @@ name="android.app.role.SYSTEM_GALLERY" defaultHolders="config_systemGallery" exclusive="true" + exclusivity="user" static="true" systemOnly="true" visible="false"> @@ -537,6 +546,7 @@ behavior="v31.AutomotiveRoleBehavior" defaultHolders="config_systemAutomotiveCluster" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -554,6 +564,7 @@ behavior="v31.CompanionDeviceWatchRoleBehavior" description="@string/role_watch_description" exclusive="false" + exclusivity="none" minSdkVersion="31" systemOnly="false" visible="false"> @@ -582,6 +593,7 @@ name="android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION" defaultHolders="config_systemAutomotiveProjection" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -619,6 +631,7 @@ behavior="v31.SystemShellRoleBehavior" defaultHolders="config_systemShell" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -714,6 +727,7 @@ name="android.app.role.SYSTEM_CONTACTS" defaultHolders="config_systemContacts" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -730,6 +744,7 @@ allowBypassingQualification="true" defaultHolders="config_systemSpeechRecognizer" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -751,6 +766,7 @@ name="android.app.role.SYSTEM_WIFI_COEX_MANAGER" defaultHolders="config_systemWifiCoexManager" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -765,6 +781,7 @@ name="android.app.role.SYSTEM_WELLBEING" defaultHolders="config_systemWellbeing" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -793,6 +810,7 @@ behavior="v31.TelevisionRoleBehavior" defaultHolders="config_systemTelevisionNotificationHandler" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -811,6 +829,7 @@ name="android.app.role.SYSTEM_COMPANION_DEVICE_PROVIDER" defaultHolders="config_systemCompanionDeviceProvider" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -846,6 +865,7 @@ name="android.app.role.SYSTEM_UI_INTELLIGENCE" defaultHolders="config_systemUiIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -901,6 +921,7 @@ name="android.app.role.SYSTEM_AMBIENT_AUDIO_INTELLIGENCE" defaultHolders="config_systemAmbientAudioIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -947,6 +968,7 @@ name="android.app.role.SYSTEM_AUDIO_INTELLIGENCE" defaultHolders="config_systemAudioIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -993,6 +1015,7 @@ name="android.app.role.SYSTEM_NOTIFICATION_INTELLIGENCE" defaultHolders="config_systemNotificationIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1035,6 +1058,7 @@ name="android.app.role.SYSTEM_TEXT_INTELLIGENCE" defaultHolders="config_systemTextIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1075,6 +1099,7 @@ name="android.app.role.SYSTEM_VISUAL_INTELLIGENCE" defaultHolders="config_systemVisualIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1100,6 +1125,7 @@ name="android.app.role.SYSTEM_DOCUMENT_MANAGER" behavior="v33.DocumentManagerRoleBehavior" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1132,6 +1158,7 @@ allowBypassingQualification="true" defaultHolders="config_systemActivityRecognizer" exclusive="false" + exclusivity="none" static="true" systemOnly="true" visible="false"> @@ -1151,6 +1178,7 @@ name="android.app.role.SYSTEM_UI" defaultHolders="config_systemUi" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1177,6 +1205,7 @@ behavior="v31.TelevisionRoleBehavior" defaultHolders="config_systemTelevisionRemoteService" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1196,6 +1225,7 @@ behavior="v33.CompanionDeviceAppStreamingRoleBehavior" description="@string/role_app_streaming_description" exclusive="false" + exclusivity="none" minSdkVersion="33" systemOnly="true" visible="false"> @@ -1220,6 +1250,7 @@ behavior="v33.CompanionDeviceComputerRoleBehavior" description="@string/role_companion_device_computer_description" exclusive="false" + exclusivity="none" minSdkVersion="33" systemOnly="true" visible="false"> @@ -1239,6 +1270,7 @@ name="android.app.role.COMPANION_DEVICE_GLASSES" behavior="v34.CompanionDeviceGlassesRoleBehavior" exclusive="false" + exclusivity="none" minSdkVersion="34" systemOnly="false" visible="false"> @@ -1265,6 +1297,7 @@ name="android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING" allowBypassingQualification="true" exclusive="false" + exclusivity="none" minSdkVersion="34" systemOnly="true" visible="false"> @@ -1278,6 +1311,7 @@ name="android.app.role.SYSTEM_SUPERVISION" defaultHolders="config_systemSupervision" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1300,6 +1334,7 @@ behavior="v33.DevicePolicyManagementRoleBehavior" defaultHolders="config_devicePolicyManagement" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="false" @@ -1412,6 +1447,7 @@ name="android.app.role.SYSTEM_APP_PROTECTION_SERVICE" defaultHolders="config_systemAppProtectionService" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1440,6 +1476,7 @@ behavior="v31.AutomotiveRoleBehavior" defaultHolders="config_systemAutomotiveCalendarSyncManager" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1461,6 +1498,7 @@ defaultHolders="config_defaultAutomotiveNavigation" description="@string/role_automotive_navigation_description" exclusive="true" + exclusivity="user" label="@string/role_automotive_navigation_label" minSdkVersion="33" overrideUserWhenGranting="true" @@ -1534,6 +1572,7 @@ name="android.app.role.SYSTEM_SETTINGS_INTELLIGENCE" defaultHolders="config_systemSettingsIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1551,6 +1590,7 @@ name="android.app.role.SYSTEM_BLUETOOTH_STACK" defaultHolders="config_systemBluetoothStack" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1575,6 +1615,7 @@ <role name="android.app.role.FINANCED_DEVICE_KIOSK" exclusive="true" + exclusivity="user" minSdkVersion="34" visible="false"> <permissions> @@ -1590,6 +1631,7 @@ name="android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER" defaultHolders="config_systemFinancedDeviceController" exclusive="true" + exclusivity="user" minSdkVersion="34" static="true" systemOnly="true" @@ -1617,6 +1659,7 @@ behavior="v33.SystemWearHealthServiceRoleBehavior" defaultHolders="config_systemWearHealthService" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1647,6 +1690,7 @@ defaultHolders="config_defaultNotes" description="@string/role_notes_description" exclusive="true" + exclusivity="user" label="@string/role_notes_label" minSdkVersion="34" overrideUserWhenGranting="true" @@ -1688,6 +1732,7 @@ allowBypassingQualification="true" defaultHolders="config_systemCallStreaming" exclusive="true" + exclusivity="user" minSdkVersion="34" static="true" systemOnly="true" @@ -1710,6 +1755,7 @@ behavior="v35.RetailDemoRoleBehavior" defaultHolders="config_defaultRetailDemo" exclusive="true" + exclusivity="user" minSdkVersion="35" static="true" visible="false"> @@ -1736,6 +1782,7 @@ defaultHolders="config_defaultWallet" description="@string/role_wallet_description" exclusive="true" + exclusivity="user" label="@string/role_wallet_label" minSdkVersion="35" overrideUserWhenGranting="true" @@ -1746,5 +1793,4 @@ shortLabel="@string/role_wallet_short_label" uiBehavior="v35.WalletRoleUiBehavior"/> - </roles> diff --git a/PermissionController/role-controller/Android.bp b/PermissionController/role-controller/Android.bp index 166823b08..dfcc83a08 100644 --- a/PermissionController/role-controller/Android.bp +++ b/PermissionController/role-controller/Android.bp @@ -31,6 +31,7 @@ java_library { "android.companion.virtualdevice.flags-aconfig-java-export", "android.permission.flags-aconfig-java-export", "android.os.flags-aconfig-java-export", + "com.android.permission.flags-aconfig-java-export", ], apex_available: [ "com.android.permission", diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java index 2f2431ece..570ef034a 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java @@ -38,6 +38,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -49,6 +50,8 @@ import com.android.role.controller.util.PackageUtils; import com.android.role.controller.util.RoleManagerCompat; import com.android.role.controller.util.UserUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -83,6 +86,27 @@ public class Role { private static final String CERTIFICATE_SEPARATOR = ":"; + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + EXCLUSIVITY_NONE, + EXCLUSIVITY_USER, + EXCLUSIVITY_PROFILE_GROUP + }) + public @interface Exclusivity {} + + /** + * Does not enforce any exclusivity, which means multiple apps may hold this role in a user. + */ + public static final int EXCLUSIVITY_NONE = 0; + + /** Enforces exclusivity within one user. */ + public static final int EXCLUSIVITY_USER = 1; + + /** + * Enforces exclusivity across all users (including profile users) in the same profile group. + */ + public static final int EXCLUSIVITY_PROFILE_GROUP = 2; + /** * The name of this role. Must be unique. */ @@ -110,9 +134,10 @@ public class Role { private final int mDescriptionResource; /** - * Whether this role is exclusive, i.e. allows at most one holder. + * The exclusivity of this role, i.e. whether this role allows multiple holders, or allows at + * most one holder within a user or a profile group. */ - private final boolean mExclusive; + private final int mExclusivity; /** * Whether this role should fall back to the default holder. @@ -186,8 +211,8 @@ public class Role { /** * Whether the UI for this role will show the "None" item. Only valid if this role is - * {@link #mExclusive exclusive}, and {@link #getFallbackHolder(Context)} should also return - * empty to allow actually selecting "None". + * {@link #isExclusive()}, and {@link #getFallbackHolder(Context)} should + * also return empty to allow actually selecting "None". */ private final boolean mShowNone; @@ -241,14 +266,14 @@ public class Role { public Role(@NonNull String name, boolean allowBypassingQualification, @Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName, - @StringRes int descriptionResource, boolean exclusive, boolean fallBackToDefaultHolder, - @Nullable Supplier<Boolean> featureFlag, @StringRes int labelResource, - int maxSdkVersion, int minSdkVersion, boolean onlyGrantWhenAdded, - boolean overrideUserWhenGranting, @StringRes int requestDescriptionResource, - @StringRes int requestTitleResource, boolean requestable, - @StringRes int searchKeywordsResource, @StringRes int shortLabelResource, - boolean showNone, boolean statik, boolean systemOnly, boolean visible, - @NonNull List<RequiredComponent> requiredComponents, + @StringRes int descriptionResource, @Exclusivity int exclusivity, + boolean fallBackToDefaultHolder, @Nullable Supplier<Boolean> featureFlag, + @StringRes int labelResource, int maxSdkVersion, int minSdkVersion, + boolean onlyGrantWhenAdded, boolean overrideUserWhenGranting, + @StringRes int requestDescriptionResource, @StringRes int requestTitleResource, + boolean requestable, @StringRes int searchKeywordsResource, + @StringRes int shortLabelResource, boolean showNone, boolean statik, boolean systemOnly, + boolean visible, @NonNull List<RequiredComponent> requiredComponents, @NonNull List<Permission> permissions, @NonNull List<Permission> appOpPermissions, @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities, @Nullable String uiBehaviorName) { @@ -257,7 +282,7 @@ public class Role { mBehavior = behavior; mDefaultHoldersResourceName = defaultHoldersResourceName; mDescriptionResource = descriptionResource; - mExclusive = exclusive; + mExclusivity = exclusivity; mFallBackToDefaultHolder = fallBackToDefaultHolder; mFeatureFlag = featureFlag; mLabelResource = labelResource; @@ -298,7 +323,13 @@ public class Role { } public boolean isExclusive() { - return mExclusive; + // TODO(b/373390494): Allow RoleBehavior to override this getExclusivity + return mExclusivity != EXCLUSIVITY_NONE; + } + + public int getExclusivity() { + // TODO(b/373390494): Allow RoleBehavior to override this + return mExclusivity; } @Nullable @@ -353,6 +384,8 @@ public class Role { * @see #mShowNone */ public boolean shouldShowNone() { + // TODO(b/373390494): Ensure RoleBehavior override doesn't conflict with this. + // mShowNone can only be true if isExclusive=true return mShowNone; } @@ -1041,7 +1074,7 @@ public class Role { */ @Nullable public Intent getRestrictionIntentAsUser(@NonNull UserHandle user, @NonNull Context context) { - if (SdkLevel.isAtLeastU() && mExclusive) { + if (SdkLevel.isAtLeastU() && isExclusive()) { UserManager userManager = context.getSystemService(UserManager.class); if (userManager.hasUserRestrictionForUser(UserManager.DISALLOW_CONFIG_DEFAULT_APPS, user)) { @@ -1104,7 +1137,7 @@ public class Role { + ", mBehavior=" + mBehavior + ", mDefaultHoldersResourceName=" + mDefaultHoldersResourceName + ", mDescriptionResource=" + mDescriptionResource - + ", mExclusive=" + mExclusive + + ", mExclusivity=" + mExclusivity + ", mFallBackToDefaultHolder=" + mFallBackToDefaultHolder + ", mFeatureFlag=" + mFeatureFlag + ", mLabelResource=" + mLabelResource diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java b/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java index a0705cd5e..3a8c90888 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java @@ -89,6 +89,7 @@ public class RoleParser { private static final String ATTRIBUTE_DEFAULT_HOLDERS = "defaultHolders"; private static final String ATTRIBUTE_DESCRIPTION = "description"; private static final String ATTRIBUTE_EXCLUSIVE = "exclusive"; + private static final String ATTRIBUTE_EXCLUSIVITY = "exclusivity"; private static final String ATTRIBUTE_FALL_BACK_TO_DEFAULT_HOLDER = "fallBackToDefaultHolder"; private static final String ATTRIBUTE_FEATURE_FLAG = "featureFlag"; private static final String ATTRIBUTE_LABEL = "label"; @@ -135,6 +136,10 @@ public class RoleParser { sModeNameToMode.put(MODE_NAME_FOREGROUND, AppOpsManager.MODE_FOREGROUND); } + private static final String EXCLUSIVITY_NONE = "none"; + private static final String EXCLUSIVITY_USER = "user"; + private static final String EXCLUSIVITY_PROFILE_GROUP = "profileGroup"; + private static final Supplier<Boolean> sFeatureFlagFallback = () -> false; private static final ArrayMap<Class<?>, Class<?>> sPrimitiveToWrapperClass = new ArrayMap<>(); @@ -413,13 +418,45 @@ public class RoleParser { shortLabelResource = 0; } - Boolean exclusive = requireAttributeBooleanValue(parser, ATTRIBUTE_EXCLUSIVE, true, - TAG_ROLE); - if (exclusive == null) { - skipCurrentTag(parser); - return null; + int exclusivity; + if (com.android.permission.flags.Flags.crossUserRoleEnabled()) { + String exclusivityName = requireAttributeValue(parser, ATTRIBUTE_EXCLUSIVITY, TAG_ROLE); + if (exclusivityName == null) { + skipCurrentTag(parser); + return null; + } + switch (exclusivityName) { + case EXCLUSIVITY_NONE: + exclusivity = Role.EXCLUSIVITY_NONE; + break; + case EXCLUSIVITY_USER: + exclusivity = Role.EXCLUSIVITY_USER; + break; + case EXCLUSIVITY_PROFILE_GROUP: + // TODO(b/372743073): change to isAtLeastB once available + // EXCLUSIVITY_PROFILE behavior only available for B+ + // fallback to default of EXCLUSIVITY_USER + exclusivity = SdkLevel.isAtLeastV() + ? Role.EXCLUSIVITY_PROFILE_GROUP + : Role.EXCLUSIVITY_USER; + break; + default: + throwOrLogMessage("Invalid value for \"exclusivity\" on <role>: " + name + + ", exclusivity: " + exclusivityName); + skipCurrentTag(parser); + return null; + } + } else { + Boolean exclusive = + requireAttributeBooleanValue(parser, ATTRIBUTE_EXCLUSIVE, true, TAG_ROLE); + if (exclusive == null) { + skipCurrentTag(parser); + return null; + } + exclusivity = exclusive ? Role.EXCLUSIVITY_USER : Role.EXCLUSIVITY_NONE; } + boolean fallBackToDefaultHolder = getAttributeBooleanValue(parser, ATTRIBUTE_FALL_BACK_TO_DEFAULT_HOLDER, false); @@ -470,7 +507,7 @@ public class RoleParser { 0); boolean showNone = getAttributeBooleanValue(parser, ATTRIBUTE_SHOW_NONE, false); - if (showNone && !exclusive) { + if (showNone && exclusivity == Role.EXCLUSIVITY_NONE) { throwOrLogMessage("showNone=\"true\" is invalid for a non-exclusive role: " + name); skipCurrentTag(parser); return null; @@ -567,12 +604,12 @@ public class RoleParser { preferredActivities = Collections.emptyList(); } return new Role(name, allowBypassingQualification, behavior, defaultHoldersResourceName, - descriptionResource, exclusive, fallBackToDefaultHolder, featureFlag, labelResource, - maxSdkVersion, minSdkVersion, onlyGrantWhenAdded, overrideUserWhenGranting, - requestDescriptionResource, requestTitleResource, requestable, - searchKeywordsResource, shortLabelResource, showNone, statik, systemOnly, visible, - requiredComponents, permissions, appOpPermissions, appOps, preferredActivities, - uiBehaviorName); + descriptionResource, exclusivity, fallBackToDefaultHolder, featureFlag, + labelResource, maxSdkVersion, minSdkVersion, onlyGrantWhenAdded, + overrideUserWhenGranting, requestDescriptionResource, requestTitleResource, + requestable, searchKeywordsResource, shortLabelResource, showNone, statik, + systemOnly, visible, requiredComponents, permissions, appOpPermissions, appOps, + preferredActivities, uiBehaviorName); } @NonNull |