summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PermissionController/Android.bp8
-rw-r--r--PermissionController/jarjar-rules.txt25
-rw-r--r--PermissionController/res/xml/roles.xml48
-rw-r--r--PermissionController/role-controller/Android.bp4
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Role.java65
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java73
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt67
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt20
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt90
-rw-r--r--PermissionController/tests/mocking/Android.bp3
-rw-r--r--service/Android.bp2
-rw-r--r--service/jarjar-rules.txt4
-rw-r--r--service/java/com/android/permission/compat/UserHandleCompat.java7
-rw-r--r--service/java/com/android/safetycenter/UserProfileGroup.java14
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java8
-rw-r--r--tests/cts/permissionpolicy/Android.bp1
-rw-r--r--tests/cts/permissionpolicy/res/raw/android_manifest.xml43
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt12
20 files changed, 416 insertions, 86 deletions
diff --git a/PermissionController/Android.bp b/PermissionController/Android.bp
index 5c238806e..596b2dbb5 100644
--- a/PermissionController/Android.bp
+++ b/PermissionController/Android.bp
@@ -72,6 +72,11 @@ java_library {
],
}
+filegroup {
+ name: "PermissionController-jarjar-rules",
+ srcs: ["jarjar-rules.txt"],
+}
+
android_library {
name: "PermissionController-lib",
sdk_version: "system_current",
@@ -167,6 +172,9 @@ android_library {
"//apex_available:platform",
"com.android.permission",
],
+
+ // TODO(b/313706381): Remove jarjar once flagging lib is fixed
+ jarjar_rules: ":PermissionController-jarjar-rules",
}
android_app {
diff --git a/PermissionController/jarjar-rules.txt b/PermissionController/jarjar-rules.txt
new file mode 100644
index 000000000..7bfda1ab1
--- /dev/null
+++ b/PermissionController/jarjar-rules.txt
@@ -0,0 +1,25 @@
+# You may bypass this Gerrit IfThisThenThat Lint if your change doesn't affect
+# RoleParser.applyJarjarTransform(), by adding NO_IFTTT=reason to your commit
+# message.
+# LINT.IfChange
+rule android.app.appfunctions.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.app.appfunctions.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.app.appfunctions.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule android.app.appfunctions.flags.Flags com.android.permissioncontroller.jarjar.@0
+rule android.companion.virtualdevice.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.companion.virtualdevice.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.companion.virtualdevice.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule android.companion.virtualdevice.flags.Flags com.android.permissioncontroller.jarjar.@0
+rule android.content.pm.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.content.pm.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.content.pm.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule android.content.pm.Flags com.android.permissioncontroller.jarjar.@0
+rule android.permission.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.permission.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.permission.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule android.permission.flags.Flags com.android.permissioncontroller.jarjar.@0
+rule android.os.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.os.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.os.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule android.os.Flags com.android.permissioncontroller.jarjar.@0
+# LINT.ThenChange(PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java:applyJarjarTransform)
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index b067c41b5..2f9ac3148 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"
@@ -1644,6 +1687,7 @@
defaultHolders="config_defaultNotes"
description="@string/role_notes_description"
exclusive="true"
+ exclusivity="user"
label="@string/role_notes_label"
minSdkVersion="34"
overrideUserWhenGranting="true"
@@ -1685,6 +1729,7 @@
allowBypassingQualification="true"
defaultHolders="config_systemCallStreaming"
exclusive="true"
+ exclusivity="user"
minSdkVersion="34"
static="true"
systemOnly="true"
@@ -1707,6 +1752,7 @@
behavior="v35.RetailDemoRoleBehavior"
defaultHolders="config_defaultRetailDemo"
exclusive="true"
+ exclusivity="user"
minSdkVersion="35"
static="true"
visible="false">
@@ -1733,6 +1779,7 @@
defaultHolders="config_defaultWallet"
description="@string/role_wallet_description"
exclusive="true"
+ exclusivity="user"
label="@string/role_wallet_label"
minSdkVersion="35"
overrideUserWhenGranting="true"
@@ -1743,5 +1790,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..612c979b5 100644
--- a/PermissionController/role-controller/Android.bp
+++ b/PermissionController/role-controller/Android.bp
@@ -17,6 +17,9 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// Any place that role-controller is added as a dependency must also include
+// "com.android.permission.flags-aconfig-java" or
+// "com.android.permission.flags-aconfig-java-export",
java_library {
name: "role-controller",
srcs: [
@@ -24,6 +27,7 @@ java_library {
],
libs: [
"androidx.annotation_annotation",
+ "com.android.permission.flags-aconfig-java",
],
static_libs: [
"modules-utils-build_system",
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..061f351de 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
@@ -1106,7 +1143,7 @@ public class RoleParser {
+ ">");
return fallbackValue;
}
- String className = applyJarjarTransformIfNeeded(value.substring(0, lastDotIndex));
+ String className = applyJarjarTransform(value.substring(0, lastDotIndex));
String methodName = value.substring(lastDotIndex + 1);
Method method;
try {
@@ -1155,16 +1192,18 @@ public class RoleParser {
};
}
- // LINT.IfChange(applyJarjarTransformIfNeeded)
+ // LINT.IfChange(applyJarjarTransform)
/**
* Simulate the jarjar transform that should happen on the class name.
* <p>
* Currently this only handles the {@code Flags} classes for feature flagging.
*/
@NonNull
- private String applyJarjarTransformIfNeeded(@NonNull String className) {
- if (className.endsWith(".Flags") && Objects.equals(mContext.getPackageName(), "android")) {
- return "com.android.permission.jarjar." + className;
+ private String applyJarjarTransform(@NonNull String className) {
+ if (className.endsWith(".Flags")) {
+ String jarjarPrefix = Objects.equals(mContext.getPackageName(), "android")
+ ? "com.android.permission.jarjar." : "com.android.permissioncontroller.jarjar.";
+ return jarjarPrefix + className;
}
return className;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt
index 4ed9e92b9..e0adf1265 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt
@@ -20,10 +20,13 @@ import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.Hyphens
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.ButtonColors
import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.LocalTextConfiguration
+import androidx.wear.compose.material3.LocalTextStyle
import androidx.wear.compose.material3.Text
import com.android.permissioncontroller.permission.ui.wear.elements.Chip
import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion
@@ -94,6 +97,8 @@ private fun WearPermissionButtonInternal(
text = label,
modifier = Modifier.fillMaxWidth(),
maxLines = labelMaxLines ?: LocalTextConfiguration.current.maxLines,
+ style =
+ LocalTextStyle.current.copy(fontWeight = FontWeight.W600, hyphens = Hyphens.Auto),
)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt
index 5a91ae46c..504c69bb0 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt
@@ -21,6 +21,7 @@ import androidx.wear.compose.material.ChipColors
import androidx.wear.compose.material.ChipDefaults
import androidx.wear.compose.material3.ButtonColors
import androidx.wear.compose.material3.ButtonDefaults
+import com.android.permissioncontroller.permission.ui.wear.elements.chipDefaultColors
import com.android.permissioncontroller.permission.ui.wear.elements.chipDisabledColors
import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.DisabledLike
import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.Primary
@@ -41,7 +42,7 @@ enum class WearPermissionButtonStyle {
@Composable
internal fun WearPermissionButtonStyle.material2ChipColors(): ChipColors {
return when (this) {
- Primary -> ChipDefaults.primaryChipColors()
+ Primary -> chipDefaultColors()
Secondary -> ChipDefaults.secondaryChipColors()
Transparent -> ChipDefaults.childChipColors()
DisabledLike -> chipDisabledColors()
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt
new file mode 100644
index 000000000..807c93370
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements.material3
+
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.requiredHeightIn
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material3.Button
+import androidx.wear.compose.material3.ButtonDefaults
+import androidx.wear.compose.material3.Text
+import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion
+
+@Composable
+fun WearPermissionListFooter(
+ materialUIVersion: WearPermissionMaterialUIVersion,
+ label: String,
+ iconBuilder: WearPermissionIconBuilder? = null,
+ onClick: (() -> Unit) = {},
+) {
+ if (materialUIVersion == WearPermissionMaterialUIVersion.MATERIAL2_5) {
+ ListFooter(
+ description = label,
+ iconRes = iconBuilder?.let { it.iconResource as Int },
+ onClick = onClick,
+ )
+ } else {
+ WearPermissionListFooterInternal(label, iconBuilder, onClick)
+ }
+}
+
+@Composable
+private fun WearPermissionListFooterInternal(
+ label: String,
+ iconBuilder: WearPermissionIconBuilder?,
+ onClick: () -> Unit,
+) {
+ val footerTextComposable: (@Composable RowScope.() -> Unit) = {
+ Text(modifier = Modifier.fillMaxWidth(), text = label, maxLines = Int.MAX_VALUE)
+ }
+ Button(
+ icon = { iconBuilder?.build() },
+ label = {},
+ secondaryLabel = footerTextComposable,
+ enabled = true,
+ onClick = onClick,
+ modifier = Modifier.requiredHeightIn(min = 1.dp).fillMaxWidth(),
+ colors = ButtonDefaults.childButtonColors(),
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt
index cfaaa0df9..adf179be6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt
@@ -35,15 +35,16 @@ import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5
import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL3
+/** This enum is used to specify the material version used for a specific screen */
enum class WearPermissionMaterialUIVersion {
MATERIAL2_5,
MATERIAL3,
}
/**
- * Supports both Material 3 and Material 2 theme. default version for permission theme will be
- * LEGACY until we migrate enough screens to 3. LEGACY version will use material 3 overlay resources
- * by default.
+ * Supports both Material 3 and Material 2_5 theme. default version for permission theme will be 2_5
+ * until we migrate enough screens to 3. 2_5 version will use material 3 overlay resources if we
+ * enable material3 for even one screen (Permission screens will be migrated in phases).
*/
@Composable
fun WearPermissionTheme(
@@ -53,7 +54,9 @@ fun WearPermissionTheme(
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
WearPermissionLegacyTheme(content)
} else {
+ // Whether we are ready to use material3 for any screen.
val useBridgedTheme = Flags.wearComposeMaterial3()
+ // Material3 UI controls are still being used in the screen that the theme is applied
if (version == MATERIAL3) {
val material3Theme = WearOverlayableMaterial3Theme(LocalContext.current)
Material3Theme(
@@ -62,7 +65,12 @@ fun WearPermissionTheme(
shapes = material3Theme.shapes,
content = content,
)
- } else if (version == MATERIAL2_5 && useBridgedTheme) {
+ }
+ // Material2_5 UI controls are still being used in the screen that the theme is applied,
+ // But some in-app screens(like permission grant screen) are migrated to material3.
+ // To avoid having two set of overlay resources, we will use material3 overlay resources to
+ // support material2_5 UI controls as well.
+ else if (version == MATERIAL2_5 && useBridgedTheme) {
val material3Theme = WearOverlayableMaterial3Theme(LocalContext.current)
val bridgedLegacyTheme = WearMaterialBridgedLegacyTheme.createFrom(material3Theme)
MaterialTheme(
@@ -71,7 +79,9 @@ fun WearPermissionTheme(
shapes = bridgedLegacyTheme.shapes,
content = content,
)
- } else {
+ }
+ // We are not ready for material3 yet in any screens.
+ else {
WearPermissionLegacyTheme(content)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt
index 13a9cb6d6..ee8fe2545 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt
@@ -30,22 +30,26 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.material.ChipDefaults
-import androidx.wear.compose.material.MaterialTheme
+import com.android.permission.flags.Flags
import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.ui.wear.elements.Chip
-import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter
import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
-import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip
import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl
-import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipBackgroundColors
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionListFooter
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControl
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControlStyle
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL3
import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData
@Composable
fun WearRequestRoleScreen(
helper: WearRequestRoleHelper,
onSetAsDefault: (Boolean, String?) -> Unit,
- onCanceled: () -> Unit
+ onCanceled: () -> Unit,
) {
val roleLiveData = helper.viewModel.roleLiveData.observeAsState(emptyList())
val manageRoleHolderState =
@@ -75,7 +79,15 @@ fun WearRequestRoleScreen(
}
}
+ val useMaterial3Controls = Flags.wearComposeMaterial3()
+ val materialUIVersion =
+ if (useMaterial3Controls) {
+ MATERIAL3
+ } else {
+ MATERIAL2_5
+ }
WearRequestRoleContent(
+ materialUIVersion,
isLoading,
helper,
roleLiveData.value,
@@ -85,7 +97,7 @@ fun WearRequestRoleScreen(
onCheckedChanged,
onDontAskAgainCheckedChanged,
onSetAsDefault,
- onCanceled
+ onCanceled,
)
if (isLoading && roleLiveData.value.isNotEmpty()) {
@@ -95,6 +107,7 @@ fun WearRequestRoleScreen(
@Composable
internal fun WearRequestRoleContent(
+ materialUIVersion: WearPermissionMaterialUIVersion,
isLoading: Boolean,
helper: WearRequestRoleHelper,
qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
@@ -104,56 +117,74 @@ internal fun WearRequestRoleContent(
onCheckedChanged: (Boolean, String?, Boolean) -> Unit,
onDontAskAgainCheckedChanged: (Boolean) -> Unit,
onSetAsDefault: (Boolean, String?) -> Unit,
- onCanceled: () -> Unit
+ onCanceled: () -> Unit,
) {
ScrollableScreen(
+ materialUIVersion = materialUIVersion,
image = helper.getIcon(),
title = helper.getTitle(),
showTimeText = false,
- isLoading = isLoading
+ isLoading = isLoading,
) {
- helper.getNonePreference(qualifyingApplications, selectedPackageName)?.let {
+ helper.getNonePreference(qualifyingApplications, selectedPackageName)?.let { pref ->
item {
- ToggleChip(
- label = it.label,
- icon = it.icon,
- enabled = enabled && it.enabled,
- checked = it.checked,
+ WearPermissionToggleControl(
+ materialUIVersion = materialUIVersion,
+ label = pref.label,
+ iconBuilder = pref.icon?.let { WearPermissionIconBuilder.builder(it) },
+ enabled = enabled && pref.enabled,
+ checked = pref.checked,
onCheckedChanged = { checked ->
- run { onCheckedChanged(checked, it.packageName, it.isHolder) }
+ onCheckedChanged(checked, pref.packageName, pref.isHolder)
},
toggleControl = ToggleChipToggleControl.Radio,
- labelMaxLine = Integer.MAX_VALUE
+ labelMaxLines = Integer.MAX_VALUE,
)
}
- it.subTitle?.let { subTitle -> item { ListFooter(description = subTitle) } }
+ pref.subTitle?.let { subTitle ->
+ item {
+ WearPermissionListFooter(
+ materialUIVersion = materialUIVersion,
+ label = subTitle,
+ )
+ }
+ }
}
for (pref in helper.getPreferences(qualifyingApplications, selectedPackageName)) {
item {
- ToggleChip(
+ WearPermissionToggleControl(
+ materialUIVersion = materialUIVersion,
label = pref.label,
- icon = pref.icon,
+ iconBuilder = pref.icon?.let { WearPermissionIconBuilder.builder(it) },
enabled = enabled && pref.enabled,
checked = pref.checked,
onCheckedChanged = { checked ->
- run { onCheckedChanged(checked, pref.packageName, pref.isHolder) }
+ onCheckedChanged(checked, pref.packageName, pref.isHolder)
},
toggleControl = ToggleChipToggleControl.Radio,
)
}
- pref.subTitle?.let { subTitle -> item { ListFooter(description = subTitle) } }
+ pref.subTitle?.let { subTitle ->
+ item {
+ WearPermissionListFooter(
+ materialUIVersion = materialUIVersion,
+ label = subTitle,
+ )
+ }
+ }
}
if (helper.showDontAskButton()) {
item {
- ToggleChip(
+ WearPermissionToggleControl(
+ materialUIVersion = materialUIVersion,
checked = dontAskAgain,
enabled = enabled,
onCheckedChanged = { checked -> run { onDontAskAgainCheckedChanged(checked) } },
label = stringResource(R.string.request_role_dont_ask_again),
toggleControl = ToggleChipToggleControl.Checkbox,
- colors = toggleChipBackgroundColors(),
+ style = WearPermissionToggleControlStyle.Transparent,
modifier =
Modifier.testTag("com.android.permissioncontroller:id/dont_ask_again"),
)
@@ -163,17 +194,18 @@ internal fun WearRequestRoleContent(
item { Spacer(modifier = Modifier.height(14.dp)) }
item {
- Chip(
+ WearPermissionButton(
+ materialUIVersion = materialUIVersion,
label = stringResource(R.string.request_role_set_as_default),
- textColor = MaterialTheme.colors.background,
- colors = ChipDefaults.primaryChipColors(),
+ style = WearPermissionButtonStyle.Primary,
enabled = helper.shouldSetAsDefaultEnabled(enabled),
onClick = { onSetAsDefault(dontAskAgain, selectedPackageName) },
modifier = Modifier.testTag("android:id/button1"),
)
}
item {
- Chip(
+ WearPermissionButton(
+ materialUIVersion = materialUIVersion,
label = stringResource(R.string.cancel),
enabled = enabled,
onClick = { onCanceled() },
diff --git a/PermissionController/tests/mocking/Android.bp b/PermissionController/tests/mocking/Android.bp
index a541f4577..37851b2bb 100644
--- a/PermissionController/tests/mocking/Android.bp
+++ b/PermissionController/tests/mocking/Android.bp
@@ -86,4 +86,7 @@ android_test {
],
kotlincflags: ["-Xjvm-default=all"],
+
+ // TODO(b/313706381): Remove jarjar once flagging lib is fixed
+ jarjar_rules: ":PermissionController-jarjar-rules",
}
diff --git a/service/Android.bp b/service/Android.bp
index 8efce5ebe..b6e85b0fc 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -101,6 +101,7 @@ java_sdk_library {
"modules-utils-backgroundthread",
"modules-utils-build",
"modules-utils-os",
+ // framework-permission-s already includes com.android.permission.flags-aconfig-java
"role-controller",
"safety-center-config",
"safety-center-internal-data",
@@ -111,6 +112,7 @@ java_sdk_library {
"service-permission-statsd",
"permissioncontroller-statsd",
"service-permission-proto-stream",
+ "com.android.permission.flags-aconfig-java",
],
errorprone: {
javacflags: ["-Xep:GuardedBy:ERROR"],
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index 77e071672..4d4d6e050 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -1,5 +1,5 @@
# You may bypass this Gerrit IfThisThenThat Lint if your change doesn't affect
-# RoleParser.applyJarjarTransformIfNeeded(), by adding NO_IFTTT=reason to your commit
+# RoleParser.applyJarjarTransform(), by adding NO_IFTTT=reason to your commit
# message.
# LINT.IfChange
rule android.app.appfunctions.flags.*FeatureFlags* com.android.permission.jarjar.@0
@@ -37,4 +37,4 @@ rule com.android.safetycenter.resources.** com.android.permission.jarjar.@0
rule com.google.protobuf.** com.android.permission.jarjar.@0
rule kotlin.** com.android.permission.jarjar.@0
rule com.android.permissioncontroller.PermissionControllerStatsLog com.android.permission.jarjar.@0
-# LINT.ThenChange(PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java:applyJarjarTransformIfNeeded)
+# LINT.ThenChange(PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java:applyJarjarTransform)
diff --git a/service/java/com/android/permission/compat/UserHandleCompat.java b/service/java/com/android/permission/compat/UserHandleCompat.java
index 1901aa997..1b3ebb8d6 100644
--- a/service/java/com/android/permission/compat/UserHandleCompat.java
+++ b/service/java/com/android/permission/compat/UserHandleCompat.java
@@ -29,6 +29,13 @@ public final class UserHandleCompat {
public static final int USER_ALL = UserHandle.ALL.getIdentifier();
/**
+ * A user ID to indicate an undefined user of the device.
+ *
+ * @see UserHandle#USER_NULL
+ */
+ public static final @UserIdInt int USER_NULL = -10000;
+
+ /**
* A user ID to indicate the "system" user of the device.
*/
public static final int USER_SYSTEM = UserHandle.SYSTEM.getIdentifier();
diff --git a/service/java/com/android/safetycenter/UserProfileGroup.java b/service/java/com/android/safetycenter/UserProfileGroup.java
index 3202c3776..a78113b04 100644
--- a/service/java/com/android/safetycenter/UserProfileGroup.java
+++ b/service/java/com/android/safetycenter/UserProfileGroup.java
@@ -30,6 +30,7 @@ import android.util.Log;
import androidx.annotation.Nullable;
+import com.android.permission.compat.UserHandleCompat;
import com.android.permission.util.UserUtils;
import java.lang.annotation.Retention;
@@ -49,8 +50,6 @@ import java.util.Objects;
public final class UserProfileGroup {
private static final String TAG = "UserProfileGroup";
- // UserHandle#USER_NULL is a @TestApi so it cannot be accessed from the mainline module.
- public static final @UserIdInt int USER_NULL = -10000;
@UserIdInt private final int mProfileParentUserId;
private final int[] mManagedProfilesUserIds;
@@ -147,7 +146,7 @@ public final class UserProfileGroup {
int managedProfilesUserIdsLen = 0;
int managedRunningProfilesUserIdsLen = 0;
- int privateProfileUserId = USER_NULL;
+ int privateProfileUserId = UserHandleCompat.USER_NULL;
boolean privateProfileRunning = false;
for (int i = 0; i < userProfiles.size(); i++) {
@@ -228,7 +227,7 @@ public final class UserProfileGroup {
/* destPos= */ 1,
mManagedProfilesUserIds.length);
- if (mPrivateProfileUserId != USER_NULL) {
+ if (mPrivateProfileUserId != UserHandleCompat.USER_NULL) {
allProfileIds[allProfileIds.length - 1] = mPrivateProfileUserId;
}
@@ -269,7 +268,7 @@ public final class UserProfileGroup {
case PROFILE_TYPE_MANAGED:
return mManagedProfilesUserIds;
case PROFILE_TYPE_PRIVATE:
- return mPrivateProfileUserId != USER_NULL
+ return mPrivateProfileUserId != UserHandleCompat.USER_NULL
? new int[]{mPrivateProfileUserId} : new int[]{};
default:
Log.w(TAG, "profiles requested for unexpected profile type " + profileType);
@@ -308,7 +307,7 @@ public final class UserProfileGroup {
private int getNumProfiles() {
return 1
+ mManagedProfilesUserIds.length
- + (mPrivateProfileUserId == USER_NULL ? 0 : 1);
+ + (mPrivateProfileUserId == UserHandleCompat.USER_NULL ? 0 : 1);
}
/**
@@ -361,7 +360,8 @@ public final class UserProfileGroup {
}
}
- return USER_NULL != mPrivateProfileUserId && userId == mPrivateProfileUserId;
+ return UserHandleCompat.USER_NULL != mPrivateProfileUserId
+ && userId == mPrivateProfileUserId;
}
@Override
diff --git a/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java
index a0637827c..9fff22747 100644
--- a/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java
+++ b/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java
@@ -30,9 +30,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.compatibility.common.util.UserHelper;
-
-import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -217,11 +214,6 @@ public class NoWifiStatePermissionTest {
*/
@Test(expected = SecurityException.class)
public void testSetWifiEnabled() {
- // Skip the test for passenger on Multi-user-multi-display devices for Automotive
- UserHelper userHelper = new UserHelper(sContext);
- Assume.assumeFalse(
- "Skipped for visible background User as wifi is disabled for visible background "
- + "user.", userHelper.isVisibleBackgroundUser());
mWifiManager.setWifiEnabled(true);
}
}
diff --git a/tests/cts/permissionpolicy/Android.bp b/tests/cts/permissionpolicy/Android.bp
index 4249f3c9d..07fde8bff 100644
--- a/tests/cts/permissionpolicy/Android.bp
+++ b/tests/cts/permissionpolicy/Android.bp
@@ -37,6 +37,7 @@ android_test {
"permission-test-util-lib",
"androidx.test.rules",
"flag-junit",
+ "android.app.flags-aconfig",
"android.permission.flags-aconfig-java-export",
],
srcs: [
diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
index 5008bda40..d30931c3f 100644
--- a/tests/cts/permissionpolicy/res/raw/android_manifest.xml
+++ b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
@@ -2616,12 +2616,22 @@
<!-- @SystemApi Allows access to perform vendor effects in the vibrator.
<p>Protection level: signature
+ @FlaggedApi("android.os.vibrator.vendor_vibration_effects")
@hide
-->
<permission android:name="android.permission.VIBRATE_VENDOR_EFFECTS"
android:protectionLevel="signature|privileged"
android:featureFlag="android.os.vibrator.vendor_vibration_effects" />
+ <!-- @SystemApi Allows access to start a vendor vibration session.
+ <p>Protection level: signature
+ @FlaggedApi("android.os.vibrator.vendor_vibration_effects")
+ @hide
+ -->
+ <permission android:name="android.permission.START_VIBRATION_SESSIONS"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.os.vibrator.vendor_vibration_effects" />
+
<!-- @SystemApi Allows access to the vibrator state.
<p>Protection level: signature
@hide
@@ -4258,6 +4268,18 @@
android:description="@string/permdesc_hideOverlayWindows"
android:protectionLevel="normal" />
+ <!-- Allows an app to enter Picture-in-Picture mode when the user is not explicitly requesting
+ it. This includes using {@link PictureInPictureParams.Builder#setAutoEnterEnabled} as well
+ as lifecycle methods such as {@link Activity#onUserLeaveHint} and {@link Activity#onPause}
+ to enter PiP when the user leaves the app.
+ This permission should only be used for certain PiP
+ <a href="{@docRoot}training/tv/get-started/multitasking#usage-types">usage types</a>.
+ @FlaggedApi("android.app.enable_tv_implicit_enter_pip_restriction")
+ -->
+ <permission android:name="android.permission.TV_IMPLICIT_ENTER_PIP"
+ android:protectionLevel="normal"
+ android:featureFlag="android.app.enable_tv_implicit_enter_pip_restriction" />
+
<!-- ================================== -->
<!-- Permissions affecting the system wallpaper -->
<!-- ================================== -->
@@ -4764,6 +4786,27 @@
<permission android:name="android.permission.PROVIDE_REMOTE_CREDENTIALS"
android:protectionLevel="signature|privileged|role" />
+ <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST)
+ Allows an application to access the Settings Preference services to read settings exposed
+ by the system Settings app and system apps that contribute settings surfaced by the
+ Settings app.
+ <p>This allows the calling application to read settings values through the host
+ application, agnostic of underlying storage. -->
+ <permission android:name="android.permission.READ_SYSTEM_PREFERENCES"
+ android:protectionLevel="signature|privileged|role"
+ android:featureFlag="com.android.settingslib.flags.settings_catalyst" />
+
+ <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST)
+ Allows an application to access the Settings Preference services to write settings
+ values exposed by the system Settings app and system apps that contribute settings surfaced
+ in the Settings app.
+ <p>This allows the calling application to write settings values
+ through the host application, agnostic of underlying storage.
+ <p>Protection Level: signature|privileged|appop - appop to be added in followup -->
+ <permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="com.android.settingslib.flags.settings_catalyst" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
index e3197599c..19b67e729 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
@@ -28,6 +28,7 @@ import android.os.Build
import android.os.Process
import android.os.SystemClock
import android.os.SystemProperties
+import android.os.UserManager
import android.permission.PermissionManager
import android.permission.cts.MtsIgnore
import android.platform.test.annotations.AsbSecurityTest
@@ -161,6 +162,8 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
@Before
fun setUp() {
+ // Camera and Mic are not supported for secondary user visible as a background user.
+ assumeFalse(isAutomotiveWithVisibleBackgroundUser())
runWithShellPermissionIdentity {
screenTimeoutBeforeTest =
Settings.System.getLong(context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
@@ -209,6 +212,9 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
@After
fun tearDown() {
+ if (isAutomotiveWithVisibleBackgroundUser()) {
+ return
+ }
uninstall()
if (isCar) {
// Deselect the indicator since it persists otherwise
@@ -775,4 +781,10 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
private fun byOneOfText(vararg textValues: String) =
By.text(Pattern.compile(textValues.joinToString(separator = "|") { Pattern.quote(it) }))
+
+ fun isAutomotiveWithVisibleBackgroundUser(): Boolean {
+ val userManager = context.getSystemService(UserManager::class.java)
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) &&
+ userManager.isVisibleBackgroundUsersSupported()
+ }
}