summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--PermissionController/Android.bp8
-rw-r--r--PermissionController/jarjar-rules.txt29
-rw-r--r--PermissionController/res/values-b+sr+Latn/strings.xml2
-rw-r--r--PermissionController/res/values-cs/strings.xml4
-rw-r--r--PermissionController/res/values-fa/strings.xml4
-rw-r--r--PermissionController/res/values-iw/strings.xml2
-rw-r--r--PermissionController/res/values-ja/strings.xml2
-rw-r--r--PermissionController/res/values-ne/strings.xml4
-rw-r--r--PermissionController/res/values-sr/strings.xml2
-rw-r--r--PermissionController/res/values-sv/strings.xml2
-rw-r--r--PermissionController/res/values-te/strings.xml6
-rw-r--r--PermissionController/res/values-watch/donottranslate.xml52
-rw-r--r--PermissionController/res/values-zh-rHK/strings.xml2
-rw-r--r--PermissionController/res/xml/roles.xml131
-rw-r--r--PermissionController/role-controller/Android.bp5
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Role.java86
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java8
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java73
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/compat/AppOpsManagerCompat.java49
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt21
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java36
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt25
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt16
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt47
-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/WearPermissionIconBuilder.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt51
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt11
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt90
-rw-r--r--PermissionController/tests/inprocess/Android.bp3
-rw-r--r--PermissionController/tests/mocking/Android.bp3
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt20
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt10
-rw-r--r--SafetyCenter/Resources/res/values-fa-v35/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-fa/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-hy-v35/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-ja/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-ka-v35/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-kn-v35/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-lv-v35/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-si-v35/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-sv-v35/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-tr-v35/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-ur-v35/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-zh-rTW/strings.xml2
-rw-r--r--framework-s/api/system-current.txt3
-rw-r--r--framework-s/java/android/app/role/IRoleManager.aidl4
-rw-r--r--framework-s/java/android/app/role/RoleManager.java92
-rw-r--r--framework-s/java/android/permission/internal/compat/UserHandleCompat.java (renamed from service/java/com/android/permission/compat/UserHandleCompat.java)9
-rw-r--r--framework-s/java/android/permission/internal/compat/package-info.java (renamed from service/java/com/android/permission/compat/package-info.java)2
-rw-r--r--service/Android.bp2
-rw-r--r--service/jarjar-rules.txt8
-rw-r--r--service/java/com/android/ecm/EnhancedConfirmationService.java3
-rw-r--r--service/java/com/android/permission/util/UserUtils.java81
-rw-r--r--service/java/com/android/role/RoleService.java204
-rw-r--r--service/java/com/android/role/RoleShellCommand.java29
-rw-r--r--service/java/com/android/role/RoleUserState.java73
-rw-r--r--service/java/com/android/role/persistence/RolesPersistenceImpl.java22
-rw-r--r--service/java/com/android/role/persistence/RolesState.java42
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterService.java3
-rw-r--r--service/java/com/android/safetycenter/UserProfileGroup.java24
-rw-r--r--service/lint-baseline.xml11
-rw-r--r--service/proto/role_service.proto3
-rw-r--r--tests/apex/Android.bp2
-rw-r--r--tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt48
-rw-r--r--tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java3
-rw-r--r--tests/cts/permission/src/android/permission/cts/NfcPermissionTest.java17
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java8
-rw-r--r--tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt39
-rw-r--r--tests/cts/permissionpolicy/Android.bp1
-rw-r--r--tests/cts/permissionpolicy/res/raw/OWNERS1
-rw-r--r--tests/cts/permissionpolicy/res/raw/android_manifest.xml272
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt15
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt12
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt10
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/SensorBlockedBannerTest.kt5
-rw-r--r--tests/cts/role/Android.bp2
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleManagerTest.java47
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt42
-rw-r--r--tests/cts/rolemultiuser/OWNERS3
-rw-r--r--tests/functional/safetycenter/singleuser/AndroidTest.xml4
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt3
88 files changed, 1664 insertions, 388 deletions
diff --git a/Android.bp b/Android.bp
index 9b1857741..f2aab3568 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,6 +110,7 @@ bootclasspath_fragment {
package_prefixes: [
"android.app.role",
"android.app.ecm",
+ "android.permission.internal",
"android.permission.jarjar",
"android.safetycenter",
"android.safetylabel",
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..05fe2a148
--- /dev/null
+++ b/PermissionController/jarjar-rules.txt
@@ -0,0 +1,29 @@
+# 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
+rule com.android.permission.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule com.android.permission.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule com.android.permission.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule com.android.permission.flags.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/values-b+sr+Latn/strings.xml b/PermissionController/res/values-b+sr+Latn/strings.xml
index 6dd7a9cb6..75e5a94d3 100644
--- a/PermissionController/res/values-b+sr+Latn/strings.xml
+++ b/PermissionController/res/values-b+sr+Latn/strings.xml
@@ -369,7 +369,7 @@
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"brojčanik"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Podrazumevana apl. za SMS"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"Aplikacija za SMS"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Aplikacije koje vam omogućavaju da koristite broj telefona da biste slali i primali kratke SMS-ove, slike, video snimke i još toga"</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Aplikacije koje vam omogućavaju da koristite broj telefona da biste slali i primali kratke tekstualne poruke, slike, video snimke i drugo"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Želite li da podesite <xliff:g id="APP_NAME">%1$s</xliff:g> kao podrazumevanu aplikacju za SMS?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Ova aplikacija će dobiti pristup kameri, kontaktima, fajlovima i medijima, mikrofonu, telefonu i SMS-ovima"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"SMS, slanje SMS-ova, poruke, slanje poruka"</string>
diff --git a/PermissionController/res/values-cs/strings.xml b/PermissionController/res/values-cs/strings.xml
index 328647205..48996724a 100644
--- a/PermissionController/res/values-cs/strings.xml
+++ b/PermissionController/res/values-cs/strings.xml
@@ -444,8 +444,8 @@
<string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Ostatní služby NFC"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Vybráno"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Vybráno – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
- <string name="special_app_access_search_keyword" msgid="8032347212290774210">"zvláštní přístup aplikací"</string>
- <string name="special_app_access" msgid="5019319067120213797">"Přístupy pro aplikace"</string>
+ <string name="special_app_access_search_keyword" msgid="8032347212290774210">"speciální přístup aplikací"</string>
+ <string name="special_app_access" msgid="5019319067120213797">"Speciální přístup aplikací"</string>
<string name="no_special_app_access" msgid="6950277571805106247">"Žádný přístup ke spec. aplik."</string>
<string name="special_app_access_no_apps" msgid="4102911722787886970">"Žádné aplikace"</string>
<string name="home_missing_work_profile_support" msgid="1756855847669387977">"Nepodporuje pracovní profil"</string>
diff --git a/PermissionController/res/values-fa/strings.xml b/PermissionController/res/values-fa/strings.xml
index 1ef41ea6d..ae6063dcc 100644
--- a/PermissionController/res/values-fa/strings.xml
+++ b/PermissionController/res/values-fa/strings.xml
@@ -581,7 +581,7 @@
<string name="sensor_permissions_qs" msgid="1022267900031317472">"اجازه‌ها"</string>
<string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"امنیت و حریم خصوصی"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"بررسی وضعیت"</string>
- <string name="privacy_controls_qs" msgid="5780144882040591169">"تنظیمات حریم خصوصی شما"</string>
+ <string name="privacy_controls_qs" msgid="5780144882040591169">"کنترل‌های حریم خصوصی شما"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"تنظیمات بیشتر"</string>
<string name="camera_toggle_label_qs" msgid="3880261453066157285">"دسترسی به دوربین"</string>
<string name="microphone_toggle_label_qs" msgid="8132912469813396552">"دسترسی به میکروفون"</string>
@@ -624,7 +624,7 @@
<string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"این برنامه همیشه می‌تواند به مکانتان دسترسی داشته باشد، حتی وقتی بسته باشد.\n\nبرخی‌از برنامه‌های ایمنی و اضطراری برای اینکه عملکرد موردانتظار را داشته باشند باید به مکان شما در پس‌زمینه دسترسی داشته باشند."</string>
<string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"دسترسی تغییر کرد"</string>
<string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"دیدن استفاده اخیر از مکان"</string>
- <string name="privacy_controls_title" msgid="7605929972256835199">"تنظیمات حریم خصوصی"</string>
+ <string name="privacy_controls_title" msgid="7605929972256835199">"کنترل‌های حریم خصوصی"</string>
<string name="camera_toggle_title" msgid="1251201397431837666">"دسترسی به دوربین"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"دسترسی به میکروفون"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"برای برنامه‌ها و سرویس‌ها"</string>
diff --git a/PermissionController/res/values-iw/strings.xml b/PermissionController/res/values-iw/strings.xml
index a936d3e4e..0fc33a62b 100644
--- a/PermissionController/res/values-iw/strings.xml
+++ b/PermissionController/res/values-iw/strings.xml
@@ -346,7 +346,7 @@
<string name="no_apps_allowed" msgid="7718822655254468631">"אין אפליקציות שקיבלו הרשאה"</string>
<string name="no_apps_allowed_full" msgid="8011716991498934104">"לאף אפליקציה אין הרשאה לכל הקבצים"</string>
<string name="no_apps_allowed_scoped" msgid="4908850477787659501">"לאף אפליקציה אין הרשאה למדיה בלבד"</string>
- <string name="no_apps_denied" msgid="7663435886986784743">"אין אפליקציות שלא קיבלו ההרשאה"</string>
+ <string name="no_apps_denied" msgid="7663435886986784743">"אין אפליקציות שלא קיבלו הרשאה"</string>
<string name="car_permission_selected" msgid="180837028920791596">"נבחר"</string>
<string name="settings" msgid="5409109923158713323">"הגדרות"</string>
<string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"לשירות <xliff:g id="SERVICE_NAME">%s</xliff:g> יש גישה מלאה למכשיר שלך"</string>
diff --git a/PermissionController/res/values-ja/strings.xml b/PermissionController/res/values-ja/strings.xml
index ecd742f55..e7322b07b 100644
--- a/PermissionController/res/values-ja/strings.xml
+++ b/PermissionController/res/values-ja/strings.xml
@@ -74,7 +74,7 @@
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{今日}=1{1 日前}other{# 日前}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"アプリを無効にする"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"このアプリを無効にすると、Android などの他のアプリが正しく動作しなくなるおそれがあります。このアプリはデバイスにプリインストールされているため、削除できません。無効にするには、このアプリをオフにし、デバイスにアプリが表示されないようにします。"</string>
- <string name="app_permission_manager" msgid="3903811137630909550">"権限マネージャ"</string>
+ <string name="app_permission_manager" msgid="3903811137630909550">"権限マネージャー"</string>
<string name="never_ask_again" msgid="4728762438198560329">"今後表示しない"</string>
<string name="no_permissions" msgid="3881676756371148563">"権限がありません"</string>
<string name="additional_permissions" msgid="5801285469338873430">"その他の権限"</string>
diff --git a/PermissionController/res/values-ne/strings.xml b/PermissionController/res/values-ne/strings.xml
index 1a5c620c1..70d474bfc 100644
--- a/PermissionController/res/values-ne/strings.xml
+++ b/PermissionController/res/values-ne/strings.xml
@@ -353,8 +353,8 @@
<string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"पहुँचसम्बन्धी <xliff:g id="NUM_SERVICES">%s</xliff:g> एपहरूले तपाईंको यन्त्रमाथि पूर्ण रूपमा पहुँच राख्न सक्छन्"</string>
<string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ले तपाईंको स्क्रिन, कारबाही र इनपुट हेर्न, कारबाहीहरू गर्न र डिस्प्ले नियन्त्रण गर्न सक्छ।"</string>
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"यी एपहरूले तपाईंको स्क्रिन, कारबाही र इनपुट हेर्न, कारबाहीहरू सम्पादन गर्न र प्रदर्शन नियन्त्रण गर्न सक्छन्।"</string>
- <string name="role_assistant_label" msgid="4727586018198208128">"डिफल्ट डिजिटल सहायक एप"</string>
- <string name="role_assistant_short_label" msgid="3369003713187703399">"डिजिटल सहायक एप"</string>
+ <string name="role_assistant_label" msgid="4727586018198208128">"डिफल्ट डिजिटल एसिस्टेन्ट एप"</string>
+ <string name="role_assistant_short_label" msgid="3369003713187703399">"डिजिटल एसिस्टेन्ट एप"</string>
<string name="role_assistant_description" msgid="6622458130459922952">"सहायक एपहरूले तपाईंले हेर्दै गर्नुभएको स्क्रिनबाट प्राप्त जानकारीमा आधारित भई तपाईंलाई मद्दत गर्न सक्छन्। केही एपहरूले तपाईंलाई एकीकृत सहायता दिन दुवै लन्चर र आवाज संलग्न इनपुट सेवाहरूलाई समर्थन गर्छन्।"</string>
<string name="role_browser_label" msgid="2877796144554070207">"डिफल्ट ब्राउजर एप"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"ब्राउजर"</string>
diff --git a/PermissionController/res/values-sr/strings.xml b/PermissionController/res/values-sr/strings.xml
index 95bbe7f72..d89ebba68 100644
--- a/PermissionController/res/values-sr/strings.xml
+++ b/PermissionController/res/values-sr/strings.xml
@@ -369,7 +369,7 @@
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"бројчаник"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Подразумевана апл. за SMS"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"Апликација за SMS"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Апликације које вам омогућавају да користите број телефона да бисте слали и примали кратке SMS-ове, слике, видео снимке и још тога"</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Апликације које вам омогућавају да користите број телефона да бисте слали и примали кратке текстуалне поруке, слике, видео снимке и друго"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Желите ли да подесите <xliff:g id="APP_NAME">%1$s</xliff:g> као подразумевану апликацју за SMS?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Ова апликација ће добити приступ камери, контактима, фајловима и медијима, микрофону, телефону и SMS-овима"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"SMS, слање SMS-ова, поруке, слање порука"</string>
diff --git a/PermissionController/res/values-sv/strings.xml b/PermissionController/res/values-sv/strings.xml
index 937a12200..c5d3969de 100644
--- a/PermissionController/res/values-sv/strings.xml
+++ b/PermissionController/res/values-sv/strings.xml
@@ -450,7 +450,7 @@
<string name="special_app_access_no_apps" msgid="4102911722787886970">"Inga appar"</string>
<string name="home_missing_work_profile_support" msgid="1756855847669387977">"Jobbprofiler stöds inte"</string>
<string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Obs! Om du startar om mobilen och har ställt in ett skärmlås kan appen inte startas förrän du låser upp mobilen."</string>
- <string name="assistant_confirmation_message" msgid="7476540402884416212">"Assistenten kan läsa information om appar som används i systemet, inklusive information som visas på skärmen eller är åtkomlig i apparna."</string>
+ <string name="assistant_confirmation_message" msgid="7476540402884416212">"Assistent kan läsa information om appar som används i systemet, inklusive information som visas på skärmen eller är åtkomlig i apparna."</string>
<string name="incident_report_channel_name" msgid="3144954065936288440">"Dela felsökningsinformation"</string>
<string name="incident_report_notification_title" msgid="4635984625656519773">"Vill du dela detaljerad felsökningsinformation?"</string>
<string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> vill ladda upp felsökningsinformation."</string>
diff --git a/PermissionController/res/values-te/strings.xml b/PermissionController/res/values-te/strings.xml
index 13097cdf0..a461319a4 100644
--- a/PermissionController/res/values-te/strings.xml
+++ b/PermissionController/res/values-te/strings.xml
@@ -238,7 +238,7 @@
<string name="permission_description_summary_camera" msgid="108004375101882069">"ఈ అనుమతి ఉన్న యాప్‌లు ఫోటోలు తీయగలవు, వీడియోను రికార్డ్ చేయగలవు"</string>
<string name="permission_description_summary_contacts" msgid="2337798886460408996">"ఈ అనుమతి ఉన్న యాప్‌లు మీ కాంటాక్ట్‌లను యాక్సెస్ చేయగలవు"</string>
<string name="permission_description_summary_location" msgid="2817531799933480694">"ఈ అనుమతి ఉన్న యాప్‌లు ఈ పరికర లొకేషన్‌ను యాక్సెస్ చేయగలవు"</string>
- <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"ఈ అనుమతి ఉన్న యాప్‌లు సమీప పరికరాలను గుర్తించవచ్చు, వాటి సంబంధిత స్థానాన్ని తెలుసుకోవచ్చు, అలాగే వాటికి కనెక్ట్ చేయవచ్చు"</string>
+ <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"ఈ అనుమతి ఉన్న యాప్‌లు సమీప పరికరాలను గుర్తించవచ్చు, వాటి సంబంధిత పొజిషన్‌ను తెలుసుకోవచ్చు, అలాగే వాటికి కనెక్ట్ చేయవచ్చు"</string>
<string name="permission_description_summary_microphone" msgid="630834800308329907">"ఈ అనుమతి ఉన్న యాప్‌లు ఆడియోను రికార్డ్ చేయగలవు"</string>
<string name="permission_description_summary_phone" msgid="4515277217435233619">"ఈ అనుమతులు ఉన్న యాప్‌లు ఫోన్ కాల్స్‌ చేయగలవు, మేనేజ్ చేయగలవు"</string>
<string name="permission_description_summary_sensors" msgid="1836045815643119949">"ఈ అనుమతి ఉన్న యాప్‌లు మీ ఆరోగ్య స్థితిని తెలియజేసే గణాంకాల సెన్సార్ డేటాను యాక్సెస్ చేయగలవు"</string>
@@ -480,9 +480,9 @@
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; కోసం లొకేష‌న్‌ యాక్సెస్‌ను మార్చాలా?"</string>
<string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు సంబంధించిన లొకేషన్ యాక్సెస్‌ను మార్చాలా?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"మీరు యాప్ ఉపయోగించనప్పుడు కూడా ఈ యాప్ మీ లొకేష‌న్‌ను ఎప్పటికప్పుడు యాక్సెస్ చేయాల‌ని అనుకుంటోంది."<annotation id="link">"సెట్టింగ్‌లలో అనుమతించండి."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి \"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;\"ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"సమీప పరికరాల సంబంధిత పొజిషన్‌ను కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి \"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;\"ను అనుమతించాలా?"</string>
<string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో సమీప పరికరాలు కనుగొని, కనెక్ట్ అయి, వాటి దూరం అంచనా వేసేలా &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
- <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా? "<annotation id="link">"సెట్టింగ్‌లలో అనుమతించండి."</annotation></string>
+ <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"సమీప పరికరాల సంబంధిత పొజిషన్‌ను కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా? "<annotation id="link">"సెట్టింగ్‌లలో అనుమతించండి."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>కు సంబంధించిన లొకేషన్ యాక్సెస్‌ను సుమారు నుండి ఖచ్చితమైనదిగా మార్చాలా?"</string>
<string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో &lt;b&gt;<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>&lt;/b&gt; లొకేషన్ యాక్సెస్‌ను రమారమి నుండి ఖచ్చితమైన లొకేషన్‌కు మార్చాలా?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"ఈ పరికరానికి సంబంధించి సుమారుగా ఉన్న లొకేషన్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
diff --git a/PermissionController/res/values-watch/donottranslate.xml b/PermissionController/res/values-watch/donottranslate.xml
index 309c51388..43830a93c 100644
--- a/PermissionController/res/values-watch/donottranslate.xml
+++ b/PermissionController/res/values-watch/donottranslate.xml
@@ -51,32 +51,32 @@
<string name="wear_compose_material3_title_medium_font_family">font-family-flex-device-default</string>
<string name="wear_compose_material3_title_large_font_family">font-family-flex-device-default</string>
- <dimen name="wear_compose_material3_arc_small_font_size">14</dimen>
- <dimen name="wear_compose_material3_arc_medium_font_size">15</dimen>
- <dimen name="wear_compose_material3_arc_large_font_size">20</dimen>
- <dimen name="wear_compose_material3_body_extra_small_font_size">10</dimen>
- <dimen name="wear_compose_material3_body_small_font_size">12</dimen>
- <dimen name="wear_compose_material3_body_medium_font_size">14</dimen>
- <dimen name="wear_compose_material3_body_large_font_size">16</dimen>
- <dimen name="wear_compose_material3_display_small_font_size">24</dimen>
- <dimen name="wear_compose_material3_display_medium_font_size">30</dimen>
- <dimen name="wear_compose_material3_display_large_font_size">40</dimen>
- <dimen name="wear_compose_material3_label_small_font_size">13</dimen>
- <dimen name="wear_compose_material3_label_medium_font_size">15</dimen>
- <dimen name="wear_compose_material3_label_large_font_size">20</dimen>
- <dimen name="wear_compose_material3_numeral_extra_small_font_size">24</dimen>
- <dimen name="wear_compose_material3_numeral_small_font_size">30</dimen>
- <dimen name="wear_compose_material3_numeral_medium_font_size">40</dimen>
- <dimen name="wear_compose_material3_numeral_large_font_size">50</dimen>
- <dimen name="wear_compose_material3_numeral_extra_large_font_size">60</dimen>
- <dimen name="wear_compose_material3_title_small_font_size">14</dimen>
- <dimen name="wear_compose_material3_title_medium_font_size">16</dimen>
- <dimen name="wear_compose_material3_title_large_font_size">20</dimen>
+ <dimen name="wear_compose_material3_arc_small_font_size">14sp</dimen>
+ <dimen name="wear_compose_material3_arc_medium_font_size">15sp</dimen>
+ <dimen name="wear_compose_material3_arc_large_font_size">20sp</dimen>
+ <dimen name="wear_compose_material3_body_extra_small_font_size">10sp</dimen>
+ <dimen name="wear_compose_material3_body_small_font_size">12sp</dimen>
+ <dimen name="wear_compose_material3_body_medium_font_size">14sp</dimen>
+ <dimen name="wear_compose_material3_body_large_font_size">16sp</dimen>
+ <dimen name="wear_compose_material3_display_small_font_size">24sp</dimen>
+ <dimen name="wear_compose_material3_display_medium_font_size">30sp</dimen>
+ <dimen name="wear_compose_material3_display_large_font_size">40sp</dimen>
+ <dimen name="wear_compose_material3_label_small_font_size">13sp</dimen>
+ <dimen name="wear_compose_material3_label_medium_font_size">15sp</dimen>
+ <dimen name="wear_compose_material3_label_large_font_size">20sp</dimen>
+ <dimen name="wear_compose_material3_numeral_extra_small_font_size">24sp</dimen>
+ <dimen name="wear_compose_material3_numeral_small_font_size">30sp</dimen>
+ <dimen name="wear_compose_material3_numeral_medium_font_size">40sp</dimen>
+ <dimen name="wear_compose_material3_numeral_large_font_size">50sp</dimen>
+ <dimen name="wear_compose_material3_numeral_extra_large_font_size">60sp</dimen>
+ <dimen name="wear_compose_material3_title_small_font_size">14sp</dimen>
+ <dimen name="wear_compose_material3_title_medium_font_size">16sp</dimen>
+ <dimen name="wear_compose_material3_title_large_font_size">20sp</dimen>
- <dimen name="wear_compose_material3_shape_corner_extra_small_size">4</dimen>
- <dimen name="wear_compose_material3_shape_corner_small_size">8</dimen>
- <dimen name="wear_compose_material3_shape_corner_medium_size">18</dimen>
- <dimen name="wear_compose_material3_shape_corner_large_size">26</dimen>
- <dimen name="wear_compose_material3_shape_corner_extra_large_size">36</dimen>
+ <dimen name="wear_compose_material3_shape_corner_extra_small_size">4dp</dimen>
+ <dimen name="wear_compose_material3_shape_corner_small_size">8dp</dimen>
+ <dimen name="wear_compose_material3_shape_corner_medium_size">18dp</dimen>
+ <dimen name="wear_compose_material3_shape_corner_large_size">26dp</dimen>
+ <dimen name="wear_compose_material3_shape_corner_extra_large_size">36dp</dimen>
</resources>
diff --git a/PermissionController/res/values-zh-rHK/strings.xml b/PermissionController/res/values-zh-rHK/strings.xml
index 3f278f113..07a401ab3 100644
--- a/PermissionController/res/values-zh-rHK/strings.xml
+++ b/PermissionController/res/values-zh-rHK/strings.xml
@@ -362,7 +362,7 @@
<string name="role_browser_request_title" msgid="2895200507835937192">"要將 <xliff:g id="APP_NAME">%1$s</xliff:g> 設為預設瀏覽器應用程式嗎?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"無需任何權限"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"預設電話應用程式"</string>
- <string name="role_dialer_short_label" msgid="7186888549465352489">"手機應用程式"</string>
+ <string name="role_dialer_short_label" msgid="7186888549465352489">"電話應用程式"</string>
<string name="role_dialer_description" msgid="8768708633696539612">"此類應用程式允許你使用自己的裝置撥打和接聽電話"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"要將「<xliff:g id="APP_NAME">%1$s</xliff:g>」設為預設手機應用程式嗎?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"此應用程式將可存取你的相機、通訊錄、麥克風、電話及短訊"</string>
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index b067c41b5..2989d8f96 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"
@@ -707,6 +720,10 @@
featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" />
<permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED"
featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" />
+ <permission name="android.permission.COPY_ACCOUNTS"
+ featureFlag="android.permission.flags.Flags.devicePolicyManagementRoleSplitCreateManagedProfileEnabled" />
+ <permission name="android.permission.REMOVE_ACCOUNTS"
+ featureFlag="android.permission.flags.Flags.devicePolicyManagementRoleSplitCreateManagedProfileEnabled" />
</permissions>
</role>
@@ -714,6 +731,7 @@
name="android.app.role.SYSTEM_CONTACTS"
defaultHolders="config_systemContacts"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -730,6 +748,7 @@
allowBypassingQualification="true"
defaultHolders="config_systemSpeechRecognizer"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -751,6 +770,7 @@
name="android.app.role.SYSTEM_WIFI_COEX_MANAGER"
defaultHolders="config_systemWifiCoexManager"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -765,6 +785,7 @@
name="android.app.role.SYSTEM_WELLBEING"
defaultHolders="config_systemWellbeing"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -793,6 +814,7 @@
behavior="v31.TelevisionRoleBehavior"
defaultHolders="config_systemTelevisionNotificationHandler"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -811,6 +833,7 @@
name="android.app.role.SYSTEM_COMPANION_DEVICE_PROVIDER"
defaultHolders="config_systemCompanionDeviceProvider"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -846,6 +869,7 @@
name="android.app.role.SYSTEM_UI_INTELLIGENCE"
defaultHolders="config_systemUiIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -901,6 +925,7 @@
name="android.app.role.SYSTEM_AMBIENT_AUDIO_INTELLIGENCE"
defaultHolders="config_systemAmbientAudioIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -947,6 +972,7 @@
name="android.app.role.SYSTEM_AUDIO_INTELLIGENCE"
defaultHolders="config_systemAudioIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -993,6 +1019,7 @@
name="android.app.role.SYSTEM_NOTIFICATION_INTELLIGENCE"
defaultHolders="config_systemNotificationIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1035,6 +1062,7 @@
name="android.app.role.SYSTEM_TEXT_INTELLIGENCE"
defaultHolders="config_systemTextIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1075,6 +1103,7 @@
name="android.app.role.SYSTEM_VISUAL_INTELLIGENCE"
defaultHolders="config_systemVisualIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1100,6 +1129,7 @@
name="android.app.role.SYSTEM_DOCUMENT_MANAGER"
behavior="v33.DocumentManagerRoleBehavior"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1132,6 +1162,7 @@
allowBypassingQualification="true"
defaultHolders="config_systemActivityRecognizer"
exclusive="false"
+ exclusivity="none"
static="true"
systemOnly="true"
visible="false">
@@ -1151,6 +1182,7 @@
name="android.app.role.SYSTEM_UI"
defaultHolders="config_systemUi"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1177,6 +1209,7 @@
behavior="v31.TelevisionRoleBehavior"
defaultHolders="config_systemTelevisionRemoteService"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1196,6 +1229,7 @@
behavior="v33.CompanionDeviceAppStreamingRoleBehavior"
description="@string/role_app_streaming_description"
exclusive="false"
+ exclusivity="none"
minSdkVersion="33"
systemOnly="true"
visible="false">
@@ -1220,6 +1254,7 @@
behavior="v33.CompanionDeviceComputerRoleBehavior"
description="@string/role_companion_device_computer_description"
exclusive="false"
+ exclusivity="none"
minSdkVersion="33"
systemOnly="true"
visible="false">
@@ -1239,6 +1274,7 @@
name="android.app.role.COMPANION_DEVICE_GLASSES"
behavior="v34.CompanionDeviceGlassesRoleBehavior"
exclusive="false"
+ exclusivity="none"
minSdkVersion="34"
systemOnly="false"
visible="false">
@@ -1265,12 +1301,15 @@
name="android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING"
allowBypassingQualification="true"
exclusive="false"
+ exclusivity="none"
minSdkVersion="34"
systemOnly="true"
visible="false">
<permissions>
<permission-set name="nearby_devices" />
<permission-set name="virtual_device" />
+ <permission-set name="notifications"
+ featureFlag="android.companion.virtualdevice.flags.Flags.notificationsForDeviceStreaming" />
</permissions>
</role>
@@ -1278,6 +1317,7 @@
name="android.app.role.SYSTEM_SUPERVISION"
defaultHolders="config_systemSupervision"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1288,6 +1328,46 @@
<permission name="android.permission.MANAGE_DEFAULT_APPLICATIONS" minSdkVersion="34"/>
<permission name="android.permission.SUSPEND_APPS"/>
<permission name="android.permission.SYSTEM_APPLICATION_OVERLAY"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_FUN"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_KEYGUARD"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCATION"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCK"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_TIME"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
+ <permission name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
+ featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/>
</permissions>
</role>
@@ -1300,6 +1380,7 @@
behavior="v33.DevicePolicyManagementRoleBehavior"
defaultHolders="config_devicePolicyManagement"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="false"
@@ -1405,6 +1486,10 @@
<permission name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY" minSdkVersion="35" />
<permission name="android.permission.MANAGE_DEVICE_POLICY_LOCALE" minSdkVersion="35" />
<permission name="android.permission.MANAGE_DEVICE_POLICY_SMS" minSdkVersion="35" />
+ <permission name="android.permission.COPY_ACCOUNTS"
+ featureFlag="android.permission.flags.Flags.devicePolicyManagementRoleSplitCreateManagedProfileEnabled" />
+ <permission name="android.permission.REMOVE_ACCOUNTS"
+ featureFlag="android.permission.flags.Flags.devicePolicyManagementRoleSplitCreateManagedProfileEnabled" />
</permissions>
</role>
@@ -1412,6 +1497,7 @@
name="android.app.role.SYSTEM_APP_PROTECTION_SERVICE"
defaultHolders="config_systemAppProtectionService"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1440,6 +1526,7 @@
behavior="v31.AutomotiveRoleBehavior"
defaultHolders="config_systemAutomotiveCalendarSyncManager"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1461,6 +1548,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 +1622,7 @@
name="android.app.role.SYSTEM_SETTINGS_INTELLIGENCE"
defaultHolders="config_systemSettingsIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1551,6 +1640,7 @@
name="android.app.role.SYSTEM_BLUETOOTH_STACK"
defaultHolders="config_systemBluetoothStack"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1575,6 +1665,7 @@
<role
name="android.app.role.FINANCED_DEVICE_KIOSK"
exclusive="true"
+ exclusivity="user"
minSdkVersion="34"
visible="false">
<permissions>
@@ -1590,6 +1681,7 @@
name="android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER"
defaultHolders="config_systemFinancedDeviceController"
exclusive="true"
+ exclusivity="user"
minSdkVersion="34"
static="true"
systemOnly="true"
@@ -1617,6 +1709,7 @@
behavior="v33.SystemWearHealthServiceRoleBehavior"
defaultHolders="config_systemWearHealthService"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1644,6 +1737,7 @@
defaultHolders="config_defaultNotes"
description="@string/role_notes_description"
exclusive="true"
+ exclusivity="user"
label="@string/role_notes_label"
minSdkVersion="34"
overrideUserWhenGranting="true"
@@ -1685,6 +1779,7 @@
allowBypassingQualification="true"
defaultHolders="config_systemCallStreaming"
exclusive="true"
+ exclusivity="user"
minSdkVersion="34"
static="true"
systemOnly="true"
@@ -1707,6 +1802,7 @@
behavior="v35.RetailDemoRoleBehavior"
defaultHolders="config_defaultRetailDemo"
exclusive="true"
+ exclusivity="user"
minSdkVersion="35"
static="true"
visible="false">
@@ -1733,6 +1829,7 @@
defaultHolders="config_defaultWallet"
description="@string/role_wallet_description"
exclusive="true"
+ exclusivity="user"
label="@string/role_wallet_label"
minSdkVersion="35"
overrideUserWhenGranting="true"
@@ -1743,5 +1840,39 @@
shortLabel="@string/role_wallet_short_label"
uiBehavior="v35.WalletRoleUiBehavior"/>
+ <role
+ name="android.app.role.SYSTEM_DEPENDENCY_INSTALLER"
+ allowBypassingQualification="true"
+ defaultHolders="config_systemDependencyInstaller"
+ exclusive="true"
+ exclusivity="user"
+ featureFlag="android.content.pm.Flags.sdkDependencyInstaller"
+ static="true"
+ systemOnly="true"
+ visible="false">
+ <required-components>
+ <service permission="android.permission.BIND_DEPENDENCY_INSTALLER">
+ <intent-filter>
+ <action name="android.content.pm.action.INSTALL_DEPENDENCY" />
+ </intent-filter>
+ </service>
+ </required-components>
+ <permissions>
+ <permission name="android.permission.ACCESS_SHARED_LIBRARIES" />
+ <permission name="android.permission.INSTALL_DEPENDENCY_SHARED_LIBRARIES" />
+ </permissions>
+ </role>
+
+ <!---
+ ~ A role for testing cross-user roles (exclusivity="profileGroup"). This should never be used
+ ~ to gate any actual functionality.
+ -->
+ <role
+ name="android.app.role.RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY"
+ exclusive="true"
+ exclusivity="profileGroup"
+ featureFlag="com.android.permission.flags.Flags.crossUserRoleEnabled"
+ showNone="true"
+ visible="false"/>
</roles>
diff --git a/PermissionController/role-controller/Android.bp b/PermissionController/role-controller/Android.bp
index 166823b08..9a046a397 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,11 +27,13 @@ java_library {
],
libs: [
"androidx.annotation_annotation",
+ "com.android.permission.flags-aconfig-java",
],
static_libs: [
"modules-utils-build_system",
"android.app.appfunctions.exported-flags-aconfig-java",
"android.companion.virtualdevice.flags-aconfig-java-export",
+ "android.content.pm.flags-aconfig-java-export",
"android.permission.flags-aconfig-java-export",
"android.os.flags-aconfig-java-export",
],
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..e3269a146 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
@@ -37,7 +37,9 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseBooleanArray;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -49,6 +51,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 +87,35 @@ 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;
+
+ /** Set of valid exclusivity values. */
+ private static final SparseBooleanArray sExclusivityValues = new SparseBooleanArray();
+ static {
+ sExclusivityValues.put(EXCLUSIVITY_NONE, true);
+ sExclusivityValues.put(EXCLUSIVITY_USER, true);
+ sExclusivityValues.put(EXCLUSIVITY_PROFILE_GROUP, true);
+ }
+
/**
* The name of this role. Must be unique.
*/
@@ -110,9 +143,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 +220,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 +275,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 +291,7 @@ public class Role {
mBehavior = behavior;
mDefaultHoldersResourceName = defaultHoldersResourceName;
mDescriptionResource = descriptionResource;
- mExclusive = exclusive;
+ mExclusivity = exclusivity;
mFallBackToDefaultHolder = fallBackToDefaultHolder;
mFeatureFlag = featureFlag;
mLabelResource = labelResource;
@@ -298,7 +332,25 @@ public class Role {
}
public boolean isExclusive() {
- return mExclusive;
+ return getExclusivity() != EXCLUSIVITY_NONE;
+ }
+
+ @Exclusivity
+ public int getExclusivity() {
+ if (com.android.permission.flags.Flags.crossUserRoleEnabled() && mBehavior != null) {
+ Integer exclusivity = mBehavior.getExclusivity();
+ if (exclusivity != null) {
+ if (!sExclusivityValues.get(exclusivity)) {
+ throw new IllegalArgumentException("Invalid exclusivity: " + exclusivity);
+ }
+ if (mShowNone && exclusivity == EXCLUSIVITY_NONE) {
+ throw new IllegalArgumentException(
+ "Role cannot be non-exclusive when showNone is true: " + exclusivity);
+ }
+ return exclusivity;
+ }
+ }
+ return mExclusivity;
}
@Nullable
@@ -414,6 +466,8 @@ public class Role {
if (!isAvailableByFeatureFlagAndSdkVersion()) {
return false;
}
+ // TODO(b/376133070): ensure that cross-user role is only available if also available for
+ // the profile-group's full user
if (mBehavior != null) {
return mBehavior.isAvailableAsUser(this, user, context);
}
@@ -1041,7 +1095,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 +1158,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/RoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java
index 3849a50e3..86ca8e2ce 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java
@@ -32,6 +32,14 @@ import java.util.List;
public interface RoleBehavior {
/**
+ * @see Role#getExclusivity()
+ */
+ @Nullable
+ default Integer getExclusivity() {
+ return null;
+ }
+
+ /**
* @see Role#onRoleAddedAsUser(UserHandle, Context)
*/
default void onRoleAddedAsUser(@NonNull Role role, @NonNull UserHandle user,
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/compat/AppOpsManagerCompat.java b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppOpsManagerCompat.java
new file mode 100644
index 000000000..05d69e998
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppOpsManagerCompat.java
@@ -0,0 +1,49 @@
+/*
+ * 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.compat;
+
+import android.annotation.SuppressLint;
+import android.app.AppOpsManager;
+import android.permission.flags.Flags;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Helper AppOpsManager compat class
+ */
+public class AppOpsManagerCompat {
+
+ private AppOpsManagerCompat() {}
+
+ /**
+ * For platform version <= V, call the deprecated unsafeCheckOpRawNoThrow. For newer platforms,
+ * call the new API checkOpRawNoThrow.
+ *
+ * @return the raw mode of the op
+ */
+ // TODO: b/379749734
+ @SuppressWarnings("deprecation")
+ @SuppressLint("NewApi")
+ public static int checkOpRawNoThrow(@NonNull AppOpsManager appOpsManager, @NonNull String op,
+ int uid, @NonNull String packageName) {
+ if (Flags.checkOpOverloadApiEnabled()) {
+ return appOpsManager.checkOpRawNoThrow(op, uid, packageName, null);
+ } else {
+ return appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt
index 1e44f16bd..3202f5b69 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt
@@ -19,6 +19,7 @@ package com.android.permissioncontroller.permission.data
import android.app.AppOpsManager
import android.app.Application
import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.compat.AppOpsManagerCompat
/**
* A LiveData which represents the appop state
@@ -36,13 +37,13 @@ private constructor(
private val app: Application,
private val packageName: String,
private val op: String,
- private val uid: Int
+ private val uid: Int,
) : SmartUpdateMediatorLiveData<Int>() {
private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!!
override fun onUpdate() {
- value = appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName)
+ value = AppOpsManagerCompat.checkOpRawNoThrow(appOpsManager, op, uid, packageName)
}
override fun onActive() {
@@ -62,7 +63,7 @@ private constructor(
PermissionControllerApplication.get(),
key.first,
key.second,
- key.third
+ key.third,
)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt
index 4a2d3b68a..2b6d9728e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt
@@ -28,6 +28,7 @@ import android.app.Application
import android.os.Build
import android.os.UserHandle
import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.compat.AppOpsManagerCompat
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
import kotlinx.coroutines.Job
@@ -46,7 +47,7 @@ object FullStoragePermissionAppsLiveData :
val packageName: String,
val user: UserHandle,
val isLegacy: Boolean,
- val isGranted: Boolean
+ val isGranted: Boolean,
)
init {
@@ -88,7 +89,7 @@ object FullStoragePermissionAppsLiveData :
fun getFullStorageStateForPackage(
appOpsManager: AppOpsManager,
packageInfo: LightPackageInfo,
- userHandle: UserHandle? = null
+ userHandle: UserHandle? = null,
): FullStoragePackageState? {
val sdk = packageInfo.targetSdkVersion
val user = userHandle ?: UserHandle.getUserHandleForUid(packageInfo.uid)
@@ -97,29 +98,31 @@ object FullStoragePermissionAppsLiveData :
packageInfo.packageName,
user,
isLegacy = true,
- isGranted = true
+ isGranted = true,
)
} else if (
sdk <= Build.VERSION_CODES.Q &&
- appOpsManager.unsafeCheckOpNoThrow(
+ AppOpsManagerCompat.checkOpRawNoThrow(
+ appOpsManager,
OPSTR_LEGACY_STORAGE,
packageInfo.uid,
- packageInfo.packageName
+ packageInfo.packageName,
) == MODE_ALLOWED
) {
return FullStoragePackageState(
packageInfo.packageName,
user,
isLegacy = true,
- isGranted = true
+ isGranted = true,
)
}
if (MANAGE_EXTERNAL_STORAGE in packageInfo.requestedPermissions) {
val mode =
- appOpsManager.unsafeCheckOpNoThrow(
+ AppOpsManagerCompat.checkOpRawNoThrow(
+ appOpsManager,
OPSTR_MANAGE_EXTERNAL_STORAGE,
packageInfo.uid,
- packageInfo.packageName
+ packageInfo.packageName,
)
val granted =
mode == MODE_ALLOWED ||
@@ -130,7 +133,7 @@ object FullStoragePermissionAppsLiveData :
packageInfo.packageName,
user,
isLegacy = false,
- isGranted = granted
+ isGranted = granted,
)
}
return null
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
index a4f629d80..c1479caf2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
@@ -46,7 +46,6 @@ import android.app.KeyguardManager;
import android.app.ecm.EnhancedConfirmationManager;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -195,7 +194,7 @@ public class GrantPermissionsActivity extends SettingsActivity
/** A list of permissions requested on an app's behalf by the system. Usually Implicitly
* requested, although this isn't necessarily always the case.
*/
- private List<String> mSystemRequestedPermissions = new ArrayList<>();
+ private final List<String> mSystemRequestedPermissions = new ArrayList<>();
/** A copy of the list of permissions originally requested in the intent to this activity */
private String[] mOriginalRequestedPermissions = new String[0];
@@ -209,7 +208,7 @@ public class GrantPermissionsActivity extends SettingsActivity
* A list of other GrantPermissionActivities for the same package which passed their list of
* permissions to this one. They need to be informed when this activity finishes.
*/
- private List<GrantPermissionsActivity> mFollowerActivities = new ArrayList<>();
+ private final List<GrantPermissionsActivity> mFollowerActivities = new ArrayList<>();
/** Whether this activity has asked another GrantPermissionsActivity to show on its behalf */
private boolean mDelegated;
@@ -235,7 +234,7 @@ public class GrantPermissionsActivity extends SettingsActivity
private PackageManager mPackageManager;
- private ActivityResultLauncher<Intent> mShowWarningDialog =
+ private final ActivityResultLauncher<Intent> mShowWarningDialog =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
@@ -284,7 +283,7 @@ public class GrantPermissionsActivity extends SettingsActivity
return;
}
try {
- PackageInfo packageInfo = mPackageManager.getPackageInfo(mTargetPackage, 0);
+ mPackageManager.getPackageInfo(mTargetPackage, 0);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Unable to get package info for the calling package.", e);
finishAfterTransition();
@@ -314,20 +313,23 @@ public class GrantPermissionsActivity extends SettingsActivity
.getPackageManager();
}
- // When the dialog is streamed to a remote device, verify requested permissions are all
- // device aware and target device is the same as the remote device. Otherwise show a
- // warning dialog.
+ // When the permission grant dialog is streamed to a virtual device, and when requested
+ // permissions include both device-aware permissions and non-device aware permissions,
+ // device-aware permissions will use virtual device id and non-device aware permissions
+ // will use default device id for granting. If flag is not enabled, we would show a
+ // warning dialog for this use case.
if (getDeviceId() != ContextCompat.DEVICE_ID_DEFAULT) {
boolean showWarningDialog = mTargetDeviceId != getDeviceId();
for (String permission : mRequestedPermissions) {
- if (!MultiDeviceUtils.isPermissionDeviceAware(
- getApplicationContext(), mTargetDeviceId, permission)) {
+ if (!MultiDeviceUtils.isPermissionDeviceAware(getApplicationContext(),
+ mTargetDeviceId, permission)) {
showWarningDialog = true;
+ break;
}
}
- if (showWarningDialog) {
+ if (showWarningDialog && !Flags.allowHostPermissionDialogsOnVirtualDevices()) {
mShowWarningDialog.launch(
new Intent(this, PermissionDialogStreamingBlockedActivity.class));
return;
@@ -1115,9 +1117,17 @@ public class GrantPermissionsActivity extends SettingsActivity
if ((mDelegated || (mViewModel != null && mViewModel.shouldReturnPermissionState()))
&& mTargetPackage != null) {
+ PackageManager defaultDevicePackageManager = SdkLevel.isAtLeastV()
+ && mTargetDeviceId != ContextCompat.DEVICE_ID_DEFAULT
+ ? createDeviceContext(ContextCompat.DEVICE_ID_DEFAULT).getPackageManager()
+ : mPackageManager;
+ PackageManager targetDevicePackageManager = mPackageManager;
for (int i = 0; i < resultPermissions.length; i++) {
- grantResults[i] =
- mPackageManager.checkPermission(resultPermissions[i], mTargetPackage);
+ String permission = resultPermissions[i];
+ PackageManager pm = MultiDeviceUtils.isPermissionDeviceAware(
+ getApplicationContext(), mTargetDeviceId, permission)
+ ? targetDevicePackageManager : defaultDevicePackageManager;
+ grantResults[i] = pm.checkPermission(resultPermissions[i], mTargetPackage);
}
} else {
grantResults = new int[0];
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
index 0a01929e6..1e5b96c2e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
@@ -29,6 +29,7 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.app.admin.DevicePolicyManager
+import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED
@@ -41,6 +42,8 @@ import android.os.Build
import android.os.Bundle
import android.os.Process
import android.permission.PermissionManager
+import android.permission.flags.Flags
+import android.util.ArrayMap
import android.util.Log
import androidx.core.util.Consumer
import androidx.lifecycle.ViewModel
@@ -116,6 +119,7 @@ import com.android.permissioncontroller.permission.utils.SafetyNetLogger
import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils
import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils
+import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils.isPermissionDeviceAware
/**
* ViewModel for the GrantPermissionsActivity. Tracks all permission groups that are affected by the
@@ -153,6 +157,21 @@ class GrantPermissionsViewModel(
} else {
null
}
+ private val permissionGroupToDeviceIdMap: Map<String, Int> =
+ if (SdkLevel.isAtLeastV() && Flags.allowHostPermissionDialogsOnVirtualDevices()) {
+ requestedPermissions
+ .filter({ PermissionMapping.getGroupOfPlatformPermission(it) != null })
+ .associateBy({ PermissionMapping.getGroupOfPlatformPermission(it)!! }, {
+ if (isPermissionDeviceAware(
+ app.applicationContext,
+ deviceId,
+ it
+ )
+ ) deviceId else Context.DEVICE_ID_DEFAULT
+ })
+ } else {
+ ArrayMap()
+ }
private val dpm = app.getSystemService(DevicePolicyManager::class.java)!!
private val permissionPolicy = dpm.getPermissionPolicy(null)
private val groupStates = mutableMapOf<String, GroupState>()
@@ -314,7 +333,8 @@ class GrantPermissionsViewModel(
}
val getLiveDataFun = { groupName: String ->
- LightAppPermGroupLiveData[packageName, groupName, user, deviceId]
+ LightAppPermGroupLiveData[packageName, groupName, user,
+ permissionGroupToDeviceIdMap.get(groupName) ?: deviceId]
}
setSourcesToDifference(requestedGroups.keys, appPermGroupLiveDatas, getLiveDataFun)
}
@@ -398,7 +418,8 @@ class GrantPermissionsViewModel(
safetyLabel,
groupState.group.permGroupName
),
- deviceId
+ permissionGroupToDeviceIdMap.get(groupState.group.permGroupName)
+ ?: deviceId
)
)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
index f94999626..6dabe8ab7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
@@ -119,7 +119,7 @@ class NumCustomPermGroupsWithPackagesLiveData() : SmartUpdateMediatorLiveData<In
/**
* A LiveData that tracks the names of the platform-defined permission groups, such that at least
* one of the permissions in the group has been requested at runtime by at least one non-system
- * application or has been pregranted to a non-system application.
+ * application.
*
* @param app The current application of the fragment
*/
@@ -130,10 +130,7 @@ class UsedStandardPermGroupNamesLiveData(private val app: Application) :
permGroups ->
if (permGroups.values.any { it != null }) {
value =
- permGroups
- .filterValues { it != null && it.nonSystemUserSetOrPreGranted > 0 }
- .keys
- .toList()
+ permGroups.filterValues { it != null && it.nonSystemTotal > 0 }.keys.toList()
}
}
}
@@ -145,8 +142,7 @@ class UsedStandardPermGroupNamesLiveData(private val app: Application) :
/**
* A LiveData that tracks the names of the platform-defined permission groups, such that none of the
- * the permissions in the group has been requested at runtime by any non-system application nor has
- * been pregranted to any non-system application.
+ * the permissions in the group has been requested at runtime by any non-system application.
*
* @param app The current application of the fragment
*/
@@ -155,11 +151,7 @@ class UnusedStandardPermGroupNamesLiveData(private val app: Application) :
init {
addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) {
permGroups ->
- value =
- permGroups
- .filterValues { it != null && it.nonSystemUserSetOrPreGranted == 0 }
- .keys
- .toList()
+ value = permGroups.filterValues { it != null && it.nonSystemTotal == 0 }.keys.toList()
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
index 50a19e571..1498b91b6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
@@ -22,7 +22,6 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.stringResource
-import com.android.permission.flags.Flags
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALWAYS_BUTTON
import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON
@@ -43,6 +42,7 @@ import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipTo
import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton
import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControl
import com.android.permissioncontroller.permission.ui.wear.model.WearGrantPermissionsViewModel
+import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper
import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5
import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL3
@@ -58,9 +58,8 @@ fun WearGrantPermissionsScreen(
val locationVisibilities = viewModel.locationVisibilitiesLiveData.observeAsState(emptyList())
val preciseLocationChecked = viewModel.preciseLocationCheckedLiveData.observeAsState(false)
val buttonVisibilities = viewModel.buttonVisibilitiesLiveData.observeAsState(emptyList())
- val useMaterial3Controls = Flags.wearComposeMaterial3()
val materialUIVersion =
- if (useMaterial3Controls) {
+ if (ResourceHelper.material3Enabled) {
MATERIAL3
} else {
MATERIAL2_5
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt
index bd1946759..9aacd65d3 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt
@@ -17,6 +17,7 @@
package com.android.permissioncontroller.permission.ui.wear
import android.graphics.drawable.Drawable
+import android.permission.flags.Flags
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
@@ -77,7 +78,13 @@ internal fun getPermGroupChipParams(
}
return permissionGroups
// Removing Health Connect from the list of permissions to fix b/331260850
- .filterNot { Utils.isHealthPermissionGroup(it.key) }
+ .let {
+ if (Flags.replaceBodySensorPermissionEnabled()) {
+ it
+ } else {
+ it.filterNot { Utils.isHealthPermissionGroup(it.key) }
+ }
+ }
.mapNotNull {
val uiInfo = it.value ?: return@mapNotNull null
PermGroupChipParam(
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..1d660ca35 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
@@ -16,14 +16,21 @@
package com.android.permissioncontroller.permission.ui.wear.elements.material3
import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.requiredSizeIn
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.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
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
@@ -76,9 +83,9 @@ fun WearPermissionButton(
}
@Composable
-private fun WearPermissionButtonInternal(
- label: String,
+internal fun WearPermissionButtonInternal(
modifier: Modifier = Modifier,
+ label: String? = null,
iconBuilder: WearPermissionIconBuilder? = null,
labelMaxLines: Int? = null,
secondaryLabel: String? = null,
@@ -86,16 +93,31 @@ private fun WearPermissionButtonInternal(
onClick: () -> Unit,
enabled: Boolean = true,
colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
+ contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
+ requiresMinimumHeight: Boolean = true,
) {
+ val minHeight: Dp =
+ if (requiresMinimumHeight) {
+ 0.dp
+ } else {
+ 1.dp
+ }
val iconParam: (@Composable BoxScope.() -> Unit)? = iconBuilder?.let { { it.build() } }
-
- val labelParam: (@Composable RowScope.() -> Unit) = {
- Text(
- text = label,
- modifier = Modifier.fillMaxWidth(),
- maxLines = labelMaxLines ?: LocalTextConfiguration.current.maxLines,
- )
- }
+ val labelParam: (@Composable RowScope.() -> Unit)? =
+ label?.let {
+ {
+ Text(
+ text = label,
+ modifier = Modifier.fillMaxWidth(),
+ maxLines = labelMaxLines ?: LocalTextConfiguration.current.maxLines,
+ style =
+ LocalTextStyle.current.copy(
+ fontWeight = FontWeight.W600,
+ hyphens = Hyphens.Auto,
+ ),
+ )
+ }
+ }
val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
secondaryLabel?.let {
@@ -110,11 +132,12 @@ private fun WearPermissionButtonInternal(
Button(
icon = iconParam,
- label = labelParam,
+ label = labelParam ?: {},
secondaryLabel = secondaryLabelParam,
enabled = enabled,
onClick = onClick,
- modifier = modifier.fillMaxWidth(),
+ modifier = modifier.requiredSizeIn(minHeight = minHeight).fillMaxWidth(),
+ contentPadding = contentPadding,
colors = colors,
)
}
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/WearPermissionIconBuilder.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionIconBuilder.kt
index 65a85db7e..b7521d073 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionIconBuilder.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionIconBuilder.kt
@@ -23,8 +23,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
-import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.IconButtonDefaults
import com.android.permissioncontroller.permission.ui.wear.elements.rememberDrawablePainter
/**
@@ -54,7 +54,7 @@ class WearPermissionIconBuilder private constructor() {
var contentDescription: String? = null
private set
- var modifier: Modifier = Modifier.size(ButtonDefaults.IconSize)
+ var modifier: Modifier = Modifier.size(IconButtonDefaults.LargeIconSize)
private set
var tint: Color = Color.Unspecified
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..10125c873
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material3.ButtonDefaults
+import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion
+
+/** This component is creates a transparent styled button to use as a list footer. */
+@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 {
+ WearPermissionButtonInternal(
+ iconBuilder = iconBuilder,
+ secondaryLabel = label,
+ secondaryLabelMaxLines = Int.MAX_VALUE,
+ onClick = onClick,
+ contentPadding = PaddingValues(0.dp),
+ colors = ButtonDefaults.childButtonColors(),
+ requiresMinimumHeight = false,
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt
index 3c2c38578..c7ed0958c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt
@@ -16,6 +16,7 @@
package com.android.permissioncontroller.permission.ui.wear.theme
import android.content.Context
+import android.os.SystemProperties
import androidx.annotation.ColorRes
import androidx.annotation.DimenRes
import androidx.annotation.DoNotInline
@@ -23,6 +24,14 @@ import androidx.annotation.StringRes
import androidx.compose.ui.graphics.Color
internal object ResourceHelper {
+
+ private const val MATERIAL3_ENABLED_SYSPROP = "persist.cw_build.bluechip.enabled"
+
+ val material3Enabled: Boolean
+ get() {
+ return SystemProperties.getBoolean(MATERIAL3_ENABLED_SYSPROP, false)
+ }
+
@DoNotInline
fun getColor(context: Context, @ColorRes id: Int): Color? {
return try {
@@ -45,7 +54,7 @@ internal object ResourceHelper {
@DoNotInline
fun getDimen(context: Context, @DimenRes id: Int): Float? {
return try {
- context.resources.getDimension(id)
+ context.resources.getDimension(id) / context.resources.displayMetrics.density
} catch (e: Exception) {
null
}
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..8823bee07 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
@@ -30,20 +30,20 @@ import androidx.wear.compose.material.Colors
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Typography
import androidx.wear.compose.material3.MaterialTheme as Material3Theme
-import com.android.permission.flags.Flags
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 +53,10 @@ fun WearPermissionTheme(
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
WearPermissionLegacyTheme(content)
} else {
- val useBridgedTheme = Flags.wearComposeMaterial3()
+ // Whether we are ready to use material3 for any screen.
+ val useBridgedTheme = ResourceHelper.material3Enabled
+
+ // 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..aa9b31e0d 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.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.ResourceHelper
+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 =
@@ -74,8 +78,14 @@ fun WearRequestRoleScreen(
helper.initializeSelectedPackageName()
}
}
-
+ val materialUIVersion =
+ if (ResourceHelper.material3Enabled) {
+ MATERIAL3
+ } else {
+ MATERIAL2_5
+ }
WearRequestRoleContent(
+ materialUIVersion,
isLoading,
helper,
roleLiveData.value,
@@ -85,7 +95,7 @@ fun WearRequestRoleScreen(
onCheckedChanged,
onDontAskAgainCheckedChanged,
onSetAsDefault,
- onCanceled
+ onCanceled,
)
if (isLoading && roleLiveData.value.isNotEmpty()) {
@@ -95,6 +105,7 @@ fun WearRequestRoleScreen(
@Composable
internal fun WearRequestRoleContent(
+ materialUIVersion: WearPermissionMaterialUIVersion,
isLoading: Boolean,
helper: WearRequestRoleHelper,
qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
@@ -104,56 +115,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 +192,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/inprocess/Android.bp b/PermissionController/tests/inprocess/Android.bp
index 60b35e80f..4cd9e0e6f 100644
--- a/PermissionController/tests/inprocess/Android.bp
+++ b/PermissionController/tests/inprocess/Android.bp
@@ -50,6 +50,9 @@ android_test {
"compatibility-device-util-axt",
"kotlin-test",
"permission-test-util-lib",
+ // This may result in two flag libs being included. This should only be used for Flag
+ //string referencing for test annotations.
+ "com.android.permission.flags-aconfig-java-export",
],
data: [
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/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt
index 0f4f3841a..ecc7e161f 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt
@@ -90,26 +90,6 @@ class HealthConnectAllAppPermissionFragmentTest : BasePermissionUiTest() {
}
}
- @Test
- fun invalidGrantedUsedHealthConnectPermissionsAreListed() {
- installInvalidTestAppThatUsesHealthConnectPermission()
- grantTestAppPermission(HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED)
-
- startManageAppPermissionsActivity()
-
- // Ensure that Health Connect permission group permissions are present if a single one is
- // already granted, regardless of whether the intent filters are incorrectly or not setup
- // for the app
- eventually {
- waitFindObject(By.text(HEALTH_CONNECT_LABEL))
- waitFindObject(By.text(HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED_LABEL))
-
- // READ_STEPS is not granted, but should still be present due to READ_FLOORS_CLIMBED
- // being granted
- waitFindObject(By.text(HEALTH_CONNECT_PERMISSION_READ_STEPS_LABEL))
- }
- }
-
private fun startManageAppPermissionsActivity() {
uiDevice.performActionAndWait(
{
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt
index 04dbac39f..b2d47a7d7 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt
@@ -70,16 +70,6 @@ class HealthConnectAppPermissionFragmentTest : BasePermissionUiTest() {
waitUntilObjectGone(By.text(HEALTH_CONNECT_LABEL), TIMEOUT_SHORT)
}
- @Test
- fun invalidGrantedUsedHealthConnectPermissionsAreListed() {
- installInvalidTestAppThatUsesHealthConnectPermission()
- grantTestAppPermission(HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED)
-
- startManageAppPermissionsActivity()
-
- eventually { waitFindObject(By.text(HEALTH_CONNECT_LABEL)) }
- }
-
private fun startManageAppPermissionsActivity() {
runWithShellPermissionIdentity {
instrumentationContext.startActivity(
diff --git a/SafetyCenter/Resources/res/values-fa-v35/strings.xml b/SafetyCenter/Resources/res/values-fa-v35/strings.xml
index 878c10bb4..40072433d 100644
--- a/SafetyCenter/Resources/res/values-fa-v35/strings.xml
+++ b/SafetyCenter/Resources/res/values-fa-v35/strings.xml
@@ -21,7 +21,7 @@
<string name="cellular_network_security_summary" msgid="7319307247487475572">"نوع شبکه، رمزگذاری، کنترل‌های اعلان"</string>
<string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
<string name="privacy_title" msgid="7047524783080782769">"حریم خصوصی"</string>
- <string name="privacy_sources_title" msgid="309304028326660956">"تنظیمات حریم خصوصی"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"کنترل‌های حریم خصوصی"</string>
<string name="privacy_sources_summary" msgid="2165270848857537278">"اجازه‌ها، کنترل‌ها"</string>
<string name="privacy_additional_title" msgid="4239060639056083649"></string>
<string name="private_space_title" msgid="6158245041481535879">"فضای خصوصی"</string>
diff --git a/SafetyCenter/Resources/res/values-fa/strings.xml b/SafetyCenter/Resources/res/values-fa/strings.xml
index bdf2063f1..01835a8e5 100644
--- a/SafetyCenter/Resources/res/values-fa/strings.xml
+++ b/SafetyCenter/Resources/res/values-fa/strings.xml
@@ -33,9 +33,9 @@
<string name="permission_manager_title" msgid="5277347862821255015">"مدیر اجازه‌ها"</string>
<string name="permission_manager_summary" msgid="8099852107340970790">"کنترل دسترسی برنامه به داده‌های شما"</string>
<string name="permission_manager_search_terms" msgid="2895147613099694722">"اجازه‌ها، مدیر اجازه‌ها"</string>
- <string name="privacy_controls_title" msgid="5322875777945432395">"تنظیمات حریم خصوصی"</string>
+ <string name="privacy_controls_title" msgid="5322875777945432395">"کنترل‌های حریم خصوصی"</string>
<string name="privacy_controls_summary" msgid="2402066941190435424">"کنترل دسترسی دستگاه به میکروفون، دوربین، و غیره"</string>
- <string name="privacy_controls_search_terms" msgid="3774472175934304165">"حریم خصوصی، تنظیمات حریم خصوصی"</string>
+ <string name="privacy_controls_search_terms" msgid="3774472175934304165">"حریم خصوصی، کنترل‌های حریم خصوصی"</string>
<string name="advanced_title" msgid="8745436380690561172">"تنظیمات بیشتر"</string>
<string name="advanced_security_title" msgid="1126833338772188155">"تنظیمات ایمنی بیشتر"</string>
<string name="advanced_security_summary" msgid="6172253327022425123">"رمزگذاری، اطلاعات اعتباری، و غیره"</string>
diff --git a/SafetyCenter/Resources/res/values-hy-v35/strings.xml b/SafetyCenter/Resources/res/values-hy-v35/strings.xml
index dd5c9e01b..d5d9f39c4 100644
--- a/SafetyCenter/Resources/res/values-hy-v35/strings.xml
+++ b/SafetyCenter/Resources/res/values-hy-v35/strings.xml
@@ -24,7 +24,7 @@
<string name="privacy_sources_title" msgid="309304028326660956">"Գաղտնիության կարգավորումներ"</string>
<string name="privacy_sources_summary" msgid="2165270848857537278">"Թույլտվություններ, կարգավորումներ"</string>
<string name="privacy_additional_title" msgid="4239060639056083649"></string>
- <string name="private_space_title" msgid="6158245041481535879">"Անձնական տարածք"</string>
+ <string name="private_space_title" msgid="6158245041481535879">"Մասնավոր տարածք"</string>
<string name="private_space_summary" msgid="529869826714610294">"Կարգավորեք մասնավոր տարածքը և ավելին"</string>
- <string name="private_space_search_terms" msgid="4820808478299116258">"Անձնական տարածք"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Մասնավոր տարածք"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-ja/strings.xml b/SafetyCenter/Resources/res/values-ja/strings.xml
index b31b1690a..6e67998f4 100644
--- a/SafetyCenter/Resources/res/values-ja/strings.xml
+++ b/SafetyCenter/Resources/res/values-ja/strings.xml
@@ -30,9 +30,9 @@
<string name="permission_usage_title" msgid="3633779688945350407">"プライバシー ダッシュボード"</string>
<string name="permission_usage_summary" msgid="5323079206029964468">"権限を最近使用したアプリが表示されます"</string>
<string name="permission_usage_search_terms" msgid="3852343592870257104">"プライバシー, プライバシー ダッシュボード"</string>
- <string name="permission_manager_title" msgid="5277347862821255015">"権限マネージャ"</string>
+ <string name="permission_manager_title" msgid="5277347862821255015">"権限マネージャー"</string>
<string name="permission_manager_summary" msgid="8099852107340970790">"アプリのデータアクセスを管理します"</string>
- <string name="permission_manager_search_terms" msgid="2895147613099694722">"権限, 権限マネージャ"</string>
+ <string name="permission_manager_search_terms" msgid="2895147613099694722">"権限, 権限マネージャー"</string>
<string name="privacy_controls_title" msgid="5322875777945432395">"プライバシー管理"</string>
<string name="privacy_controls_summary" msgid="2402066941190435424">"マイク、カメラなどへのデバイス アクセスを管理します"</string>
<string name="privacy_controls_search_terms" msgid="3774472175934304165">"プライバシー, プライバシー管理"</string>
diff --git a/SafetyCenter/Resources/res/values-ka-v35/strings.xml b/SafetyCenter/Resources/res/values-ka-v35/strings.xml
index d5a444deb..6d434bca0 100644
--- a/SafetyCenter/Resources/res/values-ka-v35/strings.xml
+++ b/SafetyCenter/Resources/res/values-ka-v35/strings.xml
@@ -24,7 +24,7 @@
<string name="privacy_sources_title" msgid="309304028326660956">"კონფიდენციალურობის მართვის პარამეტრები"</string>
<string name="privacy_sources_summary" msgid="2165270848857537278">"ნებართვები, მართვის საშუალებები"</string>
<string name="privacy_additional_title" msgid="4239060639056083649"></string>
- <string name="private_space_title" msgid="6158245041481535879">"პირადი სივრცე"</string>
+ <string name="private_space_title" msgid="6158245041481535879">"კერძო სივრცე"</string>
<string name="private_space_summary" msgid="529869826714610294">"დააყენეთ პირადი სივრცე და ა.შ."</string>
- <string name="private_space_search_terms" msgid="4820808478299116258">"პირადი სივრცე"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"კერძო სივრცე"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-kn-v35/strings.xml b/SafetyCenter/Resources/res/values-kn-v35/strings.xml
index 339e522d9..c054b2fa1 100644
--- a/SafetyCenter/Resources/res/values-kn-v35/strings.xml
+++ b/SafetyCenter/Resources/res/values-kn-v35/strings.xml
@@ -24,7 +24,7 @@
<string name="privacy_sources_title" msgid="309304028326660956">"ಗೌಪ್ಯತೆ ನಿಯಂತ್ರಣಗಳು"</string>
<string name="privacy_sources_summary" msgid="2165270848857537278">"ಅನುಮತಿಗಳು, ನಿಯಂತ್ರಣಗಳು"</string>
<string name="privacy_additional_title" msgid="4239060639056083649"></string>
- <string name="private_space_title" msgid="6158245041481535879">"ಖಾಸಗಿ ಸ್ಪೇಸ್"</string>
+ <string name="private_space_title" msgid="6158245041481535879">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string>
<string name="private_space_summary" msgid="529869826714610294">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಿ ಹಾಗೂ ಇನ್ನಷ್ಟನ್ನು ಮಾಡಿ"</string>
- <string name="private_space_search_terms" msgid="4820808478299116258">"ಖಾಸಗಿ ಸ್ಪೇಸ್"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-lv-v35/strings.xml b/SafetyCenter/Resources/res/values-lv-v35/strings.xml
index f26fcccc3..e3cf556bf 100644
--- a/SafetyCenter/Resources/res/values-lv-v35/strings.xml
+++ b/SafetyCenter/Resources/res/values-lv-v35/strings.xml
@@ -24,7 +24,7 @@
<string name="privacy_sources_title" msgid="309304028326660956">"Konfidencialitātes vadīklas"</string>
<string name="privacy_sources_summary" msgid="2165270848857537278">"Atļaujas, vadīklas"</string>
<string name="privacy_additional_title" msgid="4239060639056083649"></string>
- <string name="private_space_title" msgid="6158245041481535879">"Privātā mape"</string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privātā telpa"</string>
<string name="private_space_summary" msgid="529869826714610294">"Privātās mapes iestatīšana un citas iespējas"</string>
- <string name="private_space_search_terms" msgid="4820808478299116258">"Privātā mape"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privātā telpa"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-si-v35/strings.xml b/SafetyCenter/Resources/res/values-si-v35/strings.xml
index bcff52b2a..41025a3a4 100644
--- a/SafetyCenter/Resources/res/values-si-v35/strings.xml
+++ b/SafetyCenter/Resources/res/values-si-v35/strings.xml
@@ -24,7 +24,7 @@
<string name="privacy_sources_title" msgid="309304028326660956">"පෞද්ගලිකත්ව පාලන"</string>
<string name="privacy_sources_summary" msgid="2165270848857537278">"අවසර, පාලන"</string>
<string name="privacy_additional_title" msgid="4239060639056083649"></string>
- <string name="private_space_title" msgid="6158245041481535879">"පෞද්ගලික ඉඩ"</string>
+ <string name="private_space_title" msgid="6158245041481535879">"රහසිගත අවකාශය"</string>
<string name="private_space_summary" msgid="529869826714610294">"පෞද්ගලික ඉඩ, සහ තවත් දේ පිහිටුවන්න"</string>
- <string name="private_space_search_terms" msgid="4820808478299116258">"පෞද්ගලික ඉඩ"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"රහසිගත අවකාශය"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-sv-v35/strings.xml b/SafetyCenter/Resources/res/values-sv-v35/strings.xml
index d1f44114e..974916040 100644
--- a/SafetyCenter/Resources/res/values-sv-v35/strings.xml
+++ b/SafetyCenter/Resources/res/values-sv-v35/strings.xml
@@ -24,7 +24,7 @@
<string name="privacy_sources_title" msgid="309304028326660956">"Integritetsinställningar"</string>
<string name="privacy_sources_summary" msgid="2165270848857537278">"Behörigheter och inställningar"</string>
<string name="privacy_additional_title" msgid="4239060639056083649"></string>
- <string name="private_space_title" msgid="6158245041481535879">"Privat rum"</string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privat utrymme"</string>
<string name="private_space_summary" msgid="529869826714610294">"Ställ in privat rum med mera"</string>
- <string name="private_space_search_terms" msgid="4820808478299116258">"Privat rum"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privat utrymme"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-tr-v35/strings.xml b/SafetyCenter/Resources/res/values-tr-v35/strings.xml
index 89a3a3e0c..038bd9680 100644
--- a/SafetyCenter/Resources/res/values-tr-v35/strings.xml
+++ b/SafetyCenter/Resources/res/values-tr-v35/strings.xml
@@ -24,7 +24,7 @@
<string name="privacy_sources_title" msgid="309304028326660956">"Gizlilik denetimleri"</string>
<string name="privacy_sources_summary" msgid="2165270848857537278">"İzinler, denetimler"</string>
<string name="privacy_additional_title" msgid="4239060639056083649"></string>
- <string name="private_space_title" msgid="6158245041481535879">"Gizli Alan"</string>
+ <string name="private_space_title" msgid="6158245041481535879">"Özel Alan"</string>
<string name="private_space_summary" msgid="529869826714610294">"Gizli alan yapılandırma ve daha fazlası"</string>
- <string name="private_space_search_terms" msgid="4820808478299116258">"Gizli Alan"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Özel Alan"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-ur-v35/strings.xml b/SafetyCenter/Resources/res/values-ur-v35/strings.xml
index d4184bda3..d21e73368 100644
--- a/SafetyCenter/Resources/res/values-ur-v35/strings.xml
+++ b/SafetyCenter/Resources/res/values-ur-v35/strings.xml
@@ -24,7 +24,7 @@
<string name="privacy_sources_title" msgid="309304028326660956">"رازداری سے متعلق کنٹرولز"</string>
<string name="privacy_sources_summary" msgid="2165270848857537278">"اجازتیں، کنٹرولز"</string>
<string name="privacy_additional_title" msgid="4239060639056083649"></string>
- <string name="private_space_title" msgid="6158245041481535879">"نجی اسپیس"</string>
+ <string name="private_space_title" msgid="6158245041481535879">"پرائیویٹ اسپیس"</string>
<string name="private_space_summary" msgid="529869826714610294">"نجی اسپیس اور بھی بہت کچھ سیٹ اپ کریں"</string>
- <string name="private_space_search_terms" msgid="4820808478299116258">"نجی اسپیس"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"پرائیویٹ اسپیس"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml b/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml
index 54bbf0f1c..dc90b3fc3 100644
--- a/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="lock_screen_sources_title" msgid="5493678510117489865">"裝置解鎖"</string>
- <string name="biometrics_title_for_work" msgid="1842284049407771568">"工作應用程式的生物特徵辨識選項"</string>
+ <string name="biometrics_title_for_work" msgid="1842284049407771568">"工作應用程式的生物辨識選項"</string>
<string name="privacy_sources_summary" msgid="4083646673569677049">"權限、資訊主頁、控制選項"</string>
<string name="health_connect_title" msgid="8318152190040327804">"健康資料同步"</string>
<string name="health_connect_search_terms" msgid="4998970586245680829">"健康, 健康資料同步"</string>
diff --git a/SafetyCenter/Resources/res/values-zh-rTW/strings.xml b/SafetyCenter/Resources/res/values-zh-rTW/strings.xml
index 5c44eb33a..6e6e41a38 100644
--- a/SafetyCenter/Resources/res/values-zh-rTW/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rTW/strings.xml
@@ -23,7 +23,7 @@
<string name="lock_screen_title" msgid="4069104894527169877">"螢幕鎖定"</string>
<string name="lock_screen_summary_disabled" msgid="354071230916616692">"目前還沒有任何資訊"</string>
<string name="lock_screen_search_terms" msgid="2678486357779794826">"裝置鎖定,螢幕鎖定,將螢幕鎖定,鎖定螢幕,密碼,PIN 碼,圖案"</string>
- <string name="biometrics_title" msgid="5859504610285212938">"生物特徵辨識"</string>
+ <string name="biometrics_title" msgid="5859504610285212938">"生物辨識"</string>
<string name="biometrics_search_terms" msgid="6040319118762671981">"指紋,手指,新增指紋,人臉解鎖,人臉"</string>
<string name="privacy_sources_title" msgid="4061110826457365957">"隱私權"</string>
<string name="privacy_sources_summary" msgid="4089719981155120864">"資訊主頁、權限、控制選項"</string>
diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt
index e15887576..3222eeda8 100644
--- a/framework-s/api/system-current.txt
+++ b/framework-s/api/system-current.txt
@@ -37,6 +37,7 @@ package android.app.role {
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @Nullable @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, android.Manifest.permission.MANAGE_ROLE_HOLDERS, android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS}, conditional=true) public android.os.UserHandle getActiveUserForRole(@NonNull String);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS) public String getDefaultApplication(@NonNull String);
method @Deprecated @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
@@ -48,6 +49,7 @@ package android.app.role {
method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
+ method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, android.Manifest.permission.MANAGE_ROLE_HOLDERS, android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS}, conditional=true) public void setActiveUserForRole(@NonNull String, @NonNull android.os.UserHandle, int);
method @RequiresPermission(android.Manifest.permission.BYPASS_ROLE_QUALIFICATION) public void setBypassingRoleQualification(boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS) public void setDefaultApplication(@NonNull String, @Nullable String, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setRoleFallbackEnabled(@NonNull String, boolean);
@@ -55,6 +57,7 @@ package android.app.role {
field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
field public static final String ROLE_DEVICE_POLICY_MANAGEMENT = "android.app.role.DEVICE_POLICY_MANAGEMENT";
field public static final String ROLE_FINANCED_DEVICE_KIOSK = "android.app.role.FINANCED_DEVICE_KIOSK";
+ field @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") public static final String ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY = "android.app.role.RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY";
field public static final String ROLE_SYSTEM_ACTIVITY_RECOGNIZER = "android.app.role.SYSTEM_ACTIVITY_RECOGNIZER";
field public static final String ROLE_SYSTEM_CALL_STREAMING = "android.app.role.SYSTEM_CALL_STREAMING";
field public static final String ROLE_SYSTEM_SUPERVISION = "android.app.role.SYSTEM_SUPERVISION";
diff --git a/framework-s/java/android/app/role/IRoleManager.aidl b/framework-s/java/android/app/role/IRoleManager.aidl
index 522967630..cd0079e08 100644
--- a/framework-s/java/android/app/role/IRoleManager.aidl
+++ b/framework-s/java/android/app/role/IRoleManager.aidl
@@ -45,6 +45,10 @@ interface IRoleManager {
void setDefaultApplicationAsUser(in String roleName, in String packageName, int flags,
int userId, in RemoteCallback callback);
+ int getActiveUserForRoleAsUser(in String roleName, int userId);
+
+ void setActiveUserForRoleAsUser(in String roleName, int activeUserId, int flags, int userId);
+
void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId);
void removeOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener,
diff --git a/framework-s/java/android/app/role/RoleManager.java b/framework-s/java/android/app/role/RoleManager.java
index 4b8c9b388..6f62fdd76 100644
--- a/framework-s/java/android/app/role/RoleManager.java
+++ b/framework-s/java/android/app/role/RoleManager.java
@@ -40,6 +40,7 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.permission.flags.Flags;
+import android.permission.internal.compat.UserHandleCompat;
import android.util.ArrayMap;
import android.util.SparseArray;
@@ -211,6 +212,16 @@ public final class RoleManager {
"android.app.role.SYSTEM_CALL_STREAMING";
/**
+ * The name of the role used for testing cross-user roles.
+ *
+ * @hide
+ */
+ @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SystemApi
+ public static final String ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY =
+ "android.app.role.RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY";
+
+ /**
* @hide
*/
@IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP })
@@ -574,6 +585,87 @@ public final class RoleManager {
}
}
+ /**
+ * Get the {@link UserHandle} of the user who that is the active user for the specified role.
+ * <p>
+ * Only profile-group exclusive roles can be used with this method, and they will
+ * have one active user within a profile group.
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@code android.permission.INTERACT_ACROSS_USERS_FULL} and one of
+ * {@code android.permission.MANAGE_ROLE_HOLDERS} or
+ * {@code android.permission.MANAGE_DEFAULT_APPLICATIONS}.
+ *
+ * @param roleName the name of the role to get the active user for
+ *
+ * @return a {@link UserHandle} of the active user for the specified role
+ *
+ * @see #setActiveUserForRole(String, UserHandle, int)
+ *
+ * @hide
+ */
+ @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Manifest.permission.MANAGE_ROLE_HOLDERS,
+ Manifest.permission.MANAGE_DEFAULT_APPLICATIONS},
+ conditional = true)
+ @RequiresApi(Build.VERSION_CODES.BAKLAVA)
+ @SystemApi
+ @UserHandleAware
+ @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Nullable
+ public UserHandle getActiveUserForRole(@NonNull String roleName) {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ try {
+ int userId = mService.getActiveUserForRoleAsUser(roleName,
+ mContext.getUser().getIdentifier());
+ return userId == UserHandleCompat.USER_NULL ? null : UserHandle.of(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set a specific user as active user for a role.
+ * <p>
+ * Only profile-group exclusive roles can be used with this method, and they will have
+ * one active user within a profile group.
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@code android.permission.INTERACT_ACROSS_USERS_FULL} and one of
+ * {@code android.permission.MANAGE_ROLE_HOLDERS} or
+ * {@code android.permission.MANAGE_DEFAULT_APPLICATIONS}.
+ *
+ * @param roleName the name of the role to set the active user for
+ * @param user the user to set as active user for specified role
+ * @param flags optional behavior flags
+ *
+ * @see #getActiveUserForRole(String)
+ *
+ * @hide
+ */
+ @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Manifest.permission.MANAGE_ROLE_HOLDERS,
+ Manifest.permission.MANAGE_DEFAULT_APPLICATIONS},
+ conditional = true)
+ @RequiresApi(Build.VERSION_CODES.BAKLAVA)
+ @SystemApi
+ @UserHandleAware
+ @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ // The user handle parameter is a value to be set by this method, while the context user of the
+ // operation is indeed read from the context
+ @SuppressLint("UserHandle")
+ public void setActiveUserForRole(
+ @NonNull String roleName, @NonNull UserHandle user, @ManageHoldersFlags int flags) {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Objects.requireNonNull(user, "user cannot be null");
+ try {
+ mService.setActiveUserForRoleAsUser(roleName, user.getIdentifier(), flags,
+ mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@NonNull
private static RemoteCallback createRemoteCallback(@NonNull Executor executor,
@NonNull Consumer<Boolean> callback) {
diff --git a/service/java/com/android/permission/compat/UserHandleCompat.java b/framework-s/java/android/permission/internal/compat/UserHandleCompat.java
index 1901aa997..8a3ec444d 100644
--- a/service/java/com/android/permission/compat/UserHandleCompat.java
+++ b/framework-s/java/android/permission/internal/compat/UserHandleCompat.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permission.compat;
+package android.permission.internal.compat;
import android.annotation.UserIdInt;
import android.os.UserHandle;
@@ -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/permission/compat/package-info.java b/framework-s/java/android/permission/internal/compat/package-info.java
index c89cc8eab..b78aac878 100644
--- a/service/java/com/android/permission/compat/package-info.java
+++ b/framework-s/java/android/permission/internal/compat/package-info.java
@@ -19,4 +19,4 @@
* TODO(b/146466118) remove this javadoc tag
*/
@android.annotation.Hide
-package com.android.permission.compat;
+package android.permission.internal.compat;
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..ef6971b11 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
@@ -10,6 +10,10 @@ rule android.companion.virtualdevice.flags.*FeatureFlags* com.android.permission
rule android.companion.virtualdevice.flags.FeatureFlags* com.android.permission.jarjar.@0
rule android.companion.virtualdevice.flags.FeatureFlags com.android.permission.jarjar.@0
rule android.companion.virtualdevice.flags.Flags com.android.permission.jarjar.@0
+rule android.content.pm.*FeatureFlags* com.android.permission.jarjar.@0
+rule android.content.pm.FeatureFlags* com.android.permission.jarjar.@0
+rule android.content.pm.FeatureFlags com.android.permission.jarjar.@0
+rule android.content.pm.Flags com.android.permission.jarjar.@0
rule android.os.*FeatureFlags* com.android.permission.jarjar.@0
rule android.os.FeatureFlags* com.android.permission.jarjar.@0
rule android.os.FeatureFlags com.android.permission.jarjar.@0
@@ -37,4 +41,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/ecm/EnhancedConfirmationService.java b/service/java/com/android/ecm/EnhancedConfirmationService.java
index 708884e85..73f66609e 100644
--- a/service/java/com/android/ecm/EnhancedConfirmationService.java
+++ b/service/java/com/android/ecm/EnhancedConfirmationService.java
@@ -228,7 +228,8 @@ public class EnhancedConfirmationService extends SystemService {
}
private void enforcePermissions(@NonNull String methodName, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false, methodName, mContext);
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, methodName, mContext);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES, methodName);
}
diff --git a/service/java/com/android/permission/util/UserUtils.java b/service/java/com/android/permission/util/UserUtils.java
index 639c7aacb..c69afb199 100644
--- a/service/java/com/android/permission/util/UserUtils.java
+++ b/service/java/com/android/permission/util/UserUtils.java
@@ -17,21 +17,22 @@
package com.android.permission.util;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.internal.compat.UserHandleCompat;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.build.SdkLevel;
-import com.android.permission.compat.UserHandleCompat;
import com.android.permission.flags.Flags;
+import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
/** Utility class to deal with Android users. */
public final class UserUtils {
@@ -42,11 +43,12 @@ public final class UserUtils {
public static void enforceCrossUserPermission(
@UserIdInt int userId,
boolean allowAll,
+ boolean enforceForProfileGroup,
@NonNull String message,
@NonNull Context context) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandleCompat.getUserId(callingUid);
- if (userId == callingUserId) {
+ if (userId == callingUserId && !enforceForProfileGroup) {
return;
}
Preconditions.checkArgument(
@@ -55,13 +57,40 @@ public final class UserUtils {
"Invalid user " + userId);
context.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- if (callingUid != Process.SHELL_UID || userId < UserHandleCompat.USER_SYSTEM) {
+ if (callingUid != Process.SHELL_UID || userId == UserHandleCompat.USER_ALL) {
return;
}
+
+ if (enforceForProfileGroup) {
+ DevicePolicyManager devicePolicyManager =
+ context.getSystemService(DevicePolicyManager.class);
+ if (!devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
+ // For profileGroup exclusive roles users on BYOD are free to choose personal o
+ // work profile app regardless of DISALLOW_DEBUGGING_FEATURES
+ return;
+ }
+
+ Context userContext = UserUtils.getUserContext(userId, context);
+ List<UserHandle> profiles = getUserProfiles(userContext, true);
+ final int profilesSize = profiles.size();
+ for (int i = 0; i < profilesSize; i++) {
+ int profileId = profiles.get(i).getIdentifier();
+ if (profileId == callingUserId) {
+ continue;
+ }
+ enforceShellRestriction(profileId, context);
+ }
+ } else {
+ enforceShellRestriction(userId, context);
+ }
+ }
+
+ private static void enforceShellRestriction(int userId, @NonNull Context context) {
UserManager userManager = context.getSystemService(UserManager.class);
if (userManager.hasUserRestrictionForUser(
UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(userId))) {
- throw new SecurityException("Shell does not have permission to access user " + userId);
+ throw new SecurityException(
+ "Shell does not have permission to access user " + userId);
}
}
@@ -86,18 +115,54 @@ public final class UserUtils {
/** Returns all the enabled user profiles on the device. */
@NonNull
public static List<UserHandle> getUserProfiles(@NonNull Context context) {
+ return getUserProfiles(context, false);
+ }
+
+ /**
+ * Returns all the enabled user profiles on the device
+ *
+ * @param context the {@link Context}
+ * @param excludePrivate {@code true} to exclude private profiles from returned list of users
+ */
+ @NonNull
+ public static List<UserHandle> getUserProfiles(@NonNull Context context,
+ boolean excludePrivate) {
UserManager userManager = context.getSystemService(UserManager.class);
// This call requires the QUERY_USERS permission.
final long identity = Binder.clearCallingIdentity();
try {
- return userManager.getUserProfiles();
+ List<UserHandle> profiles = userManager.getUserProfiles();
+ if (!excludePrivate) {
+ return profiles;
+ }
+ List<UserHandle> filteredProfiles = new ArrayList<>();
+ final int profilesSize = profiles.size();
+ for (int i = 0; i < profilesSize; i++) {
+ UserHandle user = profiles.get(i);
+ if (!isPrivateProfile(user.getIdentifier(), context)) {
+ filteredProfiles.add(user);
+ }
+ }
+ return filteredProfiles;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
+ /**
+ * Returns the parent of a given user, or userId if it has no parent (e.g. it is the primary
+ * profile)
+ */
+ @UserIdInt
+ public static int getProfileParentIdOrSelf(@UserIdInt int userId, @NonNull Context context) {
+ UserHandle profileParent = getProfileParent(userId, context);
+ // If profile parent userhandle is null, then original user id is the parent
+ return profileParent != null ? profileParent.getIdentifier() : userId;
+ }
+
/** Returns the parent of a given user. */
- public static UserHandle getProfileParent(@UserIdInt int userId, @NonNull Context context) {
+ @Nullable
+ private static UserHandle getProfileParent(@UserIdInt int userId, @NonNull Context context) {
Context userContext = getUserContext(userId, context);
UserManager userManager = userContext.getSystemService(UserManager.class);
// This call requires the INTERACT_ACROSS_USERS permission.
diff --git a/service/java/com/android/role/RoleService.java b/service/java/com/android/role/RoleService.java
index 20250b4f6..1145f273d 100644
--- a/service/java/com/android/role/RoleService.java
+++ b/service/java/com/android/role/RoleService.java
@@ -18,6 +18,7 @@ package com.android.role;
import android.Manifest;
import android.annotation.AnyThread;
+import android.annotation.ChecksSdkIntAtLeast;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -46,6 +47,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.permission.flags.Flags;
+import android.permission.internal.compat.UserHandleCompat;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -62,13 +64,14 @@ import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.modules.utils.build.SdkLevel;
-import com.android.permission.compat.UserHandleCompat;
import com.android.permission.util.ArrayUtils;
import com.android.permission.util.CollectionUtils;
import com.android.permission.util.ForegroundThread;
import com.android.permission.util.PackageUtils;
import com.android.permission.util.ThrottledRunnable;
import com.android.permission.util.UserUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
import com.android.server.role.RoleServicePlatformHelper;
@@ -77,6 +80,7 @@ import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -176,6 +180,7 @@ public class RoleService extends SystemService implements RoleUserState.Callback
registerUserRemovedReceiver();
}
+ // TODO(b/375029649): enforce single active user for all cross-user roles
private void registerUserRemovedReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -191,6 +196,7 @@ public class RoleService extends SystemService implements RoleUserState.Callback
}, intentFilter, null, null);
}
+ // TODO(b/375029649): enforce single active user for all cross-user roles
@Override
public void onStart() {
publishBinderService(Context.ROLE_SERVICE, new Stub());
@@ -464,8 +470,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public boolean isRoleAvailableAsUser(@NonNull String roleName, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false, "isRoleAvailableAsUser",
- getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "isRoleAvailableAsUser", getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return false;
@@ -481,7 +487,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@UserIdInt int userId) {
mAppOpsManager.checkPackage(getCallingUid(), packageName);
- UserUtils.enforceCrossUserPermission(userId, false, "isRoleHeldAsUser", getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "isRoleHeldAsUser", getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return false;
@@ -500,8 +507,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@NonNull
@Override
public List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false, "getRoleHoldersAsUser",
- getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "getRoleHoldersAsUser", getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return Collections.emptyList();
@@ -523,8 +530,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
@NonNull RemoteCallback callback) {
- UserUtils.enforceCrossUserPermission(userId, false, "addRoleHolderAsUser",
- getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "addRoleHolderAsUser", getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return;
@@ -544,8 +551,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
@NonNull RemoteCallback callback) {
- UserUtils.enforceCrossUserPermission(userId, false, "removeRoleHolderAsUser",
- getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "removeRoleHolderAsUser", getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return;
@@ -566,8 +573,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
public void clearRoleHoldersAsUser(@NonNull String roleName,
@RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
@NonNull RemoteCallback callback) {
- UserUtils.enforceCrossUserPermission(userId, false, "clearRoleHoldersAsUser",
- getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "clearRoleHoldersAsUser", getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return;
@@ -585,7 +592,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
@Nullable
public String getDefaultApplicationAsUser(@NonNull String roleName, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false, "getDefaultApplicationAsUser",
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "getDefaultApplicationAsUser",
getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
@@ -610,7 +618,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
public void setDefaultApplicationAsUser(@NonNull String roleName,
@Nullable String packageName, @RoleManager.ManageHoldersFlags int flags,
@UserIdInt int userId, @NonNull RemoteCallback callback) {
- UserUtils.enforceCrossUserPermission(userId, false, "setDefaultApplicationAsUser",
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "setDefaultApplicationAsUser",
getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
@@ -633,10 +642,101 @@ public class RoleService extends SystemService implements RoleUserState.Callback
}
@Override
+ public int getActiveUserForRoleAsUser(@NonNull String roleName, @UserIdInt int userId) {
+ Preconditions.checkState(isProfileGroupExclusivityAvailable(),
+ "getActiveUserForRoleAsUser not available");
+ enforceProfileGroupExclusiveRole(roleName);
+
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ true, "getActiveUserForRole", getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return UserHandleCompat.USER_NULL;
+ }
+
+ enforceCallingOrSelfAnyPermissions(new String[] {
+ Manifest.permission.MANAGE_DEFAULT_APPLICATIONS,
+ Manifest.permission.MANAGE_ROLE_HOLDERS
+ }, "getActiveUserForRole");
+
+ int profileParentId = UserUtils.getProfileParentIdOrSelf(userId, getContext());
+ RoleUserState userState = getOrCreateUserState(profileParentId);
+ return userState.getActiveUserForRole(roleName);
+ }
+
+ @Override
+ public void setActiveUserForRoleAsUser(@NonNull String roleName,
+ @UserIdInt int activeUserId, @RoleManager.ManageHoldersFlags int flags,
+ @UserIdInt int userId) {
+ Preconditions.checkState(isProfileGroupExclusivityAvailable(),
+ "setActiveUserForRoleAsUser not available");
+ enforceProfileGroupExclusiveRole(roleName);
+
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ true, "setActiveUserForRole", getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+ if (!UserUtils.isUserExistent(activeUserId, getContext())) {
+ Log.e(LOG_TAG, "user " + activeUserId + " does not exist");
+ return;
+ }
+ if (UserUtils.isPrivateProfile(activeUserId, getContext())) {
+ Log.e(LOG_TAG, "Cannot set private profile " + activeUserId + " as active user"
+ + " for role");
+ return;
+ }
+ Context userContext = UserUtils.getUserContext(userId, getContext());
+ List<UserHandle> profiles = UserUtils.getUserProfiles(userContext, true);
+ if (!profiles.contains(UserHandle.of(activeUserId))) {
+ Log.e(LOG_TAG, "User " + activeUserId + " is not in the same profile-group as "
+ + userId);
+ return;
+ }
+
+ enforceCallingOrSelfAnyPermissions(new String[] {
+ Manifest.permission.MANAGE_DEFAULT_APPLICATIONS,
+ Manifest.permission.MANAGE_ROLE_HOLDERS
+ }, "setDefaultApplicationAsUser");
+
+ int profileParentId = UserUtils.getProfileParentIdOrSelf(userId, getContext());
+ RoleUserState userState = getOrCreateUserState(profileParentId);
+
+ if (!userState.setActiveUserForRole(roleName, activeUserId)) {
+ Log.i(LOG_TAG, "User " + activeUserId + " is already the active user for role");
+ return;
+ }
+
+ final int profilesSize = profiles.size();
+ for (int i = 0; i < profilesSize; i++) {
+ final AndroidFuture<Void> future = new AndroidFuture<>();
+ final RemoteCallback callback = new RemoteCallback(result -> {
+ boolean successful = result != null;
+ if (successful) {
+ future.complete(null);
+ } else {
+ future.completeExceptionally(new RuntimeException());
+ }
+ });
+ int profilesUserId = profiles.get(i).getIdentifier();
+ getOrCreateController(profilesUserId)
+ .onClearRoleHolders(roleName, flags, callback);
+ try {
+ future.get(5, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Log.e(LOG_TAG, "Exception while clearing role holders for non-active"
+ + "user: " + profilesUserId, e);
+ }
+ }
+ }
+
+ @Override
public void addOnRoleHoldersChangedListenerAsUser(
@NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, true,
- "addOnRoleHoldersChangedListenerAsUser", getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ true,
+ /* enforceForProfileGroup= */ false, "addOnRoleHoldersChangedListenerAsUser",
+ getContext());
if (userId != UserHandleCompat.USER_ALL && !UserUtils.isUserExistent(userId,
getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
@@ -656,7 +756,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public void removeOnRoleHoldersChangedListenerAsUser(
@NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, true,
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ true,
+ /* enforceForProfileGroup= */ false,
"removeOnRoleHoldersChangedListenerAsUser", getContext());
if (userId != UserHandleCompat.USER_ALL && !UserUtils.isUserExistent(userId,
getContext())) {
@@ -709,7 +810,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public boolean isRoleFallbackEnabledAsUser(@NonNull String roleName,
@UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false, "isRoleFallbackEnabledAsUser",
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "isRoleFallbackEnabledAsUser",
getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
@@ -727,7 +829,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public void setRoleFallbackEnabledAsUser(@NonNull String roleName, boolean fallbackEnabled,
@UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false, "setRoleFallbackEnabledAsUser",
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "setRoleFallbackEnabledAsUser",
getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
@@ -745,7 +848,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public void setRoleNamesFromControllerAsUser(@NonNull List<String> roleNames,
@UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false, "setRoleNamesFromControllerAsUser",
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "setRoleNamesFromControllerAsUser",
getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
@@ -764,8 +868,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public boolean addRoleHolderFromControllerAsUser(@NonNull String roleName,
@NonNull String packageName, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false,
- "addRoleHolderFromControllerAsUser", getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "addRoleHolderFromControllerAsUser",
+ getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return false;
@@ -784,8 +889,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public boolean removeRoleHolderFromControllerAsUser(@NonNull String roleName,
@NonNull String packageName, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false,
- "removeRoleHolderFromControllerAsUser", getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "removeRoleHolderFromControllerAsUser",
+ getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return false;
@@ -804,8 +910,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public List<String> getHeldRolesFromControllerAsUser(@NonNull String packageName,
@UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false,
- "getHeldRolesFromControllerAsUser", getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "getHeldRolesFromControllerAsUser",
+ getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return Collections.emptyList();
@@ -914,7 +1021,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public String getSmsRoleHolder(int userId) {
final Context context = getContext();
- UserUtils.enforceCrossUserPermission(userId, false, "getSmsRoleHolder", context);
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "getSmsRoleHolder", context);
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return null;
@@ -938,7 +1046,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public String getEmergencyRoleHolder(int userId) {
final Context context = getContext();
- UserUtils.enforceCrossUserPermission(userId, false, "getEmergencyRoleHolder", context);
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "getEmergencyRoleHolder", context);
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return null;
@@ -964,8 +1073,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public boolean isRoleVisibleAsUser(@NonNull String roleName, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false, "isRoleVisibleAsUser",
- getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "isRoleVisibleAsUser", getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return false;
@@ -982,8 +1091,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public boolean isApplicationVisibleForRoleAsUser(@NonNull String roleName,
@NonNull String packageName, @UserIdInt int userId) {
- UserUtils.enforceCrossUserPermission(userId, false,
- "isApplicationVisibleForRoleAsUser", getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ false, "isApplicationVisibleForRoleAsUser",
+ getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return false;
@@ -1040,6 +1150,36 @@ public class RoleService extends SystemService implements RoleUserState.Callback
return true;
}
}
+
+ private void enforceCallingOrSelfAnyPermissions(@NonNull String[] permissions,
+ @NonNull String message) {
+ for (String permission : permissions) {
+ if (getContext().checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+
+ throw new SecurityException(message + ": Neither user " + Binder.getCallingUid()
+ + " nor current process has at least one of" + Arrays.toString(permissions)
+ + ".");
+ }
+
+ private void enforceProfileGroupExclusiveRole(@NonNull String roleName) {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Role role = Roles.get(getContext()).get(roleName);
+ Objects.requireNonNull(role, "Unknown role: " + roleName);
+ Preconditions.checkArgument(
+ role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP,
+ roleName + " is not a profile-group exclusive role");
+ }
+
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private boolean isProfileGroupExclusivityAvailable() {
+ // TODO(b/372743073): change to isAtLeastB once available
+ return SdkLevel.isAtLeastV()
+ && com.android.permission.flags.Flags.crossUserRoleEnabled();
+ }
}
private class Local implements RoleManagerLocal {
diff --git a/service/java/com/android/role/RoleShellCommand.java b/service/java/com/android/role/RoleShellCommand.java
index 808a64cb4..538e62f47 100644
--- a/service/java/com/android/role/RoleShellCommand.java
+++ b/service/java/com/android/role/RoleShellCommand.java
@@ -22,11 +22,11 @@ import android.app.role.IRoleManager;
import android.os.Build;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.permission.internal.compat.UserHandleCompat;
import androidx.annotation.RequiresApi;
import com.android.modules.utils.BasicShellCommandHandler;
-import com.android.permission.compat.UserHandleCompat;
import java.io.PrintWriter;
import java.util.List;
@@ -87,6 +87,10 @@ class RoleShellCommand extends BasicShellCommandHandler {
return runClearRoleHolders();
case "set-bypassing-role-qualification":
return runSetBypassingRoleQualification();
+ case "get-active-user-for-role":
+ return runGetActiveUserForRole();
+ case "set-active-user-for-role":
+ return runSetActiveUserForRole();
default:
return handleDefaultCommands(cmd);
}
@@ -162,6 +166,27 @@ class RoleShellCommand extends BasicShellCommandHandler {
return 0;
}
+ private int runGetActiveUserForRole() throws RemoteException {
+ int userId = getUserIdMaybe();
+ String roleName = getNextArgRequired();
+
+ int activeUserId = mRoleManager.getActiveUserForRoleAsUser(roleName, userId);
+ if (activeUserId != UserHandleCompat.USER_NULL) {
+ getOutPrintWriter().println(activeUserId);
+ }
+ return 0;
+ }
+
+ private int runSetActiveUserForRole() throws RemoteException {
+ int userId = getUserIdMaybe();
+ String roleName = getNextArgRequired();
+ int activeUserId = Integer.parseInt(getNextArgRequired());
+ int flags = getFlagsMaybe();
+
+ mRoleManager.setActiveUserForRoleAsUser(roleName, activeUserId, flags, userId);
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -174,6 +199,8 @@ class RoleShellCommand extends BasicShellCommandHandler {
pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]");
pw.println(" clear-role-holders [--user USER_ID] ROLE [FLAGS]");
pw.println(" set-bypassing-role-qualification true|false");
+ pw.println(" get-active-user-for-role [--user USER_ID] ROLE");
+ pw.println(" set-active-user-for-role [--user USER_ID] ROLE ACTIVE_USER_ID [FLAGS]");
pw.println();
}
}
diff --git a/service/java/com/android/role/RoleUserState.java b/service/java/com/android/role/RoleUserState.java
index 81007d65e..c94b58826 100644
--- a/service/java/com/android/role/RoleUserState.java
+++ b/service/java/com/android/role/RoleUserState.java
@@ -24,6 +24,7 @@ import android.annotation.WorkerThread;
import android.os.Build;
import android.os.Handler;
import android.os.UserHandle;
+import android.permission.internal.compat.UserHandleCompat;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -39,6 +40,7 @@ import com.android.role.persistence.RolesState;
import com.android.server.role.RoleServicePlatformHelper;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -96,6 +98,10 @@ class RoleUserState {
private ArraySet<String> mFallbackEnabledRoles = new ArraySet<>();
@GuardedBy("mLock")
+ @NonNull
+ private ArrayMap<String, Integer> mActiveUserIds = new ArrayMap<>();
+
+ @GuardedBy("mLock")
private boolean mWriteScheduled;
@GuardedBy("mLock")
@@ -423,6 +429,42 @@ class RoleUserState {
}
/**
+ * Return the active user for the role
+ *
+ * @param roleName the name of the role to get the active user for
+ */
+ public int getActiveUserForRole(@NonNull String roleName) {
+ synchronized (mLock) {
+ return mActiveUserIds.getOrDefault(roleName, UserHandleCompat.USER_NULL);
+ }
+ }
+
+ /**
+ * Set the active user for the role
+ *
+ * @param roleName the name of the role to set the active user for
+ * @param userId User id to set as active for this role
+ * @return whether any changes were made
+ */
+ public boolean setActiveUserForRole(@NonNull String roleName, @UserIdInt int userId) {
+ if (!com.android.permission.flags.Flags.crossUserRoleEnabled()) {
+ return false;
+ }
+ synchronized (mLock) {
+ Integer currentActiveUserId = mActiveUserIds.get(roleName);
+ // If we have pre-existing roles that weren't profile group exclusive and don't have an
+ // active user, ensure we set and write value, and return modified, otherwise other
+ // users might not have role holder revoked.
+ if (currentActiveUserId != null && currentActiveUserId == userId) {
+ return false;
+ }
+ mActiveUserIds.put(roleName, userId);
+ scheduleWriteFileLocked();
+ return true;
+ }
+ }
+
+ /**
* Schedule writing the state to file.
*/
@GuardedBy("mLock")
@@ -449,9 +491,15 @@ class RoleUserState {
// Force a reconciliation on next boot if we are bypassing role qualification now.
String packagesHash = mBypassingRoleQualification ? null : mPackagesHash;
- roles = new RolesState(mVersion, packagesHash,
- (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked(),
- snapshotFallbackEnabledRoles());
+ if (com.android.permission.flags.Flags.crossUserRoleEnabled()) {
+ roles = new RolesState(mVersion, packagesHash,
+ (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked(),
+ snapshotFallbackEnabledRoles(), snapshotActiveUserIds());
+ } else {
+ roles = new RolesState(mVersion, packagesHash,
+ (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked(),
+ snapshotFallbackEnabledRoles());
+ }
}
mPersistence.writeForUser(roles, UserHandle.of(mUserId));
@@ -463,14 +511,17 @@ class RoleUserState {
Map<String, Set<String>> roles;
Set<String> fallbackEnabledRoles;
+ Map<String, Integer> activeUserIds;
if (roleState != null) {
mVersion = roleState.getVersion();
mPackagesHash = roleState.getPackagesHash();
roles = roleState.getRoles();
fallbackEnabledRoles = roleState.getFallbackEnabledRoles();
+ activeUserIds = roleState.getActiveUserIds();
} else {
roles = mPlatformHelper.getLegacyRoleState(mUserId);
fallbackEnabledRoles = roles.keySet();
+ activeUserIds = Collections.emptyMap();
}
mRoles.clear();
for (Map.Entry<String, Set<String>> entry : roles.entrySet()) {
@@ -480,6 +531,10 @@ class RoleUserState {
}
mFallbackEnabledRoles.clear();
mFallbackEnabledRoles.addAll(fallbackEnabledRoles);
+ mActiveUserIds.clear();
+ if (com.android.permission.flags.Flags.crossUserRoleEnabled()) {
+ mActiveUserIds.putAll(activeUserIds);
+ }
if (roleState == null) {
scheduleWriteFileLocked();
}
@@ -496,12 +551,14 @@ class RoleUserState {
int version;
String packagesHash;
ArrayMap<String, ArraySet<String>> roles;
+ ArrayMap<String, Integer> activeUserIds;
ArraySet<String> fallbackEnabledRoles;
synchronized (mLock) {
version = mVersion;
packagesHash = mPackagesHash;
roles = snapshotRolesLocked();
fallbackEnabledRoles = snapshotFallbackEnabledRoles();
+ activeUserIds = snapshotActiveUserIds();
}
long fieldToken = dumpOutputStream.start(fieldName, fieldId);
@@ -514,10 +571,14 @@ class RoleUserState {
String roleName = roles.keyAt(rolesIndex);
ArraySet<String> roleHolders = roles.valueAt(rolesIndex);
boolean fallbackEnabled = fallbackEnabledRoles.contains(roleName);
+ Integer activeUserId = activeUserIds.get(roleName);
long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES);
dumpOutputStream.write("name", RoleProto.NAME, roleName);
dumpOutputStream.write("fallback_enabled", RoleProto.FALLBACK_ENABLED, fallbackEnabled);
+ if (activeUserId != null) {
+ dumpOutputStream.write("active_user_id", RoleProto.ACTIVE_USER_ID, activeUserId);
+ }
int roleHoldersSize = roleHolders.size();
for (int roleHoldersIndex = 0; roleHoldersIndex < roleHoldersSize; roleHoldersIndex++) {
String roleHolder = roleHolders.valueAt(roleHoldersIndex);
@@ -563,6 +624,12 @@ class RoleUserState {
return new ArraySet<>(mFallbackEnabledRoles);
}
+ @GuardedBy("mLock")
+ @NonNull
+ private ArrayMap<String, Integer> snapshotActiveUserIds() {
+ return new ArrayMap<>(mActiveUserIds);
+ }
+
/**
* Destroy this user state and delete the corresponding file. Any pending writes to the file
* will be cancelled, and any future interaction with this state will throw an exception.
diff --git a/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/service/java/com/android/role/persistence/RolesPersistenceImpl.java
index 220a8440b..8382d3632 100644
--- a/service/java/com/android/role/persistence/RolesPersistenceImpl.java
+++ b/service/java/com/android/role/persistence/RolesPersistenceImpl.java
@@ -67,6 +67,7 @@ public class RolesPersistenceImpl implements RolesPersistence {
private static final String ATTRIBUTE_VERSION = "version";
private static final String ATTRIBUTE_NAME = "name";
private static final String ATTRIBUTE_FALLBACK_ENABLED = "fallbackEnabled";
+ private static final String ATTRIBUTE_ACTIVE_USER_ID = "activeUserId";
private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
@VisibleForTesting
@@ -144,6 +145,7 @@ public class RolesPersistenceImpl implements RolesPersistence {
Map<String, Set<String>> roles = new ArrayMap<>();
Set<String> fallbackEnabledRoles = new ArraySet<>();
+ Map<String, Integer> activeUserIds = new ArrayMap<>();
int type;
int depth;
int innerDepth = parser.getDepth() + 1;
@@ -159,12 +161,23 @@ public class RolesPersistenceImpl implements RolesPersistence {
if (Boolean.parseBoolean(fallbackEnabled)) {
fallbackEnabledRoles.add(roleName);
}
+ if (com.android.permission.flags.Flags.crossUserRoleEnabled()) {
+ String activeUserId = parser.getAttributeValue(null, ATTRIBUTE_ACTIVE_USER_ID);
+ if (activeUserId != null) {
+ activeUserIds.put(roleName, Integer.parseInt(activeUserId));
+ }
+ }
Set<String> roleHolders = parseRoleHolders(parser);
roles.put(roleName, roleHolders);
}
}
- return new RolesState(version, packagesHash, roles, fallbackEnabledRoles);
+ if (com.android.permission.flags.Flags.crossUserRoleEnabled()) {
+ return new RolesState(version, packagesHash, roles, fallbackEnabledRoles,
+ activeUserIds);
+ } else {
+ return new RolesState(version, packagesHash, roles, fallbackEnabledRoles);
+ }
}
@NonNull
@@ -244,15 +257,22 @@ public class RolesPersistenceImpl implements RolesPersistence {
}
Set<String> fallbackEnabledRoles = roles.getFallbackEnabledRoles();
+ Map<String, Integer> activeUserIds = roles.getActiveUserIds();
for (Map.Entry<String, Set<String>> entry : roles.getRoles().entrySet()) {
String roleName = entry.getKey();
Set<String> roleHolders = entry.getValue();
boolean isFallbackEnabled = fallbackEnabledRoles.contains(roleName);
+ Integer activeUserId = com.android.permission.flags.Flags.crossUserRoleEnabled()
+ ? activeUserIds.get(roleName) : null;
serializer.startTag(null, TAG_ROLE);
serializer.attribute(null, ATTRIBUTE_NAME, roleName);
serializer.attribute(null, ATTRIBUTE_FALLBACK_ENABLED,
Boolean.toString(isFallbackEnabled));
+ if (activeUserId != null) {
+ serializer.attribute(
+ null, ATTRIBUTE_ACTIVE_USER_ID, Integer.toString(activeUserId));
+ }
serializeRoleHolders(serializer, roleHolders);
serializer.endTag(null, TAG_ROLE);
}
diff --git a/service/java/com/android/role/persistence/RolesState.java b/service/java/com/android/role/persistence/RolesState.java
index a189dd4c2..f1b3d8dfa 100644
--- a/service/java/com/android/role/persistence/RolesState.java
+++ b/service/java/com/android/role/persistence/RolesState.java
@@ -23,12 +23,13 @@ import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
import android.permission.flags.Flags;
+import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
- * State of all roles.
+ * State of all roles for a user.
*
* TODO(b/147914847): Remove @hide when it becomes the default.
* @hide
@@ -59,6 +60,12 @@ public final class RolesState {
private final Set<String> mFallbackEnabledRoles;
/**
+ * The active users for cross user roles.
+ */
+ @NonNull
+ private final Map<String, Integer> mActiveUserIds;
+
+ /**
* Create a new instance of this class.
*
* @param version the version of the roles
@@ -81,10 +88,27 @@ public final class RolesState {
@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED)
public RolesState(int version, @Nullable String packagesHash,
@NonNull Map<String, Set<String>> roles, @NonNull Set<String> fallbackEnabledRoles) {
+ this(version, packagesHash, roles, fallbackEnabledRoles, Collections.emptyMap());
+ }
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param version the version of the roles
+ * @param packagesHash the hash of all packages in the system
+ * @param roles the roles
+ * @param fallbackEnabledRoles the roles with fallback enabled
+ * @param activeUserIds the active users for cross user roles
+ * @hide
+ */
+ public RolesState(int version, @Nullable String packagesHash,
+ @NonNull Map<String, Set<String>> roles, @NonNull Set<String> fallbackEnabledRoles,
+ @NonNull Map<String, Integer> activeUserIds) {
mVersion = version;
mPackagesHash = packagesHash;
mRoles = roles;
mFallbackEnabledRoles = fallbackEnabledRoles;
+ mActiveUserIds = activeUserIds;
}
/**
@@ -127,6 +151,17 @@ public final class RolesState {
return mFallbackEnabledRoles;
}
+ /**
+ * Get the active users for cross user roles.
+ *
+ * @return active users for cross user roles
+ * @hide
+ */
+ @NonNull
+ public Map<String, Integer> getActiveUserIds() {
+ return mActiveUserIds;
+ }
+
@Override
public boolean equals(Object object) {
if (this == object) {
@@ -139,11 +174,12 @@ public final class RolesState {
return mVersion == that.mVersion
&& Objects.equals(mPackagesHash, that.mPackagesHash)
&& Objects.equals(mRoles, that.mRoles)
- && Objects.equals(mFallbackEnabledRoles, that.mFallbackEnabledRoles);
+ && Objects.equals(mFallbackEnabledRoles, that.mFallbackEnabledRoles)
+ && Objects.equals(mActiveUserIds, that.mActiveUserIds);
}
@Override
public int hashCode() {
- return Objects.hash(mVersion, mPackagesHash, mRoles, mFallbackEnabledRoles);
+ return Objects.hash(mVersion, mPackagesHash, mRoles, mFallbackEnabledRoles, mActiveUserIds);
}
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterService.java b/service/java/com/android/safetycenter/SafetyCenterService.java
index 250be5f25..6df4184e1 100644
--- a/service/java/com/android/safetycenter/SafetyCenterService.java
+++ b/service/java/com/android/safetycenter/SafetyCenterService.java
@@ -694,7 +694,8 @@ public final class SafetyCenterService extends SystemService {
/** Enforces cross user permission and returns whether the user is valid. */
private boolean enforceCrossUserPermission(String message, @UserIdInt int userId) {
UserUtils.enforceCrossUserPermission(
- userId, /* allowAll= */ false, message, getContext());
+ userId, /* allowAll= */ false, /* enforceForProfileGroup= */ false, message,
+ getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.w(
TAG,
diff --git a/service/java/com/android/safetycenter/UserProfileGroup.java b/service/java/com/android/safetycenter/UserProfileGroup.java
index 3202c3776..1f5258437 100644
--- a/service/java/com/android/safetycenter/UserProfileGroup.java
+++ b/service/java/com/android/safetycenter/UserProfileGroup.java
@@ -21,15 +21,12 @@ import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.UserIdInt;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Binder;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.internal.compat.UserHandleCompat;
import android.util.Log;
-import androidx.annotation.Nullable;
-
import com.android.permission.util.UserUtils;
import java.lang.annotation.Retention;
@@ -49,8 +46,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;
@@ -136,18 +131,14 @@ public final class UserProfileGroup {
public static UserProfileGroup fromUser(Context context, @UserIdInt int userId) {
Context userContext = UserUtils.getUserContext(userId, context);
List<UserHandle> userProfiles = UserUtils.getUserProfiles(userContext);
- UserHandle profileParent = UserUtils.getProfileParent(userId, userContext);
- int profileParentUserId = userId;
- if (profileParent != null) {
- profileParentUserId = profileParent.getIdentifier();
- }
+ int profileParentUserId = UserUtils.getProfileParentIdOrSelf(userId, userContext);
int[] managedProfilesUserIds = new int[userProfiles.size()];
int[] managedRunningProfilesUserIds = new int[userProfiles.size()];
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 +219,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 +260,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 +299,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 +352,8 @@ public final class UserProfileGroup {
}
}
- return USER_NULL != mPrivateProfileUserId && userId == mPrivateProfileUserId;
+ return UserHandleCompat.USER_NULL != mPrivateProfileUserId
+ && userId == mPrivateProfileUserId;
}
@Override
diff --git a/service/lint-baseline.xml b/service/lint-baseline.xml
index b10928320..6aaf3da8c 100644
--- a/service/lint-baseline.xml
+++ b/service/lint-baseline.xml
@@ -35,17 +35,6 @@
</issue>
<issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.os.UserHandle#getUid`"
- errorLine1=" return UserHandle.of(userId).getUid(appId);"
- errorLine2=" ~~~~~~">
- <location
- file="packages/modules/Permission/service/java/com/android/permission/compat/UserHandleCompat.java"
- line="57"
- column="38"/>
- </issue>
-
- <issue
id="FlaggedApi"
message="Method `RolesState()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `writeFile` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
errorLine1=" roles = new RolesState(mVersion, packagesHash,"
diff --git a/service/proto/role_service.proto b/service/proto/role_service.proto
index f982ead5b..fb1866d83 100644
--- a/service/proto/role_service.proto
+++ b/service/proto/role_service.proto
@@ -56,4 +56,7 @@ message RoleProto {
// Whether fallback holders are enabled for this role.
optional bool fallback_enabled = 3;
+
+ // The active user id of this cross user role.
+ optional int32 active_user_id = 4;
}
diff --git a/tests/apex/Android.bp b/tests/apex/Android.bp
index 18f1bea75..9dfbdf589 100644
--- a/tests/apex/Android.bp
+++ b/tests/apex/Android.bp
@@ -31,6 +31,8 @@ android_test {
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.test.ext.truth",
+ "com.android.permission.flags-aconfig-java",
+ "flag-junit",
"mockito-target-extended-minus-junit4",
],
jni_libs: [
diff --git a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
index 6500b3926..e9c93a33a 100644
--- a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
+++ b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
@@ -20,12 +20,18 @@ import android.content.ApexEnvironment
import android.content.Context
import android.os.Process
import android.os.UserHandle
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.platform.app.InstrumentationRegistry
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.permission.flags.Flags
import com.google.common.truth.Truth.assertThat
import java.io.File
import org.junit.After
+import org.junit.Assume.assumeFalse
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -49,11 +55,22 @@ class RolesPersistenceTest {
private val persistence = RolesPersistenceImpl {}
private val defaultRoles = mapOf(ROLE_NAME to setOf(HOLDER_1, HOLDER_2))
+ private val activeUserIds = mapOf(ROLE_NAME to USER_ID)
private val stateVersionUndefined = RolesState(VERSION_UNDEFINED, PACKAGE_HASH, defaultRoles)
private val stateVersionFallbackMigrated =
RolesState(VERSION_FALLBACK_MIGRATED, PACKAGE_HASH, defaultRoles, setOf(ROLE_NAME))
+ private val stateVersionActiveUserIds =
+ RolesState(
+ VERSION_ACTIVE_USER_IDS,
+ PACKAGE_HASH,
+ defaultRoles,
+ setOf(ROLE_NAME),
+ activeUserIds,
+ )
private val user = Process.myUserHandle()
+ @get:Rule val flagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@Before
fun setUp() {
createMockDataDirectory()
@@ -84,16 +101,41 @@ class RolesPersistenceTest {
mockitoSession.finishMocking()
}
+ @RequiresFlagsDisabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED)
@Test
fun testWriteRead() {
+ assumeFalse(stateVersion == StateVersion.VERSION_ACTIVE_USER_IDS)
persistence.writeForUser(state, user)
val persistedState = persistence.readForUser(user)
assertThat(persistedState).isEqualTo(state)
}
+ @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ fun testWriteRead_supportsActiveUser() {
+ persistence.writeForUser(state, user)
+ val persistedState = persistence.readForUser(user)
+
+ assertThat(persistedState).isEqualTo(state)
+ }
+
+ @RequiresFlagsDisabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED)
@Test
fun testWriteCorruptReadFromReserveCopy() {
+ assumeFalse(stateVersion == StateVersion.VERSION_ACTIVE_USER_IDS)
+ persistence.writeForUser(state, user)
+ // Corrupt the primary file.
+ RolesPersistenceImpl.getFile(user)
+ .writeText("<roles version=\"-1\"><role name=\"com.foo.bar\"><holder")
+ val persistedState = persistence.readForUser(user)
+
+ assertThat(persistedState).isEqualTo(state)
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ fun testWriteCorruptReadFromReserveCopy_supportsActiveUser() {
persistence.writeForUser(state, user)
// Corrupt the primary file.
RolesPersistenceImpl.getFile(user)
@@ -116,11 +158,13 @@ class RolesPersistenceTest {
when (stateVersion) {
StateVersion.VERSION_UNDEFINED -> stateVersionUndefined
StateVersion.VERSION_FALLBACK_MIGRATED -> stateVersionFallbackMigrated
+ StateVersion.VERSION_ACTIVE_USER_IDS -> stateVersionActiveUserIds
}
enum class StateVersion {
VERSION_UNDEFINED,
- VERSION_FALLBACK_MIGRATED
+ VERSION_FALLBACK_MIGRATED,
+ VERSION_ACTIVE_USER_IDS,
}
companion object {
@@ -130,10 +174,12 @@ class RolesPersistenceTest {
private const val VERSION_UNDEFINED = -1
private const val VERSION_FALLBACK_MIGRATED = 1
+ private const val VERSION_ACTIVE_USER_IDS = 2
private const val APEX_MODULE_NAME = "com.android.permission"
private const val PACKAGE_HASH = "packagesHash"
private const val ROLE_NAME = "roleName"
private const val HOLDER_1 = "holder1"
private const val HOLDER_2 = "holder2"
+ private const val USER_ID = 10
}
}
diff --git a/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java b/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java
index fcdccd87a..f4bed4ada 100644
--- a/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java
+++ b/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java
@@ -95,8 +95,7 @@ public class BackgroundPermissionsTest {
verifyBackgroundPermissionsProperties("android");
}
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM,
- codeName = "VanillaIceCream")
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
@RequiresFlagsEnabled({Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED})
@Test
@AppModeFull(reason = "Instant apps cannot read properties of other packages")
diff --git a/tests/cts/permission/src/android/permission/cts/NfcPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NfcPermissionTest.java
index 1b3f65ee6..fc5ea3a44 100644
--- a/tests/cts/permission/src/android/permission/cts/NfcPermissionTest.java
+++ b/tests/cts/permission/src/android/permission/cts/NfcPermissionTest.java
@@ -155,23 +155,6 @@ public final class NfcPermissionTest {
}
/**
- * Verifies that isTagIntentAppPreferenceSupported() requires Permission.
- * <p>
- * Requires Permission: {@link android.Manifest.permission.WRITE_SECURE_SETTINGS}.
- */
- @Test
- @AppModeFull
- public void testIsTagIntentAppPreferenceSupported() {
- try {
- mNfcAdapter.isTagIntentAppPreferenceSupported();
- fail("mNfcAdapter.isTagIntentAppPreferenceSupported() did not throw SecurityException"
- + " as expected");
- } catch (SecurityException se) {
- // Expected Exception
- }
- }
-
- /**
* Verifies that getTagIntentAppPreferenceForUser() requires Permission.
* <p>
* Requires Permission: {@link android.Manifest.permission.WRITE_SECURE_SETTINGS}.
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/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt
index f0a0e3fc1..687234582 100644
--- a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt
+++ b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt
@@ -21,6 +21,7 @@ import android.app.Instrumentation
import android.companion.virtual.VirtualDeviceManager
import android.companion.virtual.VirtualDeviceParams
import android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM
+import android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT
import android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA
import android.content.ComponentName
import android.content.Intent
@@ -39,7 +40,9 @@ import android.permissionmultidevice.cts.UiAutomatorUtils.click
import android.permissionmultidevice.cts.UiAutomatorUtils.findTextForView
import android.permissionmultidevice.cts.UiAutomatorUtils.waitFindObject
import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
+import android.provider.Settings
import android.view.Display
import android.virtualdevice.cts.common.VirtualDeviceRule
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -47,7 +50,6 @@ import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import com.android.compatibility.common.util.SystemUtil
-import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
@@ -146,6 +148,7 @@ class DeviceAwarePermissionGrantTest {
Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
)
+ @RequiresFlagsDisabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES)
@Test
fun onRemoteDevice_requestPermissionForHostDevice_shouldShowWarningDialog() {
requestPermissionOnDevice(virtualDisplay.display.displayId, DEVICE_ID_DEFAULT)
@@ -156,6 +159,32 @@ class DeviceAwarePermissionGrantTest {
@RequiresFlagsEnabled(
Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED,
+ Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES
+ )
+ @Test
+ fun onRemoteDevice_requestPermissionForHostDevice_shouldGrantPermission() {
+ // Create a virtual device with default policy, so that camera permission request will
+ // correspond to default device camera access.
+ virtualDevice =
+ virtualDeviceRule.createManagedVirtualDevice(
+ VirtualDeviceParams.Builder()
+ .setDevicePolicy(POLICY_TYPE_CAMERA, DEVICE_POLICY_DEFAULT)
+ .build()
+ )
+ testGrantPermissionForDevice(
+ virtualDisplay.display.displayId,
+ virtualDevice.deviceId,
+ true,
+ Settings.Global.getString(defaultDeviceContext.contentResolver,
+ Settings.Global.DEVICE_NAME),
+ expectPermissionGrantedOnDefaultDevice = true,
+ expectPermissionGrantedOnRemoteDevice = false
+ )
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
)
@Test
@@ -199,7 +228,7 @@ class DeviceAwarePermissionGrantTest {
TestConstants.PERMISSION_RESULT_KEY_PERMISSIONS
)
)
- .isEqualTo(arrayOf(DEVICE_AWARE_PERMISSION))
+ .isEqualTo(arrayOf(PERMISSION))
assertThat(
grantPermissionResult.getIntArray(TestConstants.PERMISSION_RESULT_KEY_GRANT_RESULTS)
)
@@ -237,7 +266,7 @@ class DeviceAwarePermissionGrantTest {
private fun assertPermissionMessageContainsDeviceName(displayId: Int, deviceName: String) {
waitFindObject(By.displayId(displayId).res(PERMISSION_MESSAGE_ID))
val text = findTextForView(By.displayId(displayId).res(PERMISSION_MESSAGE_ID))
- Truth.assertThat(text).contains(deviceName)
+ assertThat(text).contains(deviceName)
}
private fun assertAppHasPermissionForDevice(deviceId: Int, expectPermissionGranted: Boolean) {
@@ -245,7 +274,7 @@ class DeviceAwarePermissionGrantTest {
defaultDeviceContext
.createDeviceContext(deviceId)
.packageManager
- .checkPermission(DEVICE_AWARE_PERMISSION, APP_PACKAGE_NAME)
+ .checkPermission(PERMISSION, APP_PACKAGE_NAME)
if (expectPermissionGranted) {
Assert.assertEquals(PackageManager.PERMISSION_GRANTED, checkPermissionResult)
@@ -269,7 +298,7 @@ class DeviceAwarePermissionGrantTest {
"com.android.permissioncontroller:id/permission_allow_foreground_only_button"
const val DEVICE_ID_DEFAULT = 0
const val PERSISTENT_DEVICE_ID_DEFAULT = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
- const val DEVICE_AWARE_PERMISSION = Manifest.permission.CAMERA
+ const val PERMISSION = Manifest.permission.CAMERA
const val TIMEOUT = 5000L
private const val DISPLAY_HEIGHT = 1920
private const val DISPLAY_WIDTH = 1080
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/OWNERS b/tests/cts/permissionpolicy/res/raw/OWNERS
index 6e1a91b88..4f0306f55 100644
--- a/tests/cts/permissionpolicy/res/raw/OWNERS
+++ b/tests/cts/permissionpolicy/res/raw/OWNERS
@@ -1,6 +1,5 @@
hackbod@google.com
patb@google.com
-yamasani@google.com
michaelwr@google.com
narayan@google.com
roosa@google.com
diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
index 5008bda40..e1608f878 100644
--- a/tests/cts/permissionpolicy/res/raw/android_manifest.xml
+++ b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
@@ -898,13 +898,26 @@
android:featureFlag="android.provider.user_keys" />
<!-- Allows an application to set default account for new contacts.
- <p> This permission is only granted to system applications fulfilling the Contacts app role.
+ <p>This permission is only granted to system applications fulfilling the Contacts app role.
<p>Protection level: internal|role
@SystemApi
@hide
-->
<permission android:name="android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"
- android:protectionLevel="internal|role" />
+ android:protectionLevel="internal|role"
+ android:featureFlag="!android.provider.new_default_account_api_enabled"/>
+
+ <!-- Allows an application to set default account for new contacts.
+ <p>This permission is only granted to system applications fulfilling the Contacts app role
+ and the application with known signers.
+ <p>Protection level: internal|role|knownSigner
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"
+ android:protectionLevel="internal|role|knownSigner"
+ android:knownCerts="@array/config_setContactsDefaultAccountKnownSigners"
+ android:featureFlag="android.provider.new_default_account_api_enabled"/>
<!-- ====================================================================== -->
<!-- Permissions for accessing user's calendar -->
@@ -2574,6 +2587,22 @@
android:label="@string/permlab_getAccounts" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+ <!-- @SystemApi Allows access to remove an account.
+ @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled")
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.REMOVE_ACCOUNTS"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.app.admin.flags.split_create_managed_profile_enabled" />
+
+ <!-- @SystemApi Allows access to copy an account to another user.
+ @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled")
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.COPY_ACCOUNTS"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.app.admin.flags.split_create_managed_profile_enabled" />
+
<!-- Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCOUNT_MANAGER"
@@ -2616,12 +2645,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
@@ -3929,7 +3968,7 @@
@FlaggedApi("android.security.aapm_api")
@SystemApi
@hide -->
- <permission android:name="android.permission.SET_ADVANCED_PROTECTION_MODE"
+ <permission android:name="android.permission.MANAGE_ADVANCED_PROTECTION_MODE"
android:protectionLevel="signature|privileged"
android:featureFlag="android.security.aapm_api"/>
@@ -3939,6 +3978,37 @@
android:protectionLevel="normal"
android:featureFlag="android.security.aapm_api"/>
+ <!-- Allows an application to read the state of the IntrusionDetectionService
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.READ_INTRUSION_DETECTION_STATE"
+ android:featureFlag="android.security.afl_api"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.READ_INTRUSION_DETECTION_STATE"
+ android:featureFlag="android.security.afl_api"/>
+
+ <!-- Allows an application to change the state of the IntrusionDetectionService
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.MANAGE_INTRUSION_DETECTION_STATE"
+ android:featureFlag="android.security.afl_api"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.MANAGE_INTRUSION_DETECTION_STATE"
+ android:featureFlag="android.security.afl_api"/>
+
+ <!-- Must be required by any IntrusionDetectionEventTransportService to ensure that
+ only the system can bind to it.
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE"
+ android:featureFlag="android.security.afl_api"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE"
+ android:featureFlag="android.security.afl_api"/>
+
<!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.-->
<permission android:name="android.permission.PROVISION_DEMO_DEVICE"
android:protectionLevel="signature|setup|knownSigner"
@@ -4082,6 +4152,52 @@
android:protectionLevel="signature" />
<!-- ================================== -->
+ <!-- Permissions associated with picture profiles and processing -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- @FlaggedApi(android.media.tv.flags.Flags.apply_picture_profiles)
+ Allows an app to apply a {@link MediaQualityManager.PictureProfile} to a layer via
+ {@link MediaCodec.PARAMETER_KEY_PICTURE_PROFILE} and, additionally, system apps via
+ {@link SurfaceControl.Transaction#setPictureProfileHandle}.
+ -->
+ <permission android:name="android.permission.APPLY_PICTURE_PROFILE"
+ android:protectionLevel="normal"
+ android:featureFlag="android.media.tv.flags.apply_picture_profiles"/>
+
+ <!-- @hide
+ Allows MediaQualityManager to observe any {@link MediaQualityManager.PictureProfile}
+ applied to any layer in the system by apps via
+ {@link MediaCodec.PARAMETER_KEY_PICTURE_PROFILE} and by system apps via
+ {@link SurfaceControl.Transaction#setPictureProfileHandle}.
+ -->
+ <permission android:name="android.permission.OBSERVE_PICTURE_PROFILES"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.media.tv.flags.apply_picture_profiles"/>
+
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its picture profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
+
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its sound profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
+
+ <!-- ================================== -->
<!-- Permissions affecting the display of other applications -->
<!-- ================================== -->
<eat-comment />
@@ -4258,6 +4374,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 +4892,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_WRITE_SYSTEM_PREFERENCE_PERMISSION_ENABLED)
+ 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 -->
+ <permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES"
+ android:protectionLevel="signature|privileged|appop"
+ android:featureFlag="com.android.settingslib.flags.write_system_preference_permission_enabled" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
@@ -5552,6 +5701,17 @@
<permission android:name="android.permission.LOCK_DEVICE"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi
+ @FlaggedApi(android.security.Flags.FLAG_SECURE_LOCKDOWN)
+ Allows an application to lock down the device into an enhanced security state.
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_SECURE_LOCK_DEVICE"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.security.secure_lockdown" />
+
<!-- @SystemApi Allows low-level access to setting the orientation (actually
rotation) of the screen.
<p>Not for use by third-party applications.
@@ -6162,6 +6322,15 @@
<permission android:name="android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT"
android:protectionLevel="signature|privileged|role" />
+ <!-- @SystemApi Allows an application to bypass concurrency restrictions while
+ recording audio. For example, apps with this permission can continue to record
+ while a voice call is active.</p>
+ @FlaggedApi(android.media.audio.Flags.FLAG_CONCURRENT_AUDIO_RECORD_BYPASS_PERMISSION)
+ @hide -->
+ <permission android:name="android.permission.BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION"
+ android:featureFlag="android.media.audio.concurrent_audio_record_bypass_permission"
+ android:protectionLevel="signature|privileged|role" />
+
<!-- @SystemApi Allows an application to capture audio for hotword detection.
<p>Not for use by third-party applications.</p>
@hide -->
@@ -7605,7 +7774,31 @@
<!-- @SystemApi Allows an application to access shared libraries.
@hide -->
<permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer"
+ android:featureFlag="!android.content.pm.sdk_dependency_installer" />
+
+ <!-- @SystemApi Allows an application to access shared libraries.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
+ android:protectionLevel="signature|installer|role"
+ android:featureFlag="android.content.pm.sdk_dependency_installer" />
+
+ <!-- @SystemApi Permission held by the system to allow binding to the dependency installer role
+ holder.
+ @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide -->
+ <permission android:name="android.permission.BIND_DEPENDENCY_INSTALLER"
+ android:protectionLevel="signature"
+ android:featureFlag="android.content.pm.sdk_dependency_installer" />
+
+ <!-- @SystemApi Allows an application to install shared libraries of types
+ {@link android.content.pm.SharedLibraryInfo#TYPE_STATIC} or
+ {@link android.content.pm.SharedLibraryInfo#TYPE_SDK_PACKAGE}.
+ @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide -->
+ <permission android:name="android.permission.INSTALL_DEPENDENCY_SHARED_LIBRARIES"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.content.pm.sdk_dependency_installer" />
<!-- Allows an app to log compat change usage.
@hide <p>Not for use by third-party applications.</p> -->
@@ -8096,6 +8289,26 @@
android:protectionLevel="signature|knownSigner"
android:knownCerts="@array/config_healthConnectMigrationKnownSigners" />
+ <!-- @hide @SystemApi Allows permitted apps to back up Health Connect data and settings.
+ <p>Protection level: signature|knownSigner
+ @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled")
+ -->
+ <permission
+ android:name="android.permission.BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS"
+ android:protectionLevel="signature|knownSigner"
+ android:knownCerts="@array/config_backupHealthConnectDataAndSettingsKnownSigners"
+ android:featureFlag="android.permission.flags.health_connect_backup_restore_permission_enabled" />
+
+ <!-- @hide @SystemApi Allows permitted apps to restore Health Connect data and settings.
+ <p>Protection level: signature|knownSigner
+ @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled")
+ -->
+ <permission
+ android:name="android.permission.RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS"
+ android:protectionLevel="signature|knownSigner"
+ android:knownCerts="@array/config_restoreHealthConnectDataAndSettingsKnownSigners"
+ android:featureFlag="android.permission.flags.health_connect_backup_restore_permission_enabled" />
+
<!-- @SystemApi Allows an app to query apps in clone profile. The permission is
bidirectional in nature, i.e. cloned apps would be able to query apps in root user.
The permission is not meant for 3P apps as of now.
@@ -8288,58 +8501,23 @@
<permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
android:protectionLevel="signature"/>
- <!-- @SystemApi
- @FlaggedApi("android.content.pm.verification_service")
- Allows app to be the verification agent to verify packages.
+ <!-- Allows app to enter trade-in-mode.
<p>Protection level: signature|privileged
@hide
-->
- <permission android:name="android.permission.VERIFICATION_AGENT"
+ <permission android:name="android.permission.ENTER_TRADE_IN_MODE"
android:protectionLevel="signature|privileged"
- android:featureFlag="android.content.pm.verification_service"/>
+ android:featureFlag="com.android.tradeinmode.flags.enable_trade_in_mode" />
<!-- @SystemApi
- @FlaggedApi("android.content.pm.verification_service")
- Must be required by a privileged {@link android.content.pm.verify.pkg.VerifierService}
- to ensure that only the system can bind to it.
- This permission should not be held by anything other than the system.
- <p>Not for use by third-party applications. </p>
+ @FlaggedApi(com.android.art.flags.Flags.FLAG_EXECUTABLE_METHOD_FILE_OFFSETS)
+ Ability to read program metadata and attach dynamic instrumentation.
<p>Protection level: signature
@hide
-->
- <permission android:name="android.permission.BIND_VERIFICATION_AGENT"
- android:protectionLevel="internal"
- android:featureFlag="android.content.pm.verification_service" />
-
- <!--
- @SystemApi
- @FlaggedApi("android.media.tv.flags.media_quality_fw")
- Allows an application to access its picture profile from the media quality database.
- <p> Protection level: signature|privileged|vendor privileged
- @hide
- -->
- <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"
- android:protectionLevel="signature|privileged|vendorPrivileged"
- android:featureFlag="android.media.tv.flags.media_quality_fw"/>
-
- <!--
- @SystemApi
- @FlaggedApi("android.media.tv.flags.media_quality_fw")
- Allows an application to access its sound profile from the media quality database.
- <p> Protection level: signature|privileged|vendor privileged
- @hide
- -->
- <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"
- android:protectionLevel="signature|privileged|vendorPrivileged"
- android:featureFlag="android.media.tv.flags.media_quality_fw"/>
-
- <!-- Allows app to enter trade-in-mode.
- <p>Protection level: signature|privileged
- @hide
- -->
- <permission android:name="android.permission.ENTER_TRADE_IN_MODE"
- android:protectionLevel="signature|privileged"
- android:featureFlag="com.android.tradeinmode.flags.enable_trade_in_mode" />
+ <permission android:name="android.permission.DYNAMIC_INSTRUMENTATION"
+ android:protectionLevel="signature"
+ android:featureFlag="com.android.art.flags.executable_method_file_offsets" />
<!-- @SystemApi
@FlaggedApi("android.media.tv.flags.kids_mode_tvdb_sharing")
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt
index 70832b6ba..2ce48af44 100644
--- a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt
@@ -59,6 +59,7 @@ import android.app.AppOpsManager.permissionToOp
import android.content.pm.PackageManager.GET_PERMISSIONS
import android.content.pm.PermissionInfo.PROTECTION_DANGEROUS
import android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP
+import android.health.connect.HealthPermissions
import android.os.Build
import android.permission.flags.Flags
import android.permission.PermissionManager
@@ -195,6 +196,18 @@ class RuntimePermissionProperties {
expectedPerms.add(RANGING)
}
- assertThat(expectedPerms).containsExactlyElementsIn(platformRuntimePerms.map { it.name })
+ // Separately check health permissions.
+ if (Flags.replaceBodySensorPermissionEnabled()) {
+ assertThat(expectedPerms).contains(HealthPermissions.READ_HEART_RATE);
+ assertThat(expectedPerms).contains(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
+
+ // Remove these from the expected list once we've confirmed their
+ // present. These are not permissions owned by "android" so won't be
+ // in the list of platform runtime permissions.
+ expectedPerms.remove(HealthPermissions.READ_HEART_RATE);
+ expectedPerms.remove(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
+ }
+
+ assertThat(platformRuntimePerms.map { it.name }).containsExactlyElementsIn(expectedPerms)
}
}
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()
+ }
}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
index d509add3a..da70fc186 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
@@ -17,15 +17,22 @@
package android.permissionui.cts
import android.os.Build
+import android.permission.flags.Flags
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.filters.FlakyTest
import androidx.test.filters.SdkSuppress
import org.junit.Assume.assumeFalse
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
/** Runtime permission behavior tests for permission splits. */
@FlakyTest
class PermissionSplitTest : BaseUsePermissionTest() {
+
+ @Rule @JvmField val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@Before
fun assumeNotTv() {
assumeFalse(isTv)
@@ -56,6 +63,7 @@ class PermissionSplitTest : BaseUsePermissionTest() {
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @RequiresFlagsDisabled(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
@Test
fun testBodySensorSplit() {
installPackage(APP_APK_PATH_31)
@@ -63,6 +71,7 @@ class PermissionSplitTest : BaseUsePermissionTest() {
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @RequiresFlagsDisabled(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
@Test
fun testBodySensorSplit32() {
installPackage(APP_APK_PATH_32)
@@ -70,6 +79,7 @@ class PermissionSplitTest : BaseUsePermissionTest() {
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @RequiresFlagsDisabled(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
@Test
fun testBodySensorNonSplit() {
installPackage(APP_APK_PATH_LATEST)
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/SensorBlockedBannerTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/SensorBlockedBannerTest.kt
index 614b59f3c..35b035883 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/SensorBlockedBannerTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/SensorBlockedBannerTest.kt
@@ -30,6 +30,7 @@ import androidx.test.filters.FlakyTest
import androidx.test.filters.SdkSuppress
import androidx.test.uiautomator.By
import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.eventually
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.modules.utils.build.SdkLevel
import java.util.regex.Pattern
@@ -142,7 +143,9 @@ class SensorBlockedBannerTest : BaseUsePermissionTest() {
click(By.res(CHANGE_BUTTON))
// Enable global camera toggle and verify
waitFindObject(By.text(CAMERA_TOGGLE_LABEL)).click()
- assertTrue(!isSensorPrivacyEnabled(CAMERA))
+ eventually {
+ assertTrue(!isSensorPrivacyEnabled(CAMERA))
+ }
}
private fun setSensor(sensor: Int, enable: Boolean) {
diff --git a/tests/cts/role/Android.bp b/tests/cts/role/Android.bp
index 5751aaada..9f1e6cff6 100644
--- a/tests/cts/role/Android.bp
+++ b/tests/cts/role/Android.bp
@@ -30,10 +30,12 @@ android_test {
static_libs: [
"android.permission.flags-aconfig-java-export",
"androidx.test.rules",
+ "com.android.permission.flags-aconfig-java-export",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"Harrier",
"bedstead-multiuser",
+ "flag-junit",
"platform-test-annotations",
"truth",
],
diff --git a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
index e3bf054b0..9f89140d7 100644
--- a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
@@ -104,6 +104,8 @@ public class RoleManagerTest {
private static final String ROLE_NAME = RoleManager.ROLE_BROWSER;
private static final String ROLE_PHONE_NAME = RoleManager.ROLE_DIALER;
private static final String ROLE_SMS_NAME = RoleManager.ROLE_SMS;
+ private static final String PROFILE_GROUP_EXCLUSIVE_ROLE_NAME =
+ RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY;
private static final String ROLE_SHORT_LABEL = "Browser app";
private static final String APP_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestApp.apk";
@@ -1349,6 +1351,51 @@ public class RoleManagerTest {
});
}
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ public void cannotGetActiveUserForRoleWithoutPermission() throws Exception {
+ assertThrows(SecurityException.class, ()->
+ sRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME));
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ public void cannotGetActiveUserForNonProfileGroupExclusiveRole() throws Exception {
+ runWithShellPermissionIdentity(() ->
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.getActiveUserForRole(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER)));
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ public void cannotSetActiveUserForRoleWithoutPermission() throws Exception {
+ assertThrows(SecurityException.class, ()->
+ sRoleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME,
+ Process.myUserHandle(), 0));
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ public void cannotSetActiveUserForNonProfileGroupExclusiveRole() throws Exception {
+ runWithShellPermissionIdentity(() ->
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.setActiveUserForRole(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER, Process.myUserHandle(),
+ 0)));
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ public void setAndGetActiveUserForRole() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ sRoleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME,
+ Process.myUserHandle(), 0);
+ assertThat(sRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME))
+ .isEqualTo(Process.myUserHandle());
+ });
+ }
+
@NonNull
private List<String> getRoleHolders(@NonNull String roleName) throws Exception {
return callWithShellPermissionIdentity(() -> sRoleManager.getRoleHolders(roleName));
diff --git a/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt b/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt
index 83d4f78ad..f615f0f4b 100644
--- a/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt
+++ b/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt
@@ -19,15 +19,19 @@ package android.app.role.cts
import android.app.role.RoleManager
import android.os.Build
import android.os.UserHandle
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.InstrumentationRegistry
import androidx.test.filters.SdkSuppress
import androidx.test.runner.AndroidJUnit4
import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
+import com.android.permission.flags.Flags
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,6 +47,8 @@ class RoleShellCommandTest {
private var roleHolder: String? = null
private var wasBypassingRoleQualification: Boolean = false
+ @get:Rule val flagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@Before
public fun setUp() {
saveRoleHolder()
@@ -156,6 +162,28 @@ class RoleShellCommandTest {
assertThat(isBypassingRoleQualification()).isFalse()
}
+ @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ fun setActiveUserForProfileGroupExclusiveRoleAsUser() {
+ val activeUser = userId
+ setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, activeUser)
+
+ val currentActiveUserId = getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ assertThat(currentActiveUserId).isEqualTo(activeUser)
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ fun setActiveUserForNonProfileGroupExclusiveRoleThenFails() {
+ assertThrows(AssertionError::class.java) { setActiveUserForRole(ROLE_NAME, userId) }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @Test
+ fun getActiveUserForNonProfileGroupExclusiveRoleThenFails() {
+ assertThrows(AssertionError::class.java) { getActiveUserForRole(ROLE_NAME) }
+ }
+
private fun addRoleHolder(packageName: String = APP_PACKAGE_NAME) {
runShellCommandOrThrow("cmd role add-role-holder --user $userId $ROLE_NAME $packageName")
}
@@ -204,8 +232,22 @@ class RoleShellCommandTest {
callWithShellPermissionIdentity { roleManager.setBypassingRoleQualification(value) }
}
+ private fun getActiveUserForRole(roleName: String): Int? {
+ return runShellCommandOrThrow("cmd role get-active-user-for-role --user $userId $roleName")
+ .trim()
+ .toIntOrNull()
+ }
+
+ private fun setActiveUserForRole(roleName: String, activeUserId: Int) {
+ runShellCommandOrThrow(
+ "cmd role set-active-user-for-role --user $userId $roleName $activeUserId"
+ )
+ }
+
companion object {
private const val ROLE_NAME = RoleManager.ROLE_BROWSER
+ private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME =
+ RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY
private const val APP_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestApp.apk"
private const val APP_PACKAGE_NAME = "android.app.role.cts.app"
private const val APP_CLONE_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestAppClone.apk"
diff --git a/tests/cts/rolemultiuser/OWNERS b/tests/cts/rolemultiuser/OWNERS
new file mode 100644
index 000000000..fb6099cf7
--- /dev/null
+++ b/tests/cts/rolemultiuser/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/tests/functional/safetycenter/singleuser/AndroidTest.xml b/tests/functional/safetycenter/singleuser/AndroidTest.xml
index 3aa173508..af040eb6f 100644
--- a/tests/functional/safetycenter/singleuser/AndroidTest.xml
+++ b/tests/functional/safetycenter/singleuser/AndroidTest.xml
@@ -47,6 +47,10 @@
<!-- Disable syncing to prevent overwriting flags during testing. -->
<option name="run-command" value="device_config set_sync_disabled_for_tests persistent" />
<option name="teardown-command" value="device_config set_sync_disabled_for_tests none" />
+ <!-- TODO(b/379928062): Ensure device not on lockscreen. Reassess when keyguard bug is
+ closed -->
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
<!-- Dismiss any system dialogs (e.g. crashes, ANR). -->
<option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS --receiver-foreground" />
</target_preparer>
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt
index 912ea44ad..f6bef747d 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt
@@ -17,6 +17,7 @@
package com.android.safetycenter.testing
import android.Manifest.permission.READ_DEVICE_CONFIG
+import android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG
import android.Manifest.permission.WRITE_DEVICE_CONFIG
import android.annotation.TargetApi
import android.app.job.JobInfo
@@ -532,7 +533,7 @@ object SafetyCenterFlags {
}
private fun writeDeviceConfigProperty(name: String, stringValue: String?) {
- callWithShellPermissionIdentity(WRITE_DEVICE_CONFIG) {
+ callWithShellPermissionIdentity(WRITE_DEVICE_CONFIG, WRITE_ALLOWLISTED_DEVICE_CONFIG) {
val valueWasSet =
DeviceConfig.setProperty(
NAMESPACE_PRIVACY,