summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--PermissionController/Android.bp1
-rw-r--r--PermissionController/jarjar-rules.txt12
-rw-r--r--PermissionController/res/layout-v33/preference_issue_card.xml133
-rw-r--r--PermissionController/res/layout-v33/preference_more_issues_card.xml11
-rw-r--r--PermissionController/res/layout-v33/preference_safety_status.xml11
-rw-r--r--PermissionController/res/layout-v35/app_permission_footer_link_preference.xml34
-rw-r--r--PermissionController/res/layout-v35/permission_preference_selector_with_widget.xml69
-rw-r--r--PermissionController/res/layout-v35/permission_preference_two_target.xml60
-rw-r--r--PermissionController/res/layout-v35/permission_preference_widget_radiobutton.xml24
-rw-r--r--PermissionController/res/values-af-v33/strings.xml4
-rw-r--r--PermissionController/res/values-af/strings.xml104
-rw-r--r--PermissionController/res/values-am/strings.xml2
-rw-r--r--PermissionController/res/values-ar/strings.xml2
-rw-r--r--PermissionController/res/values-as/strings.xml2
-rw-r--r--PermissionController/res/values-az/strings.xml2
-rw-r--r--PermissionController/res/values-b+sr+Latn/strings.xml2
-rw-r--r--PermissionController/res/values-be/strings.xml2
-rw-r--r--PermissionController/res/values-bg/strings.xml4
-rw-r--r--PermissionController/res/values-bn/strings.xml2
-rw-r--r--PermissionController/res/values-bs/strings.xml2
-rw-r--r--PermissionController/res/values-ca/strings.xml4
-rw-r--r--PermissionController/res/values-cs/strings.xml2
-rw-r--r--PermissionController/res/values-da/strings.xml2
-rw-r--r--PermissionController/res/values-de/strings.xml4
-rw-r--r--PermissionController/res/values-el/strings.xml2
-rw-r--r--PermissionController/res/values-en-rAU/strings.xml2
-rw-r--r--PermissionController/res/values-en-rCA/strings.xml2
-rw-r--r--PermissionController/res/values-en-rGB/strings.xml2
-rw-r--r--PermissionController/res/values-en-rIN/strings.xml2
-rw-r--r--PermissionController/res/values-es-rUS/strings.xml2
-rw-r--r--PermissionController/res/values-es/strings.xml4
-rw-r--r--PermissionController/res/values-et/strings.xml2
-rw-r--r--PermissionController/res/values-eu/strings.xml6
-rw-r--r--PermissionController/res/values-fa/strings.xml2
-rw-r--r--PermissionController/res/values-fi/strings.xml2
-rw-r--r--PermissionController/res/values-fr-rCA/strings.xml2
-rw-r--r--PermissionController/res/values-fr/strings.xml2
-rw-r--r--PermissionController/res/values-gl/strings.xml2
-rw-r--r--PermissionController/res/values-gu/strings.xml2
-rw-r--r--PermissionController/res/values-hi/strings.xml2
-rw-r--r--PermissionController/res/values-hr/strings.xml10
-rw-r--r--PermissionController/res/values-hu/strings.xml2
-rw-r--r--PermissionController/res/values-hy/strings.xml2
-rw-r--r--PermissionController/res/values-in/strings.xml2
-rw-r--r--PermissionController/res/values-is/strings.xml2
-rw-r--r--PermissionController/res/values-it/strings.xml2
-rw-r--r--PermissionController/res/values-iw/strings.xml2
-rw-r--r--PermissionController/res/values-ja/strings.xml6
-rw-r--r--PermissionController/res/values-ka/strings.xml2
-rw-r--r--PermissionController/res/values-kk/strings.xml2
-rw-r--r--PermissionController/res/values-km/strings.xml2
-rw-r--r--PermissionController/res/values-kn/strings.xml2
-rw-r--r--PermissionController/res/values-ko/strings.xml2
-rw-r--r--PermissionController/res/values-ky/strings.xml2
-rw-r--r--PermissionController/res/values-lo/strings.xml2
-rw-r--r--PermissionController/res/values-lt/strings.xml2
-rw-r--r--PermissionController/res/values-lv/strings.xml2
-rw-r--r--PermissionController/res/values-mk/strings.xml2
-rw-r--r--PermissionController/res/values-ml/strings.xml2
-rw-r--r--PermissionController/res/values-mn/strings.xml2
-rw-r--r--PermissionController/res/values-mr/strings.xml2
-rw-r--r--PermissionController/res/values-ms/strings.xml6
-rw-r--r--PermissionController/res/values-my/strings.xml2
-rw-r--r--PermissionController/res/values-nb/strings.xml2
-rw-r--r--PermissionController/res/values-ne/strings.xml2
-rw-r--r--PermissionController/res/values-night-v33/themes.xml2
-rw-r--r--PermissionController/res/values-nl/strings.xml2
-rw-r--r--PermissionController/res/values-or/strings.xml2
-rw-r--r--PermissionController/res/values-pa/strings.xml2
-rw-r--r--PermissionController/res/values-pl/strings.xml2
-rw-r--r--PermissionController/res/values-pt-rBR/strings.xml2
-rw-r--r--PermissionController/res/values-pt-rPT/strings.xml2
-rw-r--r--PermissionController/res/values-pt/strings.xml2
-rw-r--r--PermissionController/res/values-ro/strings.xml2
-rw-r--r--PermissionController/res/values-ru/strings.xml6
-rw-r--r--PermissionController/res/values-si/strings.xml2
-rw-r--r--PermissionController/res/values-sk/strings.xml2
-rw-r--r--PermissionController/res/values-sl/strings.xml2
-rw-r--r--PermissionController/res/values-sq/strings.xml2
-rw-r--r--PermissionController/res/values-sr/strings.xml2
-rw-r--r--PermissionController/res/values-sv/strings.xml2
-rw-r--r--PermissionController/res/values-sw/strings.xml2
-rw-r--r--PermissionController/res/values-ta/strings.xml2
-rw-r--r--PermissionController/res/values-te/strings.xml2
-rw-r--r--PermissionController/res/values-th/strings.xml2
-rw-r--r--PermissionController/res/values-tl/strings.xml2
-rw-r--r--PermissionController/res/values-tr/strings.xml2
-rw-r--r--PermissionController/res/values-uk/strings.xml2
-rw-r--r--PermissionController/res/values-ur/strings.xml2
-rw-r--r--PermissionController/res/values-uz/strings.xml2
-rw-r--r--PermissionController/res/values-v33/attrs.xml5
-rw-r--r--PermissionController/res/values-v33/styles.xml4
-rw-r--r--PermissionController/res/values-v33/themes.xml12
-rw-r--r--PermissionController/res/values-v35/styles.xml216
-rw-r--r--PermissionController/res/values-vi/strings.xml2
-rw-r--r--PermissionController/res/values-watch/donottranslate.xml51
-rw-r--r--PermissionController/res/values-zh-rCN/strings.xml2
-rw-r--r--PermissionController/res/values-zh-rHK/strings.xml4
-rw-r--r--PermissionController/res/values-zh-rTW/strings.xml2
-rw-r--r--PermissionController/res/values-zu/strings.xml2
-rw-r--r--PermissionController/res/values/bools.xml2
-rw-r--r--PermissionController/res/values/overlayable.xml102
-rw-r--r--PermissionController/res/values/strings.xml18
-rw-r--r--PermissionController/res/xml-v35/app_permission.xml (renamed from PermissionController/res/xml/app_permission.xml)32
-rw-r--r--PermissionController/res/xml/roles.xml216
-rw-r--r--PermissionController/role-controller/Android.bp8
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java89
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java4
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java108
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Role.java135
-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.java271
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java30
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java45
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java21
-rw-r--r--PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt59
-rw-r--r--PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt31
-rw-r--r--PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/compat/AppOpsManagerCompat.java49
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt21
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt91
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java26
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java36
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt16
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt127
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt12
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsFragment.java14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java35
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt19
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt19
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFooterLinkPreference.kt64
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionSelectorWithWidgetPreference.kt (renamed from PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionSelectorWithWidgetPreference.kt)16
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionTwoTargetPreference.kt (renamed from PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionTwoTargetPreference.kt)10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt596
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt25
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt53
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt61
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt36
-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/AnnotatedText.kt67
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt133
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt41
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt50
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt39
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt146
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt75
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionIconBuilder.kt101
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt53
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt301
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt171
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt158
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt62
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3ColorScheme.kt209
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Shapes.kt66
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3TypeScaleTokens.kt109
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Typography.kt240
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3VariableFontTokens.kt186
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearMaterialBridgedLegacyTheme.kt82
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearOverlayableMaterial3Theme.kt41
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt79
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt133
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java33
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/Role.md7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/UserPackage.java104
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java38
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppConfirmationDialogFragment.java23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java33
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java36
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleListLiveData.java (renamed from PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/MergeRoleListLiveData.java)14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleLiveData.java (renamed from PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/MergeRoleLiveData.java)4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java129
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleListFilterFunction.java53
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/ReservedForTestingProfileGroupExclusivityRoleUiBehavior.java79
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListViewModel.java1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt29
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt117
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt44
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt124
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/WearRequestRoleViewModel.kt22
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java39
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/HighlightablePreferenceGroupAdapter.java141
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java32
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java29
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt12
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt50
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt3
-rw-r--r--PermissionController/tests/inprocess/Android.bp3
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt66
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionGroupUsageDetailsUseCaseTest.kt212
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt6
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/WorkPolicyInfoTest.kt3
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt136
-rw-r--r--PermissionController/tests/permissionui/Android.bp2
-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--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt11
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt35
-rw-r--r--SafetyCenter/Resources/res/raw-v36/safety_center_config.xml158
-rw-r--r--SafetyCenter/Resources/res/values-de-v34/strings.xml2
-rw-r--r--flags/Android.bp1
-rw-r--r--flags/flags.aconfig47
-rw-r--r--framework-s/api/system-current.txt8
-rw-r--r--framework-s/java/android/app/ecm/EnhancedConfirmationManager.java11
-rw-r--r--framework-s/java/android/app/role/IRoleManager.aidl13
-rw-r--r--framework-s/java/android/app/role/RoleManager.java223
-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.bp1
-rw-r--r--service/api/system-server-current.txt14
-rw-r--r--service/jarjar-rules.txt12
-rw-r--r--service/java/com/android/ecm/EnhancedConfirmationCallTrackerService.java80
-rw-r--r--service/java/com/android/ecm/EnhancedConfirmationManagerLocal.java56
-rw-r--r--service/java/com/android/ecm/EnhancedConfirmationManagerLocalImpl.java59
-rw-r--r--service/java/com/android/ecm/EnhancedConfirmationService.java172
-rw-r--r--service/java/com/android/permission/util/UserUtils.java113
-rw-r--r--service/java/com/android/role/RoleService.java350
-rw-r--r--service/java/com/android/role/RoleShellCommand.java29
-rw-r--r--service/java/com/android/role/RoleUserState.java127
-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.java64
-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.java35
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java8
-rw-r--r--tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java36
-rwxr-xr-xtests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java73
-rw-r--r--tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt39
-rw-r--r--tests/cts/permissionmultiuser/Android.bp1
-rw-r--r--tests/cts/permissionmultiuser/AndroidTest.xml4
-rw-r--r--tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt5
-rw-r--r--tests/cts/permissionpolicy/Android.bp1
-rw-r--r--tests/cts/permissionpolicy/res/raw/android_manifest.xml411
-rw-r--r--tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml83
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt15
-rw-r--r--tests/cts/permissionui/AndroidManifest.xml18
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt1
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt3
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt78
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt201
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt11
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt30
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt25
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/VoipCallHelper.kt171
-rw-r--r--tests/cts/role/Android.bp3
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleManagerTest.java286
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleManagerUtil.kt62
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt45
-rw-r--r--tests/cts/rolemultiuser/Android.bp50
-rw-r--r--tests/cts/rolemultiuser/AndroidManifest.xml36
-rw-r--r--tests/cts/rolemultiuser/AndroidTest.xml53
-rw-r--r--tests/cts/rolemultiuser/TEST_MAPPING12
-rw-r--r--tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt1602
-rw-r--r--tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/WaitForResultActivity.kt67
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt26
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt4
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt34
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt2
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt18
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt236
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt4
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt8
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt6
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt36
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt8
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt30
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt2
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt212
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt28
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt8
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt4
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt10
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt2
-rw-r--r--tests/functional/safetycenter/multiusers/Android.bp1
-rw-r--r--tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt330
-rw-r--r--tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt271
-rw-r--r--tests/functional/safetycenter/singleuser/AndroidTest.xml4
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt811
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt92
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt15
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt44
-rw-r--r--tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt22
-rw-r--r--tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt44
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt6
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt2
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/EqualsHashCodeToStringTester.kt10
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt12
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt6
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt10
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt6
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt53
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt16
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt100
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt10
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt4
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt2
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt16
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt20
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt72
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SettingsPackage.kt2
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt2
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt2
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt18
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt2
332 files changed, 12145 insertions, 3566 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 21d6f4774..596b2dbb5 100644
--- a/PermissionController/Android.bp
+++ b/PermissionController/Android.bp
@@ -158,6 +158,7 @@ android_library {
"androidx.compose.runtime_runtime-livedata",
"androidx.compose.ui_ui",
"androidx.wear.compose_compose-material",
+ "androidx.wear.compose_compose-material3",
"android.content.pm.flags-aconfig-java-export",
"android.os.flags-aconfig-java-export",
],
diff --git a/PermissionController/jarjar-rules.txt b/PermissionController/jarjar-rules.txt
index 7bfda1ab1..5ecb0241f 100644
--- a/PermissionController/jarjar-rules.txt
+++ b/PermissionController/jarjar-rules.txt
@@ -2,6 +2,10 @@
# RoleParser.applyJarjarTransform(), by adding NO_IFTTT=reason to your commit
# message.
# LINT.IfChange
+rule android.app.admin.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.app.admin.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.app.admin.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule android.app.admin.flags.Flags 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.FeatureFlags com.android.permissioncontroller.jarjar.@0
@@ -22,4 +26,12 @@ 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
+rule com.android.settingslib.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule com.android.settingslib.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule com.android.settingslib.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule com.android.settingslib.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/layout-v33/preference_issue_card.xml b/PermissionController/res/layout-v33/preference_issue_card.xml
index e6d749142..107c778a1 100644
--- a/PermissionController/res/layout-v33/preference_issue_card.xml
+++ b/PermissionController/res/layout-v33/preference_issue_card.xml
@@ -13,81 +13,84 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<androidx.constraintlayout.widget.ConstraintLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/issue_card"
- android:clickable="false"
- android:screenReaderFocusable="true"
- style="@style/SafetyCenterCard.Issue">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/issue_card"
+ android:clickable="false"
+ android:screenReaderFocusable="true"
+ style="@style/SafetyCenterCard.Issue">
- <ImageButton
- android:id="@+id/issue_card_dismiss_btn"
- android:src="@drawable/ic_safety_issue_dismiss"
- android:contentDescription="@string/safety_center_issue_card_dismiss_button"
- style="@style/SafetyCenterIssueDismiss" />
+ <ImageButton
+ android:id="@+id/issue_card_dismiss_btn"
+ android:src="@drawable/ic_safety_issue_dismiss"
+ android:contentDescription="@string/safety_center_issue_card_dismiss_button"
+ style="@style/SafetyCenterIssueDismiss" />
- <TextView
- android:id="@+id/issue_card_attribution_title"
- android:text="@string/summary_placeholder"
- android:screenReaderFocusable="false"
- style="@style/SafetyCenterIssueAttributionTitle" />
+ <TextView
+ android:id="@+id/issue_card_attribution_title"
+ android:text="@string/summary_placeholder"
+ android:screenReaderFocusable="false"
+ style="@style/SafetyCenterIssueAttributionTitle" />
- <TextView
- android:id="@+id/issue_card_title"
- android:text="@string/summary_placeholder"
- android:screenReaderFocusable="false"
- style="@style/SafetyCenterIssueTitle" />
+ <TextView
+ android:id="@+id/issue_card_title"
+ android:text="@string/summary_placeholder"
+ android:screenReaderFocusable="false"
+ style="@style/SafetyCenterIssueTitle" />
- <TextView
- android:id="@+id/issue_card_subtitle"
- android:text="@string/summary_placeholder"
- android:screenReaderFocusable="false"
- style="@style/SafetyCenterIssueSubtitle" />
+ <TextView
+ android:id="@+id/issue_card_subtitle"
+ android:text="@string/summary_placeholder"
+ android:screenReaderFocusable="false"
+ style="@style/SafetyCenterIssueSubtitle" />
- <TextView
- android:id="@+id/issue_card_summary"
- android:text="@string/summary_placeholder"
- android:screenReaderFocusable="false"
- style="@style/SafetyCenterIssueSummary" />
+ <TextView
+ android:id="@+id/issue_card_summary"
+ android:text="@string/summary_placeholder"
+ android:screenReaderFocusable="false"
+ style="@style/SafetyCenterIssueSummary" />
- <include
- android:id="@+id/issue_card_action_button_list"
- layout="?attr/scActionButtonListLayout"/>
+ <include
+ android:id="@+id/issue_card_action_button_list"
+ layout="?attr/scActionButtonListLayout"/>
- <com.android.permissioncontroller.permission.ui.v33.widget.SafetyProtectionSectionView
- android:id="@+id/issue_card_protected_by_android"
- android:importantForAccessibility="no"
- style="@style/SafetyCenterIssueSafetyProtectionSection" />
+ <com.android.permissioncontroller.permission.ui.v33.widget.SafetyProtectionSectionView
+ android:id="@+id/issue_card_protected_by_android"
+ android:importantForAccessibility="no"
+ style="@style/SafetyCenterIssueSafetyProtectionSection" />
- <ImageView
- android:id="@+id/resolved_issue_image"
- android:src="@drawable/safety_center_issue_resolved_avd"
- android:importantForAccessibility="no"
- style="@style/SafetyCenterIssueCardResolvedImage" />
+ <ImageView
+ android:id="@+id/resolved_issue_image"
+ android:src="@drawable/safety_center_issue_resolved_avd"
+ android:importantForAccessibility="no"
+ style="@style/SafetyCenterIssueCardResolvedImage" />
- <TextView
- android:id="@+id/resolved_issue_text"
- android:text="@string/safety_center_resolved_issue_fallback"
- style="@style/SafetyCenterIssueCardResolvedTitle" />
+ <TextView
+ android:id="@+id/resolved_issue_text"
+ android:text="@string/safety_center_resolved_issue_fallback"
+ style="@style/SafetyCenterIssueCardResolvedTitle" />
- <!-- This group doesn't contain issue_card_attribution_title, issue_card_dismiss_btn,
- issue_card_subtitle or issue_card_protected_by_android since the version of
- ConstraintLayout we're using doesn't allow us to override the group's visibility on
- individual group members. See b/242705351 for context. -->
- <androidx.constraintlayout.widget.Group
- android:id="@+id/default_issue_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="visible"
- app:constraint_referenced_ids="issue_card_title,issue_card_summary,issue_card_action_button_list" />
+ <!-- This group doesn't contain issue_card_attribution_title, issue_card_dismiss_btn,
+ issue_card_subtitle or issue_card_protected_by_android since the version of
+ ConstraintLayout we're using doesn't allow us to override the group's visibility on
+ individual group members. See b/242705351 for context. -->
+ <androidx.constraintlayout.widget.Group
+ android:id="@+id/default_issue_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="visible"
+ app:constraint_referenced_ids="issue_card_title,issue_card_summary,issue_card_action_button_list" />
- <androidx.constraintlayout.widget.Group
- android:id="@+id/resolved_issue_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- app:constraint_referenced_ids="resolved_issue_image,resolved_issue_text" />
+ <androidx.constraintlayout.widget.Group
+ android:id="@+id/resolved_issue_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ app:constraint_referenced_ids="resolved_issue_image,resolved_issue_text" />
-</androidx.constraintlayout.widget.ConstraintLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/PermissionController/res/layout-v33/preference_more_issues_card.xml b/PermissionController/res/layout-v33/preference_more_issues_card.xml
index c93125762..d0ac6b1f5 100644
--- a/PermissionController/res/layout-v33/preference_more_issues_card.xml
+++ b/PermissionController/res/layout-v33/preference_more_issues_card.xml
@@ -13,8 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<com.android.permissioncontroller.safetycenter.ui.view.MoreIssuesHeaderView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/issue_card"
- style="@style/SafetyCenterMoreIssuesCollapsed"/>
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.permissioncontroller.safetycenter.ui.view.MoreIssuesHeaderView
+ android:id="@+id/more_issues_card"
+ style="@style/SafetyCenterMoreIssuesCollapsed"/>
+</FrameLayout> \ No newline at end of file
diff --git a/PermissionController/res/layout-v33/preference_safety_status.xml b/PermissionController/res/layout-v33/preference_safety_status.xml
index 42bf1c22d..17adc035f 100644
--- a/PermissionController/res/layout-v33/preference_safety_status.xml
+++ b/PermissionController/res/layout-v33/preference_safety_status.xml
@@ -14,7 +14,12 @@
~ limitations under the License.
-->
-<com.android.permissioncontroller.safetycenter.ui.view.StatusCardView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:clickable="false"
- style="@style/SafetyCenterCard.Status" /> \ No newline at end of file
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.permissioncontroller.safetycenter.ui.view.StatusCardView
+ android:id="@+id/status_card"
+ android:clickable="false"
+ style="@style/SafetyCenterCard.Status" />
+</FrameLayout> \ No newline at end of file
diff --git a/PermissionController/res/layout-v35/app_permission_footer_link_preference.xml b/PermissionController/res/layout-v35/app_permission_footer_link_preference.xml
new file mode 100644
index 000000000..75381e762
--- /dev/null
+++ b/PermissionController/res/layout-v35/app_permission_footer_link_preference.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!--
+ ~ A copy of PermissionPreference, with custom styles for some elements
+ -->
+<com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/AppPermissionFooterLinkPreferenceRootLayoutStyle">
+
+ <RelativeLayout
+ style="@style/AppPermissionFooterLinkPreferenceTextLayoutStyle">
+
+ <TextView
+ android:id="@android:id/summary"
+ style="@style/AppPermissionFooterLinkPreferenceSummaryStyle"/>
+
+ </RelativeLayout>
+
+</com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout>
diff --git a/PermissionController/res/layout-v35/permission_preference_selector_with_widget.xml b/PermissionController/res/layout-v35/permission_preference_selector_with_widget.xml
new file mode 100644
index 000000000..230e51fc3
--- /dev/null
+++ b/PermissionController/res/layout-v35/permission_preference_selector_with_widget.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<!-- A direct copy of the following layout, but with overlayable styles:
+ ~ SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
+ -->
+<com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/PermissionSelectorWithWidgetPreferenceRootLayoutStyle">
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ style="@style/PermissionSelectorWithWidgetPreferenceWidgetFrameStyle" />
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ style="@style/PermissionSelectorWithWidgetPreferenceIconFrameStyle">
+
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ style="@style/PermissionSelectorWithWidgetPreferenceIconStyle" />
+ </LinearLayout>
+
+ <LinearLayout style="@style/PermissionSelectorWithWidgetPreferenceTextContainerStyle">
+
+ <TextView
+ android:id="@android:id/title"
+ style="@style/PermissionSelectorWithWidgetPreferenceTitleStyle" />
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ style="@style/PermissionSelectorWithWidgetPreferenceSummaryContainerStyle">
+
+ <TextView
+ android:id="@android:id/summary"
+ style="@style/PermissionSelectorWithWidgetPreferenceSummaryStyle" />
+
+ <TextView
+ android:id="@+id/appendix"
+ style="@style/PermissionSelectorWithWidgetPreferenceAppendixStyle" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/selector_extra_widget_container"
+ style="@style/PermissionSelectorWithWidgetPreferenceExtraWidgetContainerStyle">
+
+ <View style="@style/PermissionSelectorWithWidgetPreferenceExtraWidgetDividerStyle" />
+
+ <ImageView
+ android:id="@+id/selector_extra_widget"
+ android:contentDescription="@string/settings_label"
+ style="@style/PermissionSelectorWithWidgetPreferenceExtraWidgetImageStyle" />
+ </LinearLayout>
+</com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout>
diff --git a/PermissionController/res/layout-v35/permission_preference_two_target.xml b/PermissionController/res/layout-v35/permission_preference_two_target.xml
new file mode 100644
index 000000000..906393b3c
--- /dev/null
+++ b/PermissionController/res/layout-v35/permission_preference_two_target.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<!-- A direct copy of the following layout, but with overlayable styles:
+ ~ SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
+ -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/PermissionTwoTargetPreferenceRootLayoutStyle">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ style="@style/PermissionTwoTargetPreferenceIconFrameStyle">
+
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ style="@style/PermissionTwoTargetPreferenceIconStyle"/>
+
+ </LinearLayout>
+
+ <RelativeLayout style="@style/PermissionTwoTargetPreferenceTextContainerStyle">
+
+ <TextView
+ android:id="@android:id/title"
+ style="@style/PermissionTwoTargetPreferenceTitleStyle"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ style="@style/PermissionTwoTargetPreferenceSummaryStyle"/>
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/two_target_divider"
+ style="@style/PermissionTwoTargetPreferenceDividerContainerStyle">
+ <View style="@style/PermissionTwoTargetPreferenceDividerStyle" />
+ </LinearLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ style="@style/PermissionTwoTargetPreferenceWidgetFrameStyle" />
+
+</com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout>
diff --git a/PermissionController/res/layout-v35/permission_preference_widget_radiobutton.xml b/PermissionController/res/layout-v35/permission_preference_widget_radiobutton.xml
new file mode 100644
index 000000000..10e311110
--- /dev/null
+++ b/PermissionController/res/layout-v35/permission_preference_widget_radiobutton.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<!-- A direct copy of the following layout, but with overlayable styles:
+ ~ SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_radiobutton.xml
+ -->
+<RadioButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/checkbox"
+ style="@style/PermissionSelectorWithWidgetPreferenceWidgetRadioButton" />
diff --git a/PermissionController/res/values-af-v33/strings.xml b/PermissionController/res/values-af-v33/strings.xml
index 6d6a118cd..8bfc6821d 100644
--- a/PermissionController/res/values-af-v33/strings.xml
+++ b/PermissionController/res/values-af-v33/strings.xml
@@ -16,8 +16,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="role_dialer_request_description" msgid="6188305064871543419">"Hierdie program sal toegelaat word om vir jou kennisgewings te stuur, en sal toegang tot jou kamera, kontakte, mikrofoon, foon en SMS\'e kry"</string>
- <string name="role_sms_request_description" msgid="1506966389698625395">"Hierdie program sal toegelaat word om vir jou kennisgewings te stuur, en sal toegang kry tot jou kamera, kontakte, lêers, mikrofoon, foon en SMS\'e"</string>
+ <string name="role_dialer_request_description" msgid="6188305064871543419">"Hierdie app sal toegelaat word om vir jou kennisgewings te stuur, en sal toegang tot jou kamera, kontakte, mikrofoon, foon en SMS\'e kry"</string>
+ <string name="role_sms_request_description" msgid="1506966389698625395">"Hierdie app sal toegelaat word om vir jou kennisgewings te stuur, en sal toegang kry tot jou kamera, kontakte, lêers, mikrofoon, foon en SMS\'e"</string>
<string name="permission_description_summary_storage" msgid="1917071243213043858">"Programme met hierdie toestemming het toegang tot alle lêers op hierdie toestel"</string>
<string name="work_policy_title" msgid="832967780713677409">"Jou werkbeleidinligting"</string>
<string name="work_policy_summary" msgid="3886113358084963931">"Instellings wat deur jou IT-admin bestuur word"</string>
diff --git a/PermissionController/res/values-af/strings.xml b/PermissionController/res/values-af/strings.xml
index 728ef11ef..44b10ebe6 100644
--- a/PermissionController/res/values-af/strings.xml
+++ b/PermissionController/res/values-af/strings.xml
@@ -27,7 +27,7 @@
<string name="on" msgid="280241003226755921">"Aan"</string>
<string name="off" msgid="1438489226422866263">"Af"</string>
<string name="uninstall_or_disable" msgid="4496612999740858933">"Deïnstalleer of deaktiveer"</string>
- <string name="app_not_found_dlg_title" msgid="6029482906093859756">"Program nie gevind nie"</string>
+ <string name="app_not_found_dlg_title" msgid="6029482906093859756">"App nie gevind nie"</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"Moenie toelaat nie"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Moenie toelaat nie en moenie weer vra nie"</string>
<string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Hou \"Terwyl die app gebruik word\""</string>
@@ -44,7 +44,7 @@
<string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> van <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
<string name="permission_warning_template" msgid="2247087781222679458">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
<string name="permission_add_background_warning_template" msgid="1812914855915092273">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; altyd toe om <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
- <string name="allow_permission_foreground_only" msgid="116465816039675404">"Net terwyl program gebruik word"</string>
+ <string name="allow_permission_foreground_only" msgid="116465816039675404">"Net terwyl app gebruik word"</string>
<string name="allow_permission_always" msgid="5194342531206054051">"Altyd"</string>
<string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"Moenie toelaat nie en moenie weer vra nie"</string>
<string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g> is gedeaktiveer"</string>
@@ -80,9 +80,9 @@
<string name="additional_permissions" msgid="5801285469338873430">"Bykomende toestemmings"</string>
<string name="app_permissions_info_button_label" msgid="7633312050729974623">"Maak programinligting oop"</string>
<string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{Nog #}other{Nog #}}"</string>
- <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As toestemming geweier word, kan dit veroorsaak dat dit dalk nie meer soos bedoel werk nie."</string>
- <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As jy hierdie toestemming toelaat, sal toegang tot alle berging (insluitend foto\'s, video\'s, musiek, oudio en ander lêers) toegelaat word."</string>
- <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As jy hierdie toestemming weier, sal toegang tot alle berging (insluitend foto\'s, video\'s, musiek, oudio en ander lêers) geweier word."</string>
+ <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As toestemming geweier word, kan dit veroorsaak dat dit dalk nie meer soos bedoel werk nie."</string>
+ <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As jy hierdie toestemming toelaat, sal toegang tot alle berging (insluitend foto\'s, video\'s, musiek, oudio en ander lêers) toegelaat word."</string>
+ <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As jy hierdie toestemming weier, sal toegang tot alle berging (insluitend foto\'s, video\'s, musiek, oudio en ander lêers) geweier word."</string>
<string name="default_permission_description" msgid="4624464917726285203">"voer \'n onbekende handeling uit"</string>
<string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_0">%1$d</xliff:g> van <xliff:g id="COUNT_1">%2$d</xliff:g> programme toegelaat"</string>
<string name="app_permissions_group_summary2" msgid="4329922444840521150">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g> programme toegelaat"</string>
@@ -95,9 +95,9 @@
<string name="location_settings" msgid="3624412509133422562">"Ligginginstellings"</string>
<string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> is \'n verskaffer van liggingdienste vir hierdie toestel. Liggingtoegang kan vanuit ligginginstellings gewysig word."</string>
<string name="system_warning" msgid="1173400963234358816">"As jy hierdie toestemming weier, sal basiese kenmerke van jou toestel dalk nie meer soos bedoel werk nie."</string>
- <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As jy nie vir hierdie program toegang tot foto\'s en video\'s gee nie, sal dit ook nie toegang tot musiek en ander oudio hê nie."</string>
- <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As jy nie vir hierdie program toegang tot musiek en ander oudio gee nie, sal dit ook nie toegang tot foto\'s en video\'s hê nie."</string>
- <string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"As jy hierdie toestemming weier, sal sommige kenmerke van jou toestel wat deur hierdie program bestuur word dalk nie meer soos bedoel werk nie."</string>
+ <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As jy nie vir hierdie app toegang tot foto\'s en video\'s gee nie, sal dit ook nie toegang tot musiek en ander oudio hê nie."</string>
+ <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As jy nie vir hierdie app toegang tot musiek en ander oudio gee nie, sal dit ook nie toegang tot foto\'s en video\'s hê nie."</string>
+ <string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"As jy hierdie toestemming weier, sal sommige kenmerke van jou toestel wat deur hierdie app bestuur word dalk nie meer soos bedoel werk nie."</string>
<string name="permission_summary_enforced_by_policy" msgid="4443598170942950519">"Afgedwing deur beleid"</string>
<string name="permission_summary_disabled_by_policy_background_only" msgid="221995005556362660">"Agtergrondtoegang is gedeaktiveer volgens beleid"</string>
<string name="permission_summary_enabled_by_policy_background_only" msgid="8287675974767104279">"Agtergrondtoegang is geaktiveer volgens beleid"</string>
@@ -118,12 +118,12 @@
<string name="other_permissions" msgid="2901186127193849594">"Ander programvermoëns"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Toestemmingsversoek"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Kies waartoe &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang mag kry"</string>
- <string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; is opgedateer. Kies waartoe hierdie program toegang mag kry."</string>
+ <string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; is opgedateer. Kies waartoe hierdie app toegang mag kry."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Kanselleer"</string>
<string name="review_button_continue" msgid="2527918375047602199">"Gaan voort"</string>
<string name="new_permissions_category" msgid="552995090178417611">"Nuwe toestemmings"</string>
<string name="current_permissions_category" msgid="4292990083585728880">"Huidige toestemmings"</string>
- <string name="message_staging" msgid="9110563899955511866">"Voer tans program uit …"</string>
+ <string name="message_staging" msgid="9110563899955511866">"Voer tans app uit …"</string>
<string name="app_name_unknown" msgid="1319665005754048952">"Onbekend"</string>
<string name="permission_usage_title" msgid="1568233336351734538">"Privaatheidkontroleskerm"</string>
<string name="auto_permission_usage_summary" msgid="7335667266743337075">"Bekyk watter programme onlangs toestemmings gebruik het"</string>
@@ -133,7 +133,7 @@
<string name="perm_usage_adv_info_summary_more_items" msgid="949055326299562218">"<xliff:g id="PERMGROUP_0">%1$s</xliff:g>, <xliff:g id="PERMGROUP_1">%2$s</xliff:g> en nog <xliff:g id="NUM">%3$s</xliff:g>"</string>
<string name="permission_group_usage_subtitle_24h" msgid="5120155996322114181">"Tydlyn van wanneer programme jou <xliff:g id="PERMGROUP">%1$s</xliff:g> in die afgelope 24 uur gebruik het"</string>
<string name="permission_group_usage_subtitle_7d" msgid="1465828402260324654">"Tydlyn van wanneer programme jou <xliff:g id="PERMGROUP">%1$s</xliff:g> in die afgelope 7 dae gebruik het"</string>
- <string name="permission_usage_access_dialog_subtitle" msgid="4171772805196955753">"Wanneer hierdie program jou <xliff:g id="PERMGROUP">%1$s</xliff:g>-toestemming gebruik het"</string>
+ <string name="permission_usage_access_dialog_subtitle" msgid="4171772805196955753">"Wanneer hierdie app jou <xliff:g id="PERMGROUP">%1$s</xliff:g>-toestemming gebruik het"</string>
<string name="permission_usage_access_dialog_learn_more" msgid="7121468469493184613">"Kom meer te wete"</string>
<string name="learn_more_content_description" msgid="8673699744544502539">"Kom meer te wete oor <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string>
<string name="manage_permission_summary" msgid="4117555482684114317">"Beheer apptoegang tot jou <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string>
@@ -204,17 +204,17 @@
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Sien alle apps met hierdie toestemming"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Wys Assistent-mikrofoongebruik"</string>
<string name="unused_apps_category_title" msgid="2988455616845243901">"Instellings vir ongebruikte apps"</string>
- <string name="auto_revoke_label" msgid="5068393642936571656">"Verwyder toestemmings as program nie gebruik word nie"</string>
+ <string name="auto_revoke_label" msgid="5068393642936571656">"Verwyder toestemmings as app nie gebruik word nie"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Verwyder toestemmings en maak spasie beskikbaar"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Onderbreek appaktiwiteit as ongebruik"</string>
<string name="unused_apps_label_v3" msgid="693340578642156657">"Bestuur app indien ongebruik"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Verwyder toestemmings, vee tydelike lêers uit, en stop kennisgewings"</string>
<string name="unused_apps_summary_v2" msgid="5011313200815115802">"Verwyder toestemmings, vee tydelike lêers uit, stop kennisgewings en argiveer die app"</string>
- <string name="auto_revoke_summary" msgid="5867548789805911683">"Om jou data te beskerm, sal toestemmings vir hierdie program verwyder word as die program \'n paar maande nie gebruik word nie."</string>
+ <string name="auto_revoke_summary" msgid="5867548789805911683">"Om jou data te beskerm, sal toestemmings vir hierdie app verwyder word as die app \'n paar maande nie gebruik word nie."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Om jou data te beskerm, sal die volgende toestemmings verwyder word as dit vir \'n paar maande nie gebruik word nie: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Om jou data te beskerm, is toestemmings verwyder van programme wat jy \'n paar maande gelede laas gebruik het."</string>
- <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"Maak die program oop as jy toestemmings weer wil toelaat."</string>
- <string name="auto_revoke_disabled" msgid="8697684442991567188">"Outomatiese verwydering is tans gedeaktiveer vir hierdie program."</string>
+ <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"Maak die app oop as jy toestemmings weer wil toelaat."</string>
+ <string name="auto_revoke_disabled" msgid="8697684442991567188">"Outomatiese verwydering is tans gedeaktiveer vir hierdie app."</string>
<string name="auto_revocable_permissions_none" msgid="8334929619113991466">"Geen toestemmings wat outomaties herroep kan word, is tans verleen nie"</string>
<string name="auto_revocable_permissions_one" msgid="5299112369449458176">"<xliff:g id="PERM">%1$s</xliff:g> toestemming sal verwyder word."</string>
<string name="auto_revocable_permissions_two" msgid="4874067408752041716">"<xliff:g id="PERM_0">%1$s</xliff:g> en <xliff:g id="PERM_1">%2$s</xliff:g> toestemmings sal verwyder word."</string>
@@ -227,10 +227,10 @@
<string name="unused_apps_page_summary" msgid="1867593913217272155">"As \'n app vir \'n paar maande nie gebruik word nie:\n\n• Word toestemmings verwyder om jou privaatheid te beskerm\n• Word kennisgewings gestop om batterykrag te bespaar\n• Word tydelike lêers verwyder om spasie beskikbaar te maak\n\nMaak die app oop om weer toestemmings en kennisgewings toe te laat."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"As ’n app ’n maand lank nie gebruik is nie:\n\n• Toestemmings word verwyder om jou data te beskerm\n• Tydelike lêers word verwyder om spasie beskikbaar te maak\n\nMaak die app oop om weer toestemmings te gee."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Meer as # maand gelede laas oopgemaak}other{Meer as # maande gelede laas oopgemaak}}"</string>
- <string name="last_opened_summary" msgid="5248984030024968808">"Program is <xliff:g id="DATE">%s</xliff:g> laas oopgemaak"</string>
+ <string name="last_opened_summary" msgid="5248984030024968808">"App is <xliff:g id="DATE">%s</xliff:g> laas oopgemaak"</string>
<string name="last_opened_summary_short" msgid="1646067226191176825">"<xliff:g id="DATE">%s</xliff:g> laas oopgemaak"</string>
- <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"As jy toelaat dat alle lêers bestuur word, kan hierdie program enige lêers in gedeelde bergingspasie op hierdie toestel en gekoppelde bergingtoestelle kry, wysig en uitvee. Die program kan toegang tot lêers kry sonder om jou te vra."</string>
- <string name="special_file_access_dialog" msgid="583804114020740610">"Laat hierdie program toe om lêers op die toestel en enige gekoppelde bergingtoestelle te kry, te wysig en uit te vee? Hierdie program kan toegang tot lêers kry sonder om jou te vra."</string>
+ <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"As jy toelaat dat alle lêers bestuur word, kan hierdie app enige lêers in gedeelde bergingspasie op hierdie toestel en gekoppelde bergingtoestelle kry, wysig en uitvee. Die app kan toegang tot lêers kry sonder om jou te vra."</string>
+ <string name="special_file_access_dialog" msgid="583804114020740610">"Laat hierdie app toe om lêers op die toestel en enige gekoppelde bergingtoestelle te kry, te wysig en uit te vee? Hierdie app kan toegang tot lêers kry sonder om jou te vra."</string>
<string name="permission_description_summary_generic" msgid="5401399408814903391">"Apps met hierdie toestemming <xliff:g id="DESCRIPTION">%1$s</xliff:g>"</string>
<string name="permission_description_summary_activity_recognition" msgid="2652850576497070146">"Programme met hierdie toestemming kan ingaan by fisieke aktiwiteit, soos stap, fietsry, ry, treëtelling, en meer"</string>
<string name="permission_description_summary_calendar" msgid="103329982944411010">"Programme met hierdie toestemming kan toegang tot jou kalender kry"</string>
@@ -264,12 +264,12 @@
<string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuut}other{# minute}}"</string>
<string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekonde}other{# sekondes}}"</string>
<string name="permission_reminders" msgid="6528257957664832636">"Toestemmingonthounotas"</string>
- <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ongebruikte program"</string>
+ <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ongebruikte app"</string>
<string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ongebruikte programme"</string>
<string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Toestemmings is verwyder om jou privaatheid te beskerm. Tik om na te gaan"</string>
<string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Toestemmings vir ongebruikte programme verwyder"</string>
<string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Sommige programme is \'n paar maande laas gebruik. Tik om te na te gaan."</string>
- <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# ongebruikte program}other{# ongebruikte programme}}"</string>
+ <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# ongebruikte app}other{# ongebruikte apps}}"</string>
<string name="unused_apps_notification_content" msgid="9195026773244581246">"Toestemmings en tydelike lêers is verwyder en kennisgewings is gestop. Tik om na te gaan."</string>
<string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Gaan programme na waarvoor toestemmings verwyder is"</string>
<string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Toestemmings en tydelike lêers is verwyder en kennisgewings is gestop vir programme wat jy ’n ruk lank nie gebruik het nie."</string>
@@ -278,7 +278,7 @@
<string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Terwyl jy bestuur het, het jy <xliff:g id="APP">%1$s</xliff:g> toegang gegee tot <xliff:g id="PERMISSION">%2$s</xliff:g>"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Terwyl jy bestuur het, het jy <xliff:g id="APP">%1$s</xliff:g> toegang gegee tot <xliff:g id="PERMISSION_1">%2$s</xliff:g> en <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"Terwyl jy bestuur het, het jy <xliff:g id="COUNT">%1$d</xliff:g> toestemmings aan <xliff:g id="APP">%2$s</xliff:g> gegee"</string>
- <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Terwyl jy bestuur het, het jy <xliff:g id="APP_0">%1$s</xliff:g> en # ander program toegang gegee}other{Terwyl jy bestuur het, het jy <xliff:g id="APP_1">%1$s</xliff:g> en # ander programme toegang gegee}}"</string>
+ <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Terwyl jy bestuur het, het jy <xliff:g id="APP_0">%1$s</xliff:g> en # ander app toegang gegee}other{Terwyl jy bestuur het, het jy <xliff:g id="APP_1">%1$s</xliff:g> en # ander apps toegang gegee}}"</string>
<string name="go_to_settings" msgid="1053735612211228335">"Gaan na Instellings"</string>
<string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Sommige programme is \'n paar maande laas gebruik"</string>
<string name="permissions_removed_category_title" msgid="1064754271178447643">"Toestemmings wat verwyder is"</string>
@@ -287,7 +287,7 @@
<string name="months_ago" msgid="1766026492610646354">"<xliff:g id="COUNT">%1$d</xliff:g> maande gelede"</string>
<string name="auto_revoke_preference_summary" msgid="5517958331781391481">"Toestemmings is verwyder om jou privaatheid te beskerm"</string>
<string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"<xliff:g id="APP_NAME">%s</xliff:g> het jou ligging op die agtergrond gekry"</string>
- <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Hierdie program het altyd toegang tot jou ligging. Tik om te verander."</string>
+ <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Hierdie app het altyd toegang tot jou ligging. Tik om te verander."</string>
<string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Gaan app met toegang tot jou kennisgewings na"</string>
<string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> kan inhoud in jou kennisgewings toemaak, daarop reageer en toegang daartoe kry"</string>
<string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Hierdie app kan inhoud in jou kennisgewings toemaak, daarop reageer en toegang daartoe kry. Sommige apps het hierdie toegang nodig om te werk soos hulle moet."</string>
@@ -303,7 +303,7 @@
<string name="safety_center_notification_app_label" msgid="2457720616141926534">"Android-stelsel"</string>
<string name="auto_revoke_after_notification_title" msgid="5417761027669887431">"Programtoestemmings is verwyder om privaatheid te beskerm"</string>
<string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"<xliff:g id="APP_NAME">%s</xliff:g> is vir \'n paar maande nie gebruik nie. Tik om te kontroleer."</string>
- <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> en 1 ander program is vir \'n paar maande nie gebruik nie. Tik om te kontroleer."</string>
+ <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> en 1 ander app is vir \'n paar maande nie gebruik nie. Tik om te kontroleer."</string>
<string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> en <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> ander programme is vir \'n paar maande nie gebruik nie. Tik om te kontroleer."</string>
<string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"1 app is ongebruik"</string>
<string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> programme is ongebruik"</string>
@@ -315,7 +315,7 @@
<string name="unused_permissions_subtitle_many" msgid="4387289202207450238">"<xliff:g id="PERM_NAME_0">%1$s</xliff:g>, <xliff:g id="PERM_NAME_1">%2$s</xliff:g> en nog <xliff:g id="NUMBER_OF_PERMISSIONS">%3$s</xliff:g>"</string>
<string name="unused_app_permissions_removed_summary" msgid="6779039455326071033">"Toestemmings is verwyder uit programme wat jy vir \'n paar maande nie gebruik het nie om jou data te beskerm"</string>
<string name="unused_app_permissions_removed_summary_some" msgid="5080490037831563441">"Om jou data te beskerm, is toestemmings verwyder uit sekere programme wat jy \'n paar maande gelede laas gebruik het"</string>
- <string name="one_unused_app_summary" msgid="7831913934488881991">"1 program is \'n paar maande gelede laas gebruik"</string>
+ <string name="one_unused_app_summary" msgid="7831913934488881991">"1 app is \'n paar maande gelede laas gebruik"</string>
<string name="num_unused_apps_summary" msgid="1870719749940571227">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> programme is \'n paar maande gelede laas gebruik."</string>
<string name="permission_subtitle_only_in_foreground" msgid="9068389431267377564">"Net terwyl die app gebruik word"</string>
<string name="permission_subtitle_media_only" msgid="8917869683764720717">"Media"</string>
@@ -365,13 +365,13 @@
<string name="role_dialer_short_label" msgid="7186888549465352489">"Foonapp"</string>
<string name="role_dialer_description" msgid="8768708633696539612">"Apps wat jou toelaat om telefoonoproepe op jou toestel te maak en te ontvang"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstekfoonprogram?"</string>
- <string name="role_dialer_request_description" msgid="6288839625724909320">"Hierdie program sal toegang tot jou kamera, kontakte, mikrofoon, foon en SMS\'e kry"</string>
+ <string name="role_dialer_request_description" msgid="6288839625724909320">"Hierdie app sal toegang tot jou kamera, kontakte, mikrofoon, foon en SMS\'e kry"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"beller"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Verstek-SMS-app"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"SMS-app"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Apps wat jou toelaat om jou foonnommer te gebruik om kort SMS\'e, foto\'s, video\'s en meer te stuur en te ontvang"</string>
- <string name="role_sms_request_title" msgid="7953552109601185602">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstek-SMS-program?"</string>
- <string name="role_sms_request_description" msgid="2691004766132144886">"Hierdie program sal toegang tot jou kamera, kontakte, lêers en media, mikrofoon, foon en SMS\'e kry"</string>
+ <string name="role_sms_request_title" msgid="7953552109601185602">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstek-SMS-app?"</string>
+ <string name="role_sms_request_description" msgid="2691004766132144886">"Hierdie app sal toegang tot jou kamera, kontakte, lêers en media, mikrofoon, foon en SMS\'e kry"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"sms, teksboodskappe, boodskappe, boodskappe"</string>
<string name="role_emergency_label" msgid="7028825857206842366">"Versteknoodprogram"</string>
<string name="role_emergency_short_label" msgid="2388431453335350348">"Noodprogram"</string>
@@ -449,7 +449,7 @@
<string name="no_special_app_access" msgid="6950277571805106247">"Geen spesiale apptoegang nie"</string>
<string name="special_app_access_no_apps" msgid="4102911722787886970">"Geen apps nie"</string>
<string name="home_missing_work_profile_support" msgid="1756855847669387977">"Steun nie werkprofiel nie"</string>
- <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Let wel: As jy jou toestel herbegin en \'n skermslot is gestel, kan hierdie program nie begin totdat jy jou toestel ontsluit nie."</string>
+ <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Let wel: As jy jou toestel herbegin en \'n skermslot is gestel, kan hierdie app nie begin totdat jy jou toestel ontsluit nie."</string>
<string name="assistant_confirmation_message" msgid="7476540402884416212">"Die assistent sal inligting oor programme wat tans op jou stelsel gebruik word, kan lees, insluitend inligting wat op jou skerm sigbaar is of toeganklik is binne die programme."</string>
<string name="incident_report_channel_name" msgid="3144954065936288440">"Deel ontfoutingsdata"</string>
<string name="incident_report_notification_title" msgid="4635984625656519773">"Deel gedetailleerde ontfoutingsdata?"</string>
@@ -473,13 +473,13 @@
<string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou kontakte op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot hierdie toestel se ligging?"</string>
<string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; se ligging?"</string>
- <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Die program sal net toegang tot die ligging hê terwyl jy die program gebruik"</string>
+ <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Die app sal net toegang tot die ligging hê terwyl jy die app gebruik"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot hierdie toestel se ligging?"</string>
<string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; se ligging?"</string>
- <string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Hierdie program wil dalk die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Hierdie app wil dalk die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Verander liggingtoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Verander liggingtoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Hierdie program wil die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
+ <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Hierdie app wil die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om toestelle in die omtrek te soek, aan hulle te koppel en hul relatiewe posisie te bepaal?"</string>
<string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestelle in die omtrek op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; soek, aan hulle koppel, en hul relatiewe posisie bepaal?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om toestelle in die omtrek te soek, aan hulle te koppel en hul relatiewe posisie te bepaal? "<annotation id="link">"Laat toe in Instellings."</annotation></string>
@@ -505,24 +505,24 @@
<string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot meer foto’s en video’s op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om oudio op te neem?"</string>
<string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om oudio op te neem op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Die program sal net kan oudio opneem terwyl jy die program gebruik"</string>
+ <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Die app sal net kan oudio opneem terwyl jy die app gebruik"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om oudio op te neem?"</string>
<string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om oudio op te neem op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Hierdie program wil dalk die hele tyd oudio opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
+ <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Hierdie app wil dalk die hele tyd oudio opneem, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Verander mikrofoontoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Verander mikrofoontoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Hierdie program wil die hele tyd oudio opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
+ <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Hierdie app wil die hele tyd oudio opneem, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou fisieke aktiwiteit?"</string>
<string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou fisieke aktiwiteit op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om foto\'s te neem en video\'s op te neem?"</string>
<string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om foto’s te neem en video’s op te neem op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Die program sal net kan foto\'s neem en video\'s opneem terwyl jy die program gebruik"</string>
+ <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Die app sal net kan foto\'s neem en video\'s opneem terwyl jy die app gebruik"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om foto\'s te neem en video\'s op te neem?"</string>
<string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om foto’s te neem en video’s op te neem op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Hierdie program wil dalk die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
+ <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Hierdie app wil dalk die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Verander kameratoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Verander kameratoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Hierdie program wil die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
+ <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Hierdie app wil die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou foonoproeprekords?"</string>
<string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou foon se oproeprekords op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om foonoproepe te maak en te bestuur?"</string>
@@ -532,8 +532,8 @@
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Dié app wil dalk deurentyd toegang tot sensordata oor jou lewenstekens hê, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Gaan na instellings"</annotation>" om hierdie verandering te maak."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot die sensordata oor jou lewenstekens?"</string>
<string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot die sensordata oor jou lewenstekens op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501"><annotation id="link">"Gaan na instellings"</annotation>" om altyd vir hierdie program toegang tot liggaamsensordata te gee, selfs wanneer jy nie die program gebruik nie."</string>
- <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; steeds toegang tot liggaamsensordata terwyl die program gebruik word?"</string>
+ <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501"><annotation id="link">"Gaan na instellings"</annotation>" om altyd vir hierdie app toegang tot liggaamsensordata te gee, selfs wanneer jy nie die app gebruik nie."</string>
+ <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; steeds toegang tot liggaamsensordata terwyl die app gebruik word?"</string>
<string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; steeds toegang tot liggaamsensordata op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; terwyl die app gebruik word?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om vir jou kennisgewings te stuur?"</string>
<string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om vir jou kennisgewings te stuur op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
@@ -553,8 +553,8 @@
<string name="privdash_label_24h" msgid="1512532123865375319">"Afgelope\n24 uur"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Afgelope\n7 dae"</string>
<string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> persent"</string>
- <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie program se toestemminggebruik word nie op die statusbalk of jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string>
- <string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie program se toestemminggebruik word nie op jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string>
+ <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie app se toestemminggebruik word nie op die statusbalk of jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string>
+ <string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie app se toestemminggebruik word nie op jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Toestelkamera is geblokkeer"</string>
<string name="blocked_microphone_title" msgid="1631517143648232585">"Toestelmikrofoon is geblokkeer"</string>
<string name="blocked_location_title" msgid="2005608279812892383">"Toestelligging is af"</string>
@@ -588,8 +588,8 @@
<string name="permissions_removed_qs" msgid="8957319130625294572">"Toestemming is verwyder"</string>
<string name="camera_usage_qs" msgid="4394233566086665994">"Sien onlangse kameragebruik"</string>
<string name="microphone_usage_qs" msgid="8527666682168170417">"Sien onlangse mikrofoongebruik"</string>
- <string name="remove_camera_qs" msgid="3649996161066883350">"Verwyder toestemming vir hierdie program"</string>
- <string name="remove_microphone_qs" msgid="1276551965129953198">"Verwyder toestemming vir hierdie program"</string>
+ <string name="remove_camera_qs" msgid="3649996161066883350">"Verwyder toestemming vir hierdie app"</string>
+ <string name="remove_microphone_qs" msgid="1276551965129953198">"Verwyder toestemming vir hierdie app"</string>
<string name="manage_service_qs" msgid="7862555549364153805">"Bestuur diens"</string>
<string name="manage_permissions_qs" msgid="3780541819763475434">"Bestuur toestemmings"</string>
<string name="active_call_usage_qs" msgid="8559974395932523391">"Word tans gebruik deur foonoproep"</string>
@@ -610,14 +610,14 @@
<string name="media_confirm_dialog_title_q_to_s_aural_deny" msgid="3128147568953297969">"Toegang tot foto\'s en video\'s sal ook nie toegelaat word"</string>
<string name="media_confirm_dialog_title_q_to_s_visual_allow" msgid="6310682466493330434">"Toegang tot musiek- en oudiolêers sal ook toegelaat word"</string>
<string name="media_confirm_dialog_title_q_to_s_visual_deny" msgid="1123845663785900471">"Toegang tot musiek- en oudiolêers sal ook nie toegelaat word nie"</string>
- <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program toegang tot musiek- en oudiolêers het, sal dit ook toegelaat word om toegang tot foto\'s, video\'s en ander lêers te kry."</string>
- <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s, video\'s en ander lêers te kry nie."</string>
- <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program toegang tot foto\'s en video\'s het, sal dit ook toegelaat word om toegang tot musiek-, oudio- en ander lêers te kry."</string>
- <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program nie toegang tot foto\'s en video\'s het nie, sal dit ook nie toegelaat word om toegang tot musiek-, oudio- en ander lêers te kry nie."</string>
- <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program toegang tot musiek en oudiolêers het, sal dit ook toegelaat word om toegang tot foto\'s en video\'s te kry."</string>
- <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s en video\'s te kry nie."</string>
- <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program toegang tot foto\'s en video\'s het, sal dit ook toegelaat word toegang tot musiek-, oudio- en ander lêers te kry."</string>
- <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s en video\'s te kry nie."</string>
+ <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app toegang tot musiek- en oudiolêers het, sal dit ook toegelaat word om toegang tot foto\'s, video\'s en ander lêers te kry."</string>
+ <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s, video\'s en ander lêers te kry nie."</string>
+ <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app toegang tot foto\'s en video\'s het, sal dit ook toegelaat word om toegang tot musiek-, oudio- en ander lêers te kry."</string>
+ <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app nie toegang tot foto\'s en video\'s het nie, sal dit ook nie toegelaat word om toegang tot musiek-, oudio- en ander lêers te kry nie."</string>
+ <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app toegang tot musiek en oudiolêers het, sal dit ook toegelaat word om toegang tot foto\'s en video\'s te kry."</string>
+ <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s en video\'s te kry nie."</string>
+ <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app toegang tot foto\'s en video\'s het, sal dit ook toegelaat word toegang tot musiek-, oudio- en ander lêers te kry."</string>
+ <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s en video\'s te kry nie."</string>
<string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"Gaan app met agtergrondliggingtoegang na"</string>
<string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> het altyd toegang tot jou ligging, selfs wanneer die app toe is"</string>
<string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Gaan app met agtergrondliggingtoegang na"</string>
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Laat beperkte instellings toe"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Beperkte instelling"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Hierdie instelling is vir jou veiligheid tans onbeskikbaar."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Handeling nie beskikbaar tydens ’n foonoproep nie"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Dit word nie toegelaat om tydens ’n foonoproep apps toe te laat om ander apps te installeer nie.\n\n Swendelaars versoek dikwels hierdie soort handeling tydens foonoproepgesprekke en dis dus geblokkeer om jou te beskerm. As jy aangesê word om hierdie handeling te doen deur iemand wat jy nie ken nie, kan dit ’n swendelary wees."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Toegang tot <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> is geweier vir die app"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Die app het toegang tot toestemming vir sensitiewe inligting versoek wat jou persoonlike en finansiële inligting in gevaar kan stel.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Dit is moontlik dat die app nie reg sal werk sonder hierdie beperkte toestemming nie. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Kry meer inligting oor hoe om toegang toe te laat&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Toegang is geweier vir app om die verstek <xliff:g id="ROLE_NAME">%1$s</xliff:g> te wees"</string>
diff --git a/PermissionController/res/values-am/strings.xml b/PermissionController/res/values-am/strings.xml
index f49db7957..e1f87b92c 100644
--- a/PermissionController/res/values-am/strings.xml
+++ b/PermissionController/res/values-am/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"የተገደቡ ቅንብሮችን ፍቀድ"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"የተገደበ ቅንብር"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ለእርስዎ ደህንነት ሲባል ይህ ቅንብር በአሁኑ ጊዜ አይገኝም።"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"በስልክ ጥሪ ላይ ሳለ እርምጃ አይገኝም"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"መተግበሪያዎች በስልክ ጥሪ ወቅት ሌሎች መተግበሪያዎችን እንዲጭኑ መፍቀድ አይፈቀድም።\n\n አጭበርባሪዎች ብዙ ጊዜ እንደዚህ ዓይነት እርምጃ በስልክ ጥሪ ውይይቶች ወቅት ይጠይቃሉ፣ ስለዚህ እርስዎን ለመጠበቅ ታግዷል። ይህን እርምጃ በማያውቁት ሰው እንዲወስዱ እየተመሩ ከሆነ፣ ማጭበርበር ሊሆን ይችላል።"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"መተግበሪያ የ<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> መዳረሻ ተከልክሏል"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"መተግበሪያው የእርስዎን የግል እና የፋይናንስ መረጃ አደጋ ላይ የሚጥል አደገኛ ፈቃድ እንዲደርስ ጠይቋል።<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ያለዚህ የተገደበ ፈቃድ መተግበሪያው በትክክል ላይሰራ ይችላል። &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;እንዴት መዳረሻን እንደሚፈቅዱ ይረዱ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"መተግበሪያ ነባሪ <xliff:g id="ROLE_NAME">%1$s</xliff:g> የመሆን መዳረሻ ተከልክሏል"</string>
diff --git a/PermissionController/res/values-ar/strings.xml b/PermissionController/res/values-ar/strings.xml
index 90fce2d06..0b463c09d 100644
--- a/PermissionController/res/values-ar/strings.xml
+++ b/PermissionController/res/values-ar/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"السماح بالإعدادات المحظورة"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"الإعداد محظور"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"للحفاظ على أمانك، هذا الإعداد غير متوفِّر حاليًا."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"لا يتوفّر هذا الإجراء أثناء إجراء مكالمة هاتفية"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"لا يُسمح للتطبيقات بتثبيت تطبيقات أخرى أثناء إجراء مكالمة هاتفية.\n\n يطلب المخادِعون غالبًا هذا النوع من الإجراءات أثناء المكالمات الهاتفية، لذا تم حظره لحماية بياناتك. إذا طلبَ منك شخص غير معروف اتّخاذ هذا الإجراء، قد يكون ذلك عملية خداع."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"تم منع التطبيق من الوصول إلى <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"‏طلب التطبيق أحد أذونات الوصول إلى المعلومات الحساسة، ما قد يعرّض معلوماتك الشخصية والمالية للخطر.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>من المحتمل ألا يعمل التطبيق بشكل صحيح إذا لم يحصل على هذا الإذن المقيَّد. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;مزيد من المعلومات حول كيفية منح الأذونات&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"لم يُسمَح للتطبيق بأن يكون <xliff:g id="ROLE_NAME">%1$s</xliff:g> التلقائي"</string>
diff --git a/PermissionController/res/values-as/strings.xml b/PermissionController/res/values-as/strings.xml
index ef3d28e5a..2e6c890b0 100644
--- a/PermissionController/res/values-as/strings.xml
+++ b/PermissionController/res/values-as/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"প্ৰতিবন্ধিত ছেটিঙৰ অনুমতি দিয়ক"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"প্ৰতিবন্ধিত ছেটিং"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"আপোনাৰ সুৰক্ষাৰ বাবে, এই ছেটিংটো বৰ্তমান উপলব্ধ নহয়।"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ফ’ন কল চলি থকাৰ সময়ত এই কাৰ্যটো কৰিব নোৱাৰি"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ফ’ন কল চলি থকাৰ সময়ত এপক অন্য এপ্‌ ইনষ্টল কৰিবলৈ দিয়াৰ অনুমতি নাই।\n\n ফ’ন কল চলি থকাৰ সময়ত প্ৰৱঞ্চকে প্ৰায়েই এই ধৰণৰ কাৰ্য কৰিবলৈ অনুৰোধ কৰে, গতিকে আপোনাক সুৰক্ষিত কৰিবলৈ এয়া অৱৰোধ কৰা হয়। যদি আপোনাক আপুনি চিনি নোপোৱা কোনো লোকে এই কাৰ্যটো কৰিবলৈ নিৰ্দেশনা দিয়ে, সেয়া প্ৰৱঞ্চনা হ’ব পাৰে।"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"এপ্‌টোক <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>ৰ এক্সেছ প্ৰত্যাখ্যান কৰা হৈছে"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"এপ্‌টোৱে সংবেদনশীল অনুমতি এক্সেছ কৰিবলৈ অনুৰোধ কৰিছে, যিটোৱে আপোনাৰ ব্যক্তিগত আৰু বিত্তীয় তথ্য বিপদাপন্ন কৰিব পাৰে।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>এপ্‌টোৱে এই প্ৰতিবন্ধিত অনুমতিটোৰ অবিহনে সঠিককৈ কাম নকৰাটো সম্ভৱ হ’ব পাৰে। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;কেনেকৈ এক্সেছৰ অনুমতি দিব লাগে জানক&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"এপ্‌টোক ডিফ’ল্ট <xliff:g id="ROLE_NAME">%1$s</xliff:g>ৰ এক্সেছ প্ৰত্যাখ্যান কৰা হৈছে"</string>
diff --git a/PermissionController/res/values-az/strings.xml b/PermissionController/res/values-az/strings.xml
index a33e4c27e..4a7ddc91f 100644
--- a/PermissionController/res/values-az/strings.xml
+++ b/PermissionController/res/values-az/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Məhdudlaşdırılmış ayarlara icazə verin"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Məhdudlaşdırılmış ayar"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Güvənlik üçün bu ayar əlçatan deyil."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Telefon zəngi zamanı əməliyyat mümkün deyil"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefon zəngi zamanı tətbiqlərin digər tətbiqləri quraşdırmağına icazə verilmir.\n\n Fırıldaqçılar, adətən, telefon danışıqları zamanı bu cür əməliyyatları həyata keçirməyinizi tələb edirlər, ona görə də sizi qorumaq bloklanıb. Tanımadığınız şəxs tərəfindən bu əməliyyatları həyata keçirməyiniz istənilsə, bu, fırıldaqçılıq ola bilər."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Tətbiqə <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> icazəsinə giriş verilmədi"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Tətbiq şəxsi və maliyyə məlumatlarınızı riskə ata biləcək həssas məlumat icazəsinə giriş istədi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Bu məhdud icazə olmadan tətbiq düzgün işləməyə bilər. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Girişə icazə vermək haqqında ətraflı&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Tətbiqə defolt <xliff:g id="ROLE_NAME">%1$s</xliff:g> roluna giriş verilmədi"</string>
diff --git a/PermissionController/res/values-b+sr+Latn/strings.xml b/PermissionController/res/values-b+sr+Latn/strings.xml
index 75e5a94d3..27d37ce41 100644
--- a/PermissionController/res/values-b+sr+Latn/strings.xml
+++ b/PermissionController/res/values-b+sr+Latn/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Dozvoli ograničena podešavanja"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ograničeno podešavanje"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ovo podešavanje je trenutno nedostupno radi vaše bezbednosti."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Radnja nije dostupna tokom telefonskog poziva"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Nije dozvoljeno da aplikacije instaliraju druge aplikacije tokom telefonskog poziva.\n\n Prevaranti često traže ovaj tip radnje tokom telefonskog poziva, pa smo je blokirali da bismo vas zaštitili. Ako vas neko koga ne poznajete upućuje na ovu radnju, to bi mogla da bude prevara."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji nije dozvoljen pristup dozvoli: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup osetljivoj dozvoli, što može da ugrozi bezbednost ličnih i finansijskih podataka.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikacija možda neće raditi ispravno bez ove ograničene dozvole. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako da dozvolite pristup&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji nije dozvoljen pristup da postane podrazumevana: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-be/strings.xml b/PermissionController/res/values-be/strings.xml
index b009b68d3..e02518d3d 100644
--- a/PermissionController/res/values-be/strings.xml
+++ b/PermissionController/res/values-be/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Дазволіць абмежаваныя налады"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Налада з абмежаваным доступам"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"У мэтах бяспекі гэта налада цяпер недаступная."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Дзеянне недаступнае падчас тэлефоннага выкліку"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Падчас тэлефоннага выкліку забараняецца даваць дазвол праграмам усталёўваць іншыя праграмы.\n\n У тэлефонных размовах махляры часта просяць усталяваць праграмы, таму ў мэтах бяспекі мы блакіруем такія дзеянні. Запыт ад невядомай вам асобы выканаць падобнае дзеянне можа расцэньвацца як махлярства."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Праграме адмоўлена ў дазволе \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\""</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Праграма запытала дазвол на доступ да канфідэнцыяльнай інфармацыі. Калі вы яго дасце, ваша асабістая і фінансавая інфармацыя можа аказацца ў небяспецы.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Магчыма, без гэтага абмежаванага дазволу праграма не будзе працаваць належным чынам. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Даведацца, як дазволіць доступ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Праграме адмоўлена ў дазволе стандартна выконваць наступную ролю: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-bg/strings.xml b/PermissionController/res/values-bg/strings.xml
index 9bd171db2..f60db861f 100644
--- a/PermissionController/res/values-bg/strings.xml
+++ b/PermissionController/res/values-bg/strings.xml
@@ -674,9 +674,11 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Разрешаване на ограничените настройки"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ограничена настройка"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"От съображения за сигурност понастоящем тази настройка не е налице."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Действието не е налице по време на тел. обаждане"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"По време на телефонно обаждане не можете да разрешавате на приложенията да инсталират други приложения.\n\nТова действие е блокирано от съображения за сигурност, защото често се използва от измамници по време на телефонни разговори. Ако човек, когото не познавате, ви напътства, за да предприемете това действие, възможно е да става дума за измама."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"На приложението бе отказан достъп до <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Приложението поиска разрешение за достъп до поверителни данни, което може да изложи на риск личната или финансовата ви информация.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Възможно е приложението да не работи правилно без това ограничено разрешение. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Научете как да разрешите достъпа&lt;/a&gt;"</string>
- <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"На приложението бе забранено да изпълнява функциите на основно <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Приложението не получи разрешение да бъде по подразбиране: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Приложението поиска разрешения за достъп до поверителни данни, които може да изложат на риск личната или финансовата ви информация.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Възможно е приложението да не работи правилно без тези ограничени разрешения. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Научете как да разрешите достъпа&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"На приложението бе отказан достъп"</string>
<string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Достъпът до това разрешение може да изложи на риск личната и финансовата ви информация.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Възможно е приложението да не работи правилно без това ограничено разрешение. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Научете как да разрешите достъпа&lt;/a&gt;"</string>
diff --git a/PermissionController/res/values-bn/strings.xml b/PermissionController/res/values-bn/strings.xml
index 4d7e6a55e..27c5f85cc 100644
--- a/PermissionController/res/values-bn/strings.xml
+++ b/PermissionController/res/values-bn/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"বিধিনিষেধযুক্ত সেটিংসের অনুমতি দিন"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"বিধিনিষেধযুক্ত সেটিংস"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"আপনার নিরাপত্তার জন্য, এই সেটিং বর্তমানে উপলভ্য নেই।"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ফোন কলের সময় অ্যাকশন নেওয়া যাবে না"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ফোন কলের সময় অ্যাপকে অন্য অ্যাপ ইনস্টল করার অনুমতি দেওয়া হয় না।\n\n ফোন কলের সময় স্ক্যামার প্রায়ই এই ধরনের অ্যাকশনের অনুরোধ করেন, তাই আপনাকে সুরক্ষিত রাখতে এটি ব্লক করা হয়েছে। আপনি চেনেন না এমন কেউ আপনাকে এই অ্যাকশন নিতে বললে, সেটি স্ক্যাম হতে পারে।"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"অ্যাপকে <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> অ্যাক্সেস করতে দেওয়া হয়নি"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"অ্যাপটি সংবেদনশীল অনুমতি অ্যাক্সেস করার অনুরোধ জানিয়েছে, যার জন্য আপনার ব্যক্তিগত ও আর্থিক অবস্থা সম্পর্কিত তথ্যের ক্ষেত্রে ঝুঁকির সম্ভাবনা থাকতে পারে।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>এই সীমাবদ্ধ অনুমতি ছাড়া অ্যাপটি সঠিকভাবে কাজ না করার সম্ভাবনাও আছে। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;কীভাবে অ্যাক্সেস করার অনুমতি দেবেন সেই সম্পর্কে জানুন&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"অ্যাপকে ডিফল্ট <xliff:g id="ROLE_NAME">%1$s</xliff:g> হিসেবে কাজ করার অ্যাক্সেস দেওয়া হয়নি"</string>
diff --git a/PermissionController/res/values-bs/strings.xml b/PermissionController/res/values-bs/strings.xml
index fe036961d..d8b396ec5 100644
--- a/PermissionController/res/values-bs/strings.xml
+++ b/PermissionController/res/values-bs/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Dozvoli ograničene postavke"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ograničena postavka"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Radi vaše sigurnosti postavka trenutno nije dostupna."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Radnja nije dostupna tokom telefonskog poziva"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Dozvoljavanje aplikacijama da instaliraju druge aplikacije nije dozvoljeno tokom telefonskog poziva.\n\n Prevaranti često traže ovu vrstu radnje tokom telefonskih razgovora, pa ovo blokiramo da vas zaštitimo. Ako neko koga ne poznajete traži da poduzmete ovu radnju, to može biti prevara."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji je odbijen pristup odobrenju <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup osjetljivom odobrenju, što može ugroziti vaše lične i finansijske informacije.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće ispravno raditi bez ovog ograničenog odobrenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako dozvoliti pristup&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je odbijen pristup da bude zadana <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-ca/strings.xml b/PermissionController/res/values-ca/strings.xml
index 1de8e07e6..720c0161d 100644
--- a/PermissionController/res/values-ca/strings.xml
+++ b/PermissionController/res/values-ca/strings.xml
@@ -674,12 +674,14 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Permet la configuració restringida"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Opció de configuració restringida"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Per a la teva seguretat, aquesta opció de configuració no està disponible en aquests moments."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Acció no disponible durant les trucades"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"No es permet que les aplicacions instal·lin altres aplicacions durant una trucada.\n\n Els estafadors solen demanar aquest tipus d\'acció durant les trucades, de manera que s\'ha bloquejat per protegir-te. Si algú que no coneixes t\'indica que facis aquesta acció, pot ser que es tracti d\'una estafa."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"A l\'aplicació se li ha denegat l\'accés a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'aplicació ha demanat accés a un permís sensible, el qual pot posar en risc la teva informació personal o financera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>És possible que l\'aplicació no funcioni correctament sense aquest permís restringit. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Més informació sobre com pots permetre l\'accés&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"A l\'aplicació se li ha denegat l\'accés per ser <xliff:g id="ROLE_NAME">%1$s</xliff:g> de manera predeterminada"</string>
<string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"L\'aplicació ha demanat accés a permisos sensibles, els quals poden posar en risc la teva informació personal o financera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>És possible que l\'aplicació no funcioni correctament sense aquests permisos restringits. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Més informació sobre com pots permetre l\'accés&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"A l\'aplicació se li ha denegat l\'accés"</string>
- <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"L\'accés a aquest permís pot posar en risc la teva informació personal i financera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>És possible que l\'aplicació no funcioni correctament sense aquest permís. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Més informació sobre com pots permetre l\'accés&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"L\'accés a aquest permís pot posar en risc la teva informació personal i financera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>És possible que l\'aplicació no funcioni correctament sense aquest permís restringit. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Més informació sobre com pots permetre l\'accés&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Més informació"</string>
<string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"D\'acord"</string>
<string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"S\'ha suprimit la sol·licitud de permís"</string>
diff --git a/PermissionController/res/values-cs/strings.xml b/PermissionController/res/values-cs/strings.xml
index 48996724a..95eb6a558 100644
--- a/PermissionController/res/values-cs/strings.xml
+++ b/PermissionController/res/values-cs/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Povolit omezená nastavení"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Blokované nastavení"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Z důvodu vaší bezpečnosti toto nastavení momentálně není dostupné."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Akce není během telefonního hovoru k dispozici"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Během telefonního hovoru nelze aplikacím povolit instalovat další aplikace.\n\n O tento typ akce během hovorů často žádají podvodníci. Kvůli vaší ochraně je proto blokována. Pokud vás k této akci navádí někdo, koho neznáte, může se jednat o podvod."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaci byl odepřen přístup k oprávnění <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikace požádala o přístup k citlivému oprávnění, které může ohrozit vaše osobní a finanční údaje.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Je možné, že aplikace bez tohoto oprávnění nebude fungovat správně. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Jak povolit přístup&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaci byla odepřena role výchozí <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-da/strings.xml b/PermissionController/res/values-da/strings.xml
index 6a2456bd6..49746b159 100644
--- a/PermissionController/res/values-da/strings.xml
+++ b/PermissionController/res/values-da/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Tillad begrænsede indstillinger"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Begrænset indstilling"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Af hensyn til din sikkerhed er denne indstilling i øjeblikket ikke tilgængelig."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Handlingen er utilgængelig under et telefonopkald"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Det er ikke tilladt at give apps tilladelse til at installere andre apps under et telefonopkald.\n\n Svindlere opfordrer ofte til denne type handling under telefonopkald, så den er blokeret for at beskytte dig. Hvis du bliver bedt om at foretage denne handling af en person, du ikke kender, kan det være svindel."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Appen blev nægtet adgang til <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Appen anmodede om adgang til en følsom tilladelse, der kan kompromittere dine personlige eller økonomiske oplysninger.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Appen fungerer muligvis ikke korrekt uden denne begrænsede tilladelse. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Se, hvordan du giver tilladelse&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appen blev nægtet adgang til at være <xliff:g id="ROLE_NAME">%1$s</xliff:g> som standard"</string>
diff --git a/PermissionController/res/values-de/strings.xml b/PermissionController/res/values-de/strings.xml
index efb3062d6..bf6047e5c 100644
--- a/PermissionController/res/values-de/strings.xml
+++ b/PermissionController/res/values-de/strings.xml
@@ -370,7 +370,7 @@
<string name="role_sms_label" msgid="8456999857547686640">"Standard-SMS-App"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"SMS-App"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Apps, mit denen du über deine Telefonnummer unter anderem SMS, Fotos oder Videos senden und empfangen kannst"</string>
- <string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g>als Standard-SMS-App festlegen?"</string>
+ <string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g> als Standard-SMS-App festlegen?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Diese App erhält Zugriff auf Folgendes: Kamera, Kontakte, Mikrofon, Dateien und Medien, Telefon und SMS"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"textnachricht, sms, sms schicken, sms senden, nachrichten, mms"</string>
<string name="role_emergency_label" msgid="7028825857206842366">"Standardmäßige Notfall-App"</string>
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Eingeschränkte Einstellungen zulassen"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Eingeschränkte Einstellung"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Aus Sicherheitsgründen ist diese Einstellung derzeit nicht verfügbar."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Aktion während Anrufen nicht verfügbar"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Während eines Anrufs kannst du Apps nicht erlauben, andere Apps zu installieren.\n\n Betrüger versuchen oft, dich am Telefon genau dazu zu bringen, daher haben wir diese Art von Aktion zu deinem Schutz blockiert. Drängt dich jemand Unbekanntes zu einer solchen Installation, könnte es sich um Betrug handeln."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App wurde Zugriff auf „<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>“ verweigert"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Die App hat Zugriff auf eine vertrauliche Berechtigung angefordert. Wenn du diesen zulässt, sind deine privaten Daten und Finanzdaten eventuell gefährdet.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Die App funktioniert ohne diese eingeschränkte Berechtigung aber möglicherweise nicht richtig. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Informationen dazu, wie du den Zugriff erlaubst&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App wurde der Zugriff verweigert, die standardmäßige <xliff:g id="ROLE_NAME">%1$s</xliff:g> zu sein"</string>
diff --git a/PermissionController/res/values-el/strings.xml b/PermissionController/res/values-el/strings.xml
index e000a2ac2..ed69ea315 100644
--- a/PermissionController/res/values-el/strings.xml
+++ b/PermissionController/res/values-el/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Επιτρέπονται περιορισμένες ρυθμίσεις"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Περιορισμένη ρύθμιση"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Για την ασφάλειά σας, αυτή η ρύθμιση δεν είναι διαθέσιμη αυτή τη στιγμή."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Δεν διατίθεται κατά τη διάρκεια τηλεφωνικής κλήσης"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Κατά τη διάρκεια μιας τηλεφωνικής κλήσης, δεν επιτρέπεται η παραχώρηση άδειας σε μια εφαρμογή για εγκατάσταση άλλων εφαρμογών.\n\n Οι απατεώνες συχνά ζητούν να κάνετε τέτοιες ενέργειες κατά τη διάρκεια τηλεφωνικών κλήσεων, επομένως η ενέργεια έχει αποκλειστεί για την προστασία σας. Αν κάποιος που δεν γνωρίζετε σας ζητάει να πραγματοποιήσετε αυτή την ενέργεια, ενδέχεται να πρόκειται για απάτη."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Απορρίφθηκε η πρόσβαση της εφαρμογής στην άδεια <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Η εφαρμογή ζήτησε πρόσβαση σε μια άδεια πρόσβασης σε ευαίσθητες πληροφορίες, γεγονός που μπορεί να θέσει σε κίνδυνο τα προσωπικά και οικονομικά στοιχεία σας.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Είναι πιθανό η εφαρμογή να μην λειτουργεί σωστά χωρίς αυτή την περιορισμένη άδεια. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Μάθετε πώς μπορείτε να επιτρέψετε την πρόσβαση&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Δεν επιτράπηκε στην εφαρμογή να οριστεί ως η προεπιλεγμένη <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-en-rAU/strings.xml b/PermissionController/res/values-en-rAU/strings.xml
index d15cb526b..d5a94de2f 100644
--- a/PermissionController/res/values-en-rAU/strings.xml
+++ b/PermissionController/res/values-en-rAU/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Allow restricted settings"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action not available while on a phone call"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Allowing apps to install other apps is not allowed during a phone call.\n\n Scammers often request this type of action during phone call conversations, so it\'s blocked to protect you. If you are being guided to take this action by someone whom you don\'t know, it might be a scam."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-en-rCA/strings.xml b/PermissionController/res/values-en-rCA/strings.xml
index bf9058a8c..d39bcf96a 100644
--- a/PermissionController/res/values-en-rCA/strings.xml
+++ b/PermissionController/res/values-en-rCA/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Allow restricted settings"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action not available while on a phone call"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Allowing apps to install other apps is not allowed during a phone call.\n\n Scammers often request this type of action during phone call conversations, so it’s blocked to protect you. If you are being guided to take this action by someone you don’t know, it might be a scam."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-en-rGB/strings.xml b/PermissionController/res/values-en-rGB/strings.xml
index 93508b04a..f9a015705 100644
--- a/PermissionController/res/values-en-rGB/strings.xml
+++ b/PermissionController/res/values-en-rGB/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Allow restricted settings"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action not available while on a phone call"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Allowing apps to install other apps is not allowed during a phone call.\n\n Scammers often request this type of action during phone call conversations, so it\'s blocked to protect you. If you are being guided to take this action by someone whom you don\'t know, it might be a scam."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-en-rIN/strings.xml b/PermissionController/res/values-en-rIN/strings.xml
index 93508b04a..f9a015705 100644
--- a/PermissionController/res/values-en-rIN/strings.xml
+++ b/PermissionController/res/values-en-rIN/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Allow restricted settings"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action not available while on a phone call"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Allowing apps to install other apps is not allowed during a phone call.\n\n Scammers often request this type of action during phone call conversations, so it\'s blocked to protect you. If you are being guided to take this action by someone whom you don\'t know, it might be a scam."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-es-rUS/strings.xml b/PermissionController/res/values-es-rUS/strings.xml
index 1247b2d3e..4041247fe 100644
--- a/PermissionController/res/values-es-rUS/strings.xml
+++ b/PermissionController/res/values-es-rUS/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir configuración restringida"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Parámetro restringido"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Por seguridad, este parámetro de configuración no está disponible actualmente."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"La acción no está disponible durante una llamada telefónica"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"No se permite que las apps instalen otras apps durante una llamada telefónica.\n\n Esto se debe a que los estafadores suelen solicitar este tipo de acciones durante conversaciones telefónicas. Si alguien que no conoces te indica que realices esta acción, es posible que se trate de una estafa."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"A la app se le negó el acceso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"La app solicitó acceso a un permiso sensible, lo que puede poner en riesgo tu información financiera y personal.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Es posible que la app no funcione como corresponde sin este permiso restringido. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Descubre cómo permitir el acceso&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"A la app se le negó el acceso para usarse como <xliff:g id="ROLE_NAME">%1$s</xliff:g> de forma predeterminada"</string>
diff --git a/PermissionController/res/values-es/strings.xml b/PermissionController/res/values-es/strings.xml
index 402bd5214..d73329ef8 100644
--- a/PermissionController/res/values-es/strings.xml
+++ b/PermissionController/res/values-es/strings.xml
@@ -256,7 +256,7 @@
<string name="allowed_storage_scoped" msgid="5383645873719086975">"Pueden acceder solo al contenido multimedia"</string>
<string name="allowed_storage_full" msgid="5356699280625693530">"Pueden gestionar todos los archivos"</string>
<string name="ask_header" msgid="2633816846459944376">"Preguntar siempre"</string>
- <string name="denied_header" msgid="903209608358177654">"No permitidas"</string>
+ <string name="denied_header" msgid="903209608358177654">"No permitido"</string>
<string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ver más aplicaciones que pueden acceder a todos los archivos"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 día}many{# días}other{# días}}"</string>
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir ajustes restringidos"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ajuste restringido"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Por seguridad, este ajuste no está disponible actualmente."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Acción no disponible durante una llamada"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"No es posible permitir que las aplicaciones instalen otras aplicaciones durante una llamada telefónica.\n\n Los estafadores suelen solicitar este tipo de acciones durante las conversaciones telefónicas, por lo que esta acción se bloquea para protegerte. Si alguien que no conoces te indica que debes realizar esta acción, podría tratarse de una estafa."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Se ha denegado el acceso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> a la aplicación"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"La aplicación ha solicitado acceso a un permiso sensible que puede poner en riesgo tu información personal y financiera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Es posible que la aplicación no funcione correctamente sin este permiso restringido. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Consulta cómo permitir el acceso&lt;/a&gt;."</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Se ha denegado el acceso para que la aplicación sea <xliff:g id="ROLE_NAME">%1$s</xliff:g> predeterminada"</string>
diff --git a/PermissionController/res/values-et/strings.xml b/PermissionController/res/values-et/strings.xml
index 4d93ac4c0..7622870c7 100644
--- a/PermissionController/res/values-et/strings.xml
+++ b/PermissionController/res/values-et/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Piiratud seadete lubamine"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Piiratud seade"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Teie turvalisuse huvides pole see seade praegu saadaval."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Toiming pole telefonikõne ajal saadaval"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefonikõne ajal pole lubatud anda rakendustele luba installida teisi rakendusi.\n\n Petised taotlevad telefonivestluse ajal sageli seda tüüpi toimingut, mistõttu on see teie kaitsmiseks blokeeritud. Kui keegi, keda te ei tea, suunab teid seda toimingut tegema, võib tegu olla pettusega."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Rakendusele ei antud luba <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Rakendus taotles tundlikku luba, mis võib teie isikuandmed ja finantsteabe ohtu seada.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Võimalik, et rakendus ei tööta ilma selle piiratud loata korralikult. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt; Teave juurdepääsu andmise kohta&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Rakendusele ei antud luba olla vaikimisi <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-eu/strings.xml b/PermissionController/res/values-eu/strings.xml
index 139f41668..4e81fb5f1 100644
--- a/PermissionController/res/values-eu/strings.xml
+++ b/PermissionController/res/values-eu/strings.xml
@@ -78,7 +78,7 @@
<string name="never_ask_again" msgid="4728762438198560329">"Ez galdetu berriro"</string>
<string name="no_permissions" msgid="3881676756371148563">"Ez dago baimenik"</string>
<string name="additional_permissions" msgid="5801285469338873430">"Baimen gehigarriak"</string>
- <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Ireki aplikazioaren informazioa"</string>
+ <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Ireki aplikazioari buruzko informazioa"</string>
<string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{Beste #}other{Beste #}}"</string>
<string name="old_sdk_deny_warning" msgid="2382236998845153919">"Android-en bertsio zaharrago baterako diseinatuta dago aplikazio hau. Baimena ukatzen baduzu, agian aurrerantzean ez du behar bezala funtzionatuko."</string>
<string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Android-en bertsio zaharrago baterako dago diseinatuta aplikazio hau. Baimena ematen baduzu, biltegi osoa erabiltzeko baimena emango da (argazkiak, musika, audioa eta bestelako fitxategiak atzitzekoa barne)."</string>
@@ -263,7 +263,7 @@
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ordu}other{# ordu}}"</string>
<string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minutu}other{# minutu}}"</string>
<string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}other{# segundo}}"</string>
- <string name="permission_reminders" msgid="6528257957664832636">"Baimenen abisuak"</string>
+ <string name="permission_reminders" msgid="6528257957664832636">"Baimenen gogorarazpenak"</string>
<string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"Erabiltzen ez den 1 aplikazio"</string>
<string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Erabiltzen ez diren <xliff:g id="NUMBER_OF_APPS">%s</xliff:g> aplikazio"</string>
<string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Baimenak kendu egin dira zure pribatutasuna babesteko. Sakatu berrikusteko."</string>
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Eman ezarpen mugatuak erabiltzeko baimena"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Murriztapenak ditu ezarpenak"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Zure segurtasuna bermatzeko, ezarpena ez dago erabilgarri une honetan."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Ekintza hori ez dago erabilgarri telefono-deietan"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefono-dei batean zaudenean ezin diezu eman beste aplikazio batzuk instalatzeko baimena aplikazioei.\n\n Iruzurgileek halako ekintzak egiteko eskatu ohi dute telefono bidezko elkarrizketetan; beraz, aukera hori blokeatu dugu zu babesteko. Ezagutzen ez duzun norbaitek halako zerbait egiteko esaten badizu, baliteke azpikeria izatea."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikazioari <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> erabiltzeko baimena ukatu zaio"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Kontuzko informazioa erabiltzeko baimen bat eskatu du aplikazioak, eta agian horrek arriskuan jarriko ditu zure informazio pertsonala eta finantzei buruzko informazioa.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Baliteke aplikazioak behar bezala ez funtzionatzea baimen murriztu hori gabe. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Lortu baimena emateko argibideak&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikazioari <xliff:g id="ROLE_NAME">%1$s</xliff:g> lehenetsia izateko baimena ukatu zaio"</string>
diff --git a/PermissionController/res/values-fa/strings.xml b/PermissionController/res/values-fa/strings.xml
index ae6063dcc..307c1b5da 100644
--- a/PermissionController/res/values-fa/strings.xml
+++ b/PermissionController/res/values-fa/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"مجاز کردن تنظیمات محدودشده"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"تنظیم محدودشده"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"برای حفظ امنیت شما، درحال‌حاضر این تنظیم دردسترس نیست."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"اقدام درطول تماس تلفنی دردسترس نیست"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"درطول تماس تلفنی، اجازه دادن به برنامه‌ها برای نصب برنامه‌های دیگر مجاز نیست.\n\n افراد کلاهبردار اغلب این نوع اقدامات را درطول مکالمه‌های تلفنی درخواست می‌کنند، بنابراین این کار برای محافظت از شما مسدود شده است. اگر شخص ناآشنایی شما را ترغیب به انجام این اقدام می‌کند، ممکن است موضوع کلاهبرداری باشد."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"درخواست برنامه برای دسترسی به <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> رد شد"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"‏این برنامه درخواست دسترسی به اجازه‌ای حساس را داشته است که می‌تواند اطلاعات شخصی و مالی‌تان را درمعرض خطر قرار دهد.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ممکن است برنامه بدون این اجازه محدودشده به‌درستی کار نکند. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;با نحوه اعطای دسترسی آشنا شوید&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"درخواست دسترسی برنامه برای تبدیل شدن به <xliff:g id="ROLE_NAME">%1$s</xliff:g> پیش‌فرض رد شد"</string>
diff --git a/PermissionController/res/values-fi/strings.xml b/PermissionController/res/values-fi/strings.xml
index e47b603d3..a88c39ab2 100644
--- a/PermissionController/res/values-fi/strings.xml
+++ b/PermissionController/res/values-fi/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Salli rajoitetut asetukset"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Rajoitettu asetus"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Asetus ei ole tällä hetkellä käytettävissä turvallisuussyistä."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Toiminto ei ole käytettävissä puhelun aikana"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Sovellusten ei sallita asentaa muita sovelluksia puhelun aikana.\n\n Huijarit pyytävät usein tätä puhelun aikana, joten se on estetty suojataksesi sinua. Jos joku, jota et tunne, kehottaa sinua toimimaan näin, kyseessä voi olla huijaus."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Sovellukselta on evätty pääsy: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Sovellus on pyytänyt pääsyä arkaluontoiseen lupaan, joka voi vaarantaa henkilökohtaisia tietojasi ja taloustietojasi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Sovellus ei välttämättä toimi oikein ilman tätä rajoitettua lupaa. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Lue, miten voit sallia pääsyn&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Sovellus ei saa olla oletuksena <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-fr-rCA/strings.xml b/PermissionController/res/values-fr-rCA/strings.xml
index 31b6bdbec..c7941a592 100644
--- a/PermissionController/res/values-fr-rCA/strings.xml
+++ b/PermissionController/res/values-fr-rCA/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Autoriser les paramètres restreints"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Paramètre restreint"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pour protéger votre sécurité, ce paramètre n\'est pas accessible actuellement."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action non proposée lors d\'un appel téléphonique"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Autoriser les applis à installer d\'autres applis n\'est pas permis lors d\'un appel téléphonique.\n\n Les escrocs demandent souvent ce type d\'action lors de conversations téléphoniques, alors ces actions sont bloquées pour vous protéger. Si quelqu\'un que vous ne connaissez pas vous guide dans cette démarche, il pourrait s\'agir d\'une escroquerie."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"L\'appli n\'a pas obtenu l\'accès à <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'appli a demandé l\'accès à une autorisation sensible qui pose un risque pour vos renseignements personnels et financiers.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Il est possible que l\'appli ne fonctionne pas correctement sans cette autorisation limitée. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Découvrir comment autoriser l\'accès&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"L\'appli n\'a pas obtenu l\'accès pour avoir le rôle<xliff:g id="ROLE_NAME">%1$s</xliff:g> par défaut"</string>
diff --git a/PermissionController/res/values-fr/strings.xml b/PermissionController/res/values-fr/strings.xml
index 14c29125e..e51f0a592 100644
--- a/PermissionController/res/values-fr/strings.xml
+++ b/PermissionController/res/values-fr/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Autoriser les paramètres restreints"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Paramètre restreint"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pour votre sécurité, ce paramètre est actuellement indisponible."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action non disponible lors d\'un appel téléphonique"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Vous ne pouvez pas autoriser des applis à installer d\'autres applis pendant un appel téléphonique.\n\n Les escrocs demandent souvent d\'effectuer ce type d\'action lors d\'appels téléphoniques. Cette action est donc bloquée pour vous protéger. Si quelqu\'un que vous ne connaissez pas vous demande d\'effectuer cette action, il s\'agit peut-être d\'une escroquerie."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"L\'appli s\'est vu refuser l\'accès à <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Cette appli a demandé l\'accès à une autorisation sensible susceptible d\'exposer vos informations financières et personnelles à un risque.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Si elle ne dispose pas d\'un accès à cette autorisation restreinte, l\'appli peut ne pas fonctionner correctement. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Découvrez comment autoriser l\'accès&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"L\'appli s\'est vu refuser l\'accès au statut de <xliff:g id="ROLE_NAME">%1$s</xliff:g> par défaut"</string>
diff --git a/PermissionController/res/values-gl/strings.xml b/PermissionController/res/values-gl/strings.xml
index 8ceeab1a6..b4766bf20 100644
--- a/PermissionController/res/values-gl/strings.xml
+++ b/PermissionController/res/values-gl/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir opcións restrinxidas"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Opción de configuración restrinxida"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pola túa seguranza, esta opción de configuración non está dispoñible nestes momentos."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Acción non dispoñible nas chamadas telefónicas"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Non está permitido que as aplicacións instalen outras aplicacións durante as chamadas telefónicas.\n\n Como os estafadores adoitan solicitar este tipo de accións durante as chamadas telefónicas, bloqueámola para protexerte. Se alguén pretende que realices esta acción, é posible que sexa unha estafa."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Denegóuselle á aplicación o acceso ao permiso: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"A aplicación solicitou acceso a un permiso confidencial que pode poñer en perigo a túa información persoal e financeira.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É posible que a aplicación non funcione correctamente sen este permiso restrinxido. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Máis información sobre como permitir o acceso&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Denegóuselle á aplicación o acceso para actuar como <xliff:g id="ROLE_NAME">%1$s</xliff:g> de forma predeterminada"</string>
diff --git a/PermissionController/res/values-gu/strings.xml b/PermissionController/res/values-gu/strings.xml
index b65a5c582..0ea95ae75 100644
--- a/PermissionController/res/values-gu/strings.xml
+++ b/PermissionController/res/values-gu/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"પ્રતિબંધિત સેટિંગને મંજૂરી આપો"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"પ્રતિબંધિત સેટિંગ"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"તમારી સુરક્ષા માટે, આ સેટિંગ હાલમાં ઉપલબ્ધ નથી."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ફોન કૉલ પર હોવા દરમિયાન આ ક્રિયા ઉપલબ્ધ હોતી નથી"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"કોઈ ફોન કૉલ દરમિયાન, કોઈ એક દ્વારા બીજી ઍપ ઇન્સ્ટૉલ કરવાની મંજૂરી આપવામાં આવતી નથી.\n\n સ્કૅમર દ્વારા ફોન કૉલ પરની વાતચીતો દરમિયાન આ પ્રકારની ક્રિયાની વિનંતી સામાન્ય રીતે કરવામાં આવતી હોય છે, તેથી તમારી સુરક્ષા માટે આને બ્લૉક કરવામાં આવે છે. જો કોઈ અજાણી વ્યક્તિ દ્વારા આ ક્રિયા કરવા માટે તમને માર્ગદર્શન આપવામાં આવતું હોય, તો આ સ્કૅમ હોઈ શકે છે."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ઍપને <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>નો ઍક્સેસ નકારવામાં આવ્યો"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"આ ઍપ દ્વારા કોઈ સંવેદનશીલ માહિતીની પરવાનગીના ઍક્સેસની વિનંતી કરવામાં આવી છે, જેને કારણે તમારી વ્યક્તિગત અને નાણાકીય માહિતી જોખમમાં આવી શકે છે.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>એ પણ શક્ય છે કે આ પ્રતિબંધિત પરવાનગી વિના ઍપ કદાચ યોગ્ય રીતે કામ ન પણ કરી શકે. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ઍક્સેસ મંજૂર કરવાની રીત જાણો&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ઍપને ડિફૉલ્ટ <xliff:g id="ROLE_NAME">%1$s</xliff:g> બનવાનો ઍક્સેસ નકારવામાં આવ્યો"</string>
diff --git a/PermissionController/res/values-hi/strings.xml b/PermissionController/res/values-hi/strings.xml
index 2557708bc..54dceb35d 100644
--- a/PermissionController/res/values-hi/strings.xml
+++ b/PermissionController/res/values-hi/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"पाबंदी वाली सेटिंग को अनुमति दें"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"इस सेटिंग पर पाबंदी लगाई गई है"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"आपकी सुरक्षा के लिए, यह सेटिंग फ़िलहाल उपलब्ध नहीं है."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"फ़ोन कॉल के दौरान, कार्रवाई नहीं की जा सकती"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"फ़ोन कॉल के दौरान, ऐप्लिकेशन को अन्य ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है.\n\n फ़ोन पर बातचीत करते हुए, धोखाधड़ी करने वाले लोग अक्सर इस तरह की कार्रवाई करने का अनुरोध करते हैं. इसलिए, आपको सुरक्षित रखने के लिए यह अनुमति नहीं दी जाती है. अगर कोई अनज़ान व्यक्ति, आपको यह कार्रवाई करने के लिए कहता है, तो हो सकता है कि आपके साथ धोखाधड़ी की जा रही हो."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ऐप्लिकेशन को, <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> को ऐक्सेस करने की अनुमति नहीं मिली"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"इस ऐप्लिकेशन ने संवेदनशील जानकारी ऐक्सेस करने का अनुरोध किया है. इसे अनुमति देने पर, आपकी निजी और वित्तीय जानकारी की सुरक्षा को खतरा हो सकता है.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>हालांकि, हो सकता है कि पाबंदी वाली अनुमति न मिलने पर, ऐप्लिकेशन सही तरह से काम न करें. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ऐक्सेस देने का तरीका जानें&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ऐप्लिकेशन को डिफ़ॉल्ट <xliff:g id="ROLE_NAME">%1$s</xliff:g> के तौर पर सेट करने की अनुमति नहीं दी गई"</string>
diff --git a/PermissionController/res/values-hr/strings.xml b/PermissionController/res/values-hr/strings.xml
index 55cfcfa78..7275d1681 100644
--- a/PermissionController/res/values-hr/strings.xml
+++ b/PermissionController/res/values-hr/strings.xml
@@ -674,12 +674,14 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Dopusti ograničene postavke"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ograničena postavka"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Radi vaše sigurnosti ova postavka trenutačno nije dostupna."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Radnja nije dostupna tijekom telefonskog poziva"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Aplikacijama nije dopušteno instalirati druge aplikacije tijekom telefonskog poziva.\n\n Prevaranti često zahtijevaju tu vrstu radnje tijekom telefonskih razgovora, pa je ona blokirana radi vaše zaštite. Ako vas netko koga ne poznajete navodi na tu radnju, možda je riječ o prijevari."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji je odbijen pristup dopuštenju <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
- <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup dopuštenju za osjetljive podatke koje može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog ograničenog dopuštenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako omogućiti pristup&lt;/a&gt;"</string>
- <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je uskraćeno da prema zadanim postavkama bude <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
- <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacija je zatražila pristup dopuštenjima za osjetljive podatke koja mogu ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tih ograničenih dopuštenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako omogućiti pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup dopuštenju za osjetljive podatke koje može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog uskraćenog dopuštenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako omogućiti pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je uskraćeno da bude zadana <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacija je zatražila pristup dopuštenjima za osjetljive podatke čime može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tih ograničenih dopuštenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako omogućiti pristup&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikaciji je odbijen pristup"</string>
- <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Pristup tom dopuštenju može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog ograničenog dopuštenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako omogućiti pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Pristup tom dopuštenju može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog dopuštenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako omogućiti pristup&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Saznajte više"</string>
<string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"U redu"</string>
<string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Upit za dopuštenje je spriječen"</string>
diff --git a/PermissionController/res/values-hu/strings.xml b/PermissionController/res/values-hu/strings.xml
index 32bfb9660..c1c8b3756 100644
--- a/PermissionController/res/values-hu/strings.xml
+++ b/PermissionController/res/values-hu/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Korlátozott beállítások engedélyezése"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Korlátozott beállítás"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Biztonsága érdekében ez a beállítás jelenleg nem használható."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"A művelet nem végezhető el telefonhívás közben"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefonhívás közben nem lehet engedélyezni, hogy alkalmazások más alkalmazásokat telepítsenek.\n\n A csalók gyakran kérik ilyen jellegű műveletek végrehajtását a telefonhívások során, ezért a rendszer az Ön védelme érdekében letiltja ezt a lehetőséget. Ha valaki, akit nem ismer, arra kéri, hogy végezze el ezt a műveletet, előfordulhat, hogy csalárd szándékkal teszi."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Az app nem kapott hozzáférést a következőhöz: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Az alkalmazás hozzáférési engedélyt kért a bizalmas adatokhoz, ami veszélybe sodorhatja az Ön személyes és pénzügyi adatait.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Lehetséges, hogy az alkalmazás nem működik megfelelően enélkül a korlátozott engedély nélkül. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;További információ a hozzáférés megadásának módjáról.&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Az app nem kapott hozzáférést a következőhöz: alapértelmezett <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-hy/strings.xml b/PermissionController/res/values-hy/strings.xml
index 88c8d8d28..13b0ba067 100644
--- a/PermissionController/res/values-hy/strings.xml
+++ b/PermissionController/res/values-hy/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Թույլատրել սահմանափակ ռեժիմի կարգավորումները"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Սահմանափակումներով կարգավորում"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Անվտանգության նկատառումներից ելնելով՝ այս կարգավորումը ներկայումս անհասանելի է։"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Գործողությունը հասանելի չէ հեռախոսազանգի ժամանակ"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Հեռախոսազանգի ժամանակ հավելվածներին չի թույլատրվում տեղադրել այլ հավելվածներ։\n\n Խաբեբաները սովորաբար ցանկանում են այս տեսակի գործողություններն անել հեռախոսային զրույցների ժամանակ, ուստի դա արգելափակված է՝ ձեր անվտանգության նկատառումներից ելնելով։ Եթե որևէ անծանոթ մարդ ձեզանից խնդրել է նմանատիպ թույլտվություն, ամենայն հավանականությամբ դա խարդախություն է։"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Հավելվածին մերժվել է <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>ի օգտագործման թույլտվությունը"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Հավելվածը հայցել է կոնֆիդենցիալ տեղեկությունների օգտագործման թույլտվություն, որը կարող է վտանգի ենթարկել ձեր անձնական և ֆինանսական տեղեկությունները։<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Հնարավոր է, որ առանց այս սահմանափակված թույլտվության՝ հավելվածը չաշխատի պատշաճ կերպով։ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ինչպես տրամադրել տվյալների օգտագործման թույլտվություն&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Հավելվածին մերժվել է «<xliff:g id="ROLE_NAME">%1$s</xliff:g>» կատեգորիայում կանխադրված լինելու թույլտվությունը"</string>
diff --git a/PermissionController/res/values-in/strings.xml b/PermissionController/res/values-in/strings.xml
index e409aabd9..9ee6d5b8b 100644
--- a/PermissionController/res/values-in/strings.xml
+++ b/PermissionController/res/values-in/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Izinkan setelan terbatas"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Setelan terbatas"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Demi keamanan Anda, setelan ini tidak tersedia untuk saat ini."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Tindakan tidak tersedia selama panggilan telepon"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Mengizinkan aplikasi menginstal aplikasi lain tidak diizinkan selama panggilan telepon berlangsung.\n\n Scammer sering meminta jenis tindakan ini selama percakapan panggilan telepon, jadi tindakan ini diblokir untuk melindungi Anda. Jika Anda diarahkan untuk melakukan tindakan ini oleh seseorang yang tidak Anda kenal, hal tersebut mungkin merupakan scam."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikasi ditolak aksesnya ke <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikasi meminta akses ke izin sensitif yang dapat membahayakan info pribadi dan keuangan Anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikasi mungkin tidak dapat berfungsi dengan baik tanpa izin terbatas ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&amp;gtPelajari cara mengizinkan akses&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikasi ditolak aksesnya untuk menjadi <xliff:g id="ROLE_NAME">%1$s</xliff:g> default"</string>
diff --git a/PermissionController/res/values-is/strings.xml b/PermissionController/res/values-is/strings.xml
index ee49e777d..bd8d62dad 100644
--- a/PermissionController/res/values-is/strings.xml
+++ b/PermissionController/res/values-is/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Leyfa takmarkaðar stillingar"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Takmörkuð stilling"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Til að tryggja öryggi þitt er þessi stilling ekki tiltæk eins og er."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Aðgerð ekki í boði á meðan á símtali stendur"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Ekki er heimilt að leyfa forritum að setja önnur forrit upp á meðan á símtali stendur.\n\n Svikarar eiga það til að biðja notendur um að framkvæma aðgerðir á borð við þessa á meðan á samtali stendur. Þar af leiðandi er lokað á hana til að vernda þig. Ef einhver sem þú þekkir ekki bað þig um að framkvæma þessa aðgerð gæti verið um svik að ræða."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Forritið fékk ekki aðgang að <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Forritið bað um aðgang að heimild fyrir viðkvæmu efni sem getur stofnað persónu- og fjármálaupplýsingum þínum í hættu.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Forritið virkar hugsanlega ekki sem skyldi án þessarar takmörkuðu heimildar. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Kynntu þér hvernig þú leyfir aðgang&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Forritið fékk ekki aðgang að sjálfgefnu <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-it/strings.xml b/PermissionController/res/values-it/strings.xml
index c855b4ce6..df7a62b8f 100644
--- a/PermissionController/res/values-it/strings.xml
+++ b/PermissionController/res/values-it/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Consenti impostazioni con limitazioni"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Impostazione con limitazioni"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Per la tua sicurezza, questa impostazione non è al momento disponibile."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Azione non disponibile durante una telefonata"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"È vietato consentire alle app di installare altre app durante una telefonata.\n\n I truffatori richiedono spesso questo tipo di azione durante le conversazioni telefoniche, quindi è bloccata per proteggerti. Se qualcuno che non conosci ti guida a compiere questa azione, potrebbe trattarsi di una truffa."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"All\'app è stato negato l\'accesso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'app ha richiesto l\'accesso a un\'autorizzazione sensibile che può mettere a rischio le tue informazioni finanziarie e personali.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>È possibile che l\'app non funzioni correttamente senza questa autorizzazione limitata. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Scopri di più su come consentire l\'accesso&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"All\'app è stato negato l\'accesso al ruolo <xliff:g id="ROLE_NAME">%1$s</xliff:g> predefinita"</string>
diff --git a/PermissionController/res/values-iw/strings.xml b/PermissionController/res/values-iw/strings.xml
index 0fc33a62b..9779e6c66 100644
--- a/PermissionController/res/values-iw/strings.xml
+++ b/PermissionController/res/values-iw/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"הרשאה להגדרות מוגבלות"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"הגדרה מוגבלת"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"מטעמי אבטחה, ההגדרה הזו לא זמינה כרגע."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"הפעולה לא זמינה במהלך שיחת טלפון"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"אי אפשר לתת לאפליקציות הרשאה להתקין אפליקציות אחרות במהלך שיחת טלפון.\n\n לרוב, אנשים שמבצעים תרמיות מבקשים לבצע פעולות מהסוג הזה במהלך שיחות טלפון, לכן הפעולה הזו חסומה כדי להגן עליך. אם מישהו לא מוכר מנחה אותך לבצע את הפעולה הזו, יכול להיות שמדובר בתרמית."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"בקשת הגישה של האפליקציה ל<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> נדחתה"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"‏האפליקציה ביקשה הרשאת גישה למידע רגיש שעלולה לסכן את המידע האישי והפיננסי שלך.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>יכול להיות שהאפליקציה לא תעבוד כמו שצריך ללא ההרשאה המוגבלת הזו. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;מידע נוסף על מתן גישה להרשאות&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"בקשת הגישה של האפליקציה לשמש כברירת המחדל של <xliff:g id="ROLE_NAME">%1$s</xliff:g> נדחתה"</string>
diff --git a/PermissionController/res/values-ja/strings.xml b/PermissionController/res/values-ja/strings.xml
index e7322b07b..99c927b67 100644
--- a/PermissionController/res/values-ja/strings.xml
+++ b/PermissionController/res/values-ja/strings.xml
@@ -185,7 +185,7 @@
<string name="app_permission_usage_summary" msgid="390383661936709672">"アクセス: <xliff:g id="NUM">%1$s</xliff:g> 回。合計時間: <xliff:g id="DURATION">%2$s</xliff:g>。最終使用: <xliff:g id="TIME">%3$s</xliff:g>前。"</string>
<string name="app_permission_usage_summary_no_duration" msgid="3698475875179457400">"アクセス: <xliff:g id="NUM">%1$s</xliff:g> 回。最終使用: <xliff:g id="TIME">%2$s</xliff:g>前。"</string>
<string name="app_permission_button_allow" msgid="5808039516494774647">"許可する"</string>
- <string name="app_permission_button_allow_all_files" msgid="1792232272599018825">"すべてのファイルの管理を許可"</string>
+ <string name="app_permission_button_allow_all_files" msgid="1792232272599018825">"すべての管理を許可"</string>
<string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"アクセスのみ許可"</string>
<string name="app_permission_button_allow_always" msgid="4573292371734011171">"常に許可"</string>
<string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"アプリの使用中のみ許可"</string>
@@ -211,7 +211,7 @@
<string name="unused_apps_summary" msgid="8839466950318403115">"権限と一時ファイルを削除し、通知を停止します"</string>
<string name="unused_apps_summary_v2" msgid="5011313200815115802">"権限と一時ファイルを削除し、通知を停止し、アプリをアーカイブします"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"データ保護のため、このアプリが数か月使用されていない場合はアプリの権限が取り消されます。"</string>
- <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"データ保護のため、アプリが数か月使用されていない場合は以下の権限が取り消されます。<xliff:g id="PERMS">%1$s</xliff:g>"</string>
+ <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"データ保護のため、アプリが数か月使用されていない場合は次の権限が取り消されます。<xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"データ保護のため、数か月使用していないアプリの権限を削除しました。"</string>
<string name="auto_revoke_open_app_message" msgid="8075556291711205039">"もう一度権限を許可するには、アプリを開いてください。"</string>
<string name="auto_revoke_disabled" msgid="8697684442991567188">"このアプリでは現在、自動削除は無効になっています。"</string>
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"制限付き設定を許可"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"制限付き設定"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"セキュリティ保護のため、この設定は現在利用できません。"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"この操作は通話中はできません"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"通話中にアプリが他のアプリをインストールすることはできません。\n\n このような操作は、詐欺師が電話での会話中に要求することが多いため、ユーザー保護を目的に禁止されています。知らない相手からこのような操作を求められた場合は、詐欺の可能性があります。"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"アプリは<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>へのアクセスを拒否されました"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"機密情報に関わる権限へのアクセスをアプリがリクエストしました。この権限へのアクセスは、あなたの個人情報や財務情報を危険にさらす恐れがあります。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>この制限付きの権限がないとアプリは正しく動作しない可能性があります。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;アクセスを許可する方法の詳細&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"アプリはデフォルトの<xliff:g id="ROLE_NAME">%1$s</xliff:g>としてのアクセスを拒否されました"</string>
diff --git a/PermissionController/res/values-ka/strings.xml b/PermissionController/res/values-ka/strings.xml
index 3f836afdf..ec1b1bb3a 100644
--- a/PermissionController/res/values-ka/strings.xml
+++ b/PermissionController/res/values-ka/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"შეზღუდული პარამეტრების დაშვება"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"შეზღუდული პარამეტრი"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"თქვენივე უსაფრთხოებისთვის ეს პარამეტრი ამჟამად მიუწვდომელია."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"სატელეფონო ზარისას მოქმედება არ არის ხელმისაწვდომი"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"აპებისთვის სხვა აპების ინსტალაციის დაშვება არ არის ნებადართული ტელეფონზე ზარის დროს.\n\n თაღლითები ხშირად ითხოვენ ამ ტიპის ქმედებას ტელეფონზე საუბრისას, შესაბამისად, აღნიშნული ქმედება დაიბლოკა თქვენი უსაფრთხოებისთვის. თუ ვინმემ, ვისაც არ იცნობთ, მიგითითათ ამ ქმედების განხორციელებისკენ, ეს შესაძლოა იყოს თაღლითური სქემა."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"აპისთვის უარყოფილია ნებართვაზე წვდომა: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"აპმა სენსიტიურ ინფორმაციაზე წვდომა მოითხოვა, რაც თქვენს პირად და ფინანსურ ინფორმაციას საფრთხის ქვეშ აყენებს.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>შესაძლოა აპმა ამ შეზღუდული ნებართვის გარეშე სათანადოდ ვერ იმუშაოს. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;შეიტყვეთ მეტი ნებართვის დაშვების შესახებ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"აპისთვის ნაგულისხმევ როლზე (<xliff:g id="ROLE_NAME">%1$s</xliff:g>) წვდომა უარყოფილია"</string>
diff --git a/PermissionController/res/values-kk/strings.xml b/PermissionController/res/values-kk/strings.xml
index ee9e14a6a..0413f0b54 100644
--- a/PermissionController/res/values-kk/strings.xml
+++ b/PermissionController/res/values-kk/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Шектелген параметрлерге рұқсат беру"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Шектелген параметр"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Қауіпсіздік мақсатында бұл параметрді қазір пайдалану мүмкін емес."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Телефон қоңырауы кезінде бұл әрекет мүмкін емес"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Телефон қоңырауы кезінде қолданбаларға басқа қолданбаларды орнатуға тыйым салынады.\n\n Алаяқтар телефонмен сөйлесу кезінде көбіне осындай әрекеттерді жасауды сұрайды, сондықтан сізді қорғау үшін бұл әрекет блокталған. Таныс емес адам осылай әрекет етуге нұсқау берсе, бұл алаяқтық болуы мүмкін."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Қолданбаға <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> рұқсаты берілмеді"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Қолданба жеке және қаржылық ақпаратыңызға қауіп төндіруі мүмкін құпия ақпарат рұқсатын сұрады.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Қолданба бұл шектеулі рұқсатсыз дұрыс жұмыс істемеуі мүмкін. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Рұқсат беру туралы ақпарат&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Қолданбаға әдепкі <xliff:g id="ROLE_NAME">%1$s</xliff:g> болу рұқсаты берілмеді"</string>
diff --git a/PermissionController/res/values-km/strings.xml b/PermissionController/res/values-km/strings.xml
index 60e0590ce..3712f99ac 100644
--- a/PermissionController/res/values-km/strings.xml
+++ b/PermissionController/res/values-km/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"អនុញ្ញាតការកំណត់​ដែលបានដាក់កំហិត"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ការកំណត់​ដែលបានដាក់កំហិត"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ដើម្បីសុវត្ថិភាព​របស់អ្នក បច្ចុប្បន្នមិនអាចប្រើការកំណត់នេះបានទេ។"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"មិនអាចធ្វើសកម្មភាពបានទេ ពេលកំពុងហៅទូរសព្ទ"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ការអនុញ្ញាតឱ្យកម្មវិធីដំឡើងកម្មវិធីផ្សេងទៀតមិនត្រូវបានអនុញ្ញាតអំឡុងពេលហៅទូរសព្ទទេ។\n\n ជារឿយៗ ជនឆបោកស្នើសុំសកម្មភាពប្រភេទនេះអំឡុងពេលសន្ទនាតាមទូរសព្ទ ដូច្នេះសកម្មភាពនេះត្រូវបានទប់ស្កាត់ដើម្បីការពារអ្នក។ ប្រសិនបើអ្នកត្រូវបានណែនាំឱ្យធ្វើសកម្មភាពនេះដោយនរណាម្នាក់ដែលអ្នកមិនស្គាល់ វាអាចជាការឆបោក។"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"កម្មវិធីត្រូវបានបដិសេធមិនឱ្យចូលប្រើ<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"កម្មវិធីបានស្នើសុំសិទ្ធិចូលប្រើការអនុញ្ញាត​ដែលមានលក្ខណៈរសើប ដែលអាចធ្វើឱ្យព័ត៌មានហិរញ្ញវត្ថុ និងព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នកប្រឈមនឹងហានិភ័យ។<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>កម្មវិធីអាចនឹងមិនដំណើរការបានត្រឹមត្រូវទេ ប្រសិនបើគ្មានការអនុញ្ញាតដែលមានការរឹតបន្តឹងនេះ។ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ស្វែងយល់អំពីរបៀបផ្ដល់សិទ្ធិចូលប្រើ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"កម្មវិធីត្រូវបានបដិសេធមិនឱ្យចូលប្រើជា<xliff:g id="ROLE_NAME">%1$s</xliff:g>លំនាំដើម"</string>
diff --git a/PermissionController/res/values-kn/strings.xml b/PermissionController/res/values-kn/strings.xml
index 43ce0a8b9..86f93bc4c 100644
--- a/PermissionController/res/values-kn/strings.xml
+++ b/PermissionController/res/values-kn/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"ನಿರ್ಬಂಧಿಸಲಾದ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಅನುಮತಿಸಿ"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ನಿರ್ಬಂಧಿಸಲಾದ ಸೆಟ್ಟಿಂಗ್"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ನಿಮ್ಮ ಸುರಕ್ಷತೆಗಾಗಿ, ಈ ಸೆಟ್ಟಿಂಗ್ ಪ್ರಸ್ತುತ ಲಭ್ಯವಿಲ್ಲ."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ಫೋನ್ ಕರೆಯಲ್ಲಿರುವಾಗ ಕ್ರಿಯೆ ಲಭ್ಯವಿರುವುದಿಲ್ಲ"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ಫೋನ್ ಕರೆಯ ಸಮಯದಲ್ಲಿ ಇತರ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಆ್ಯಪ್‌ಗಳನ್ನು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ.\n\n ಫೋನ್ ಕರೆ ಸಂಭಾಷಣೆಯ ಸಮಯದಲ್ಲಿ ಸ್ಕ್ಯಾಮರ್‌ಗಳು ಆಗಾಗ್ಗೆ ಈ ರೀತಿಯ ಕ್ರಿಯೆಯನ್ನು ವಿನಂತಿಸುತ್ತಾರೆ, ಆದ್ದರಿಂದ ನಿಮ್ಮನ್ನು ರಕ್ಷಿಸಲು ಇದನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತದೆ. ನಿಮಗೆ ಪರಿಚಯವಿಲ್ಲದ ಯಾರಾದರೂ ಈ ಕ್ರಮವನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ನಿಮಗೆ ಮಾರ್ಗದರ್ಶನ ನೀಡುತ್ತಿದ್ದರೆ, ಅದು ಸ್ಕ್ಯಾಮ್ ಆಗಿರಬಹುದು."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ಅನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಆ್ಯಪ್‌ಗೆ ನಿರಾಕರಿಸಲಾಗಿದೆ"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮತ್ತು ಹಣಕಾಸಿನ ಮಾಹಿತಿಯನ್ನು ಅಪಾಯಕ್ಕೆ ಸಿಲುಕಿಸಬಹುದಾದ ಸೂಕ್ಷ್ಮ ಅನುಮತಿಗೆ ಆ್ಯಪ್ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ವಿನಂತಿಸಿದೆ.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>.ಈ ನಿರ್ಬಂಧಿತ ಅನುಮತಿಯಿಲ್ಲದೆ ಆ್ಯಪ್ ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರುವ ಸಾಧ್ಯತೆಯಿದೆ. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೇಗೆ ಅನುಮತಿಸುವುದು ಎಂಬುದನ್ನು ತಿಳಿಯಿರಿ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ಡೀಫಾಲ್ಟ್ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ಆಗಿರಲು ಆ್ಯಪ್‌ಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ"</string>
diff --git a/PermissionController/res/values-ko/strings.xml b/PermissionController/res/values-ko/strings.xml
index d611cdf9d..dac0845fb 100644
--- a/PermissionController/res/values-ko/strings.xml
+++ b/PermissionController/res/values-ko/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"제한된 설정 허용"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"제한된 설정"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"보안을 위해 이 설정은 현재 사용할 수 없습니다."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"통화 중에 수행할 수 없는 작업입니다"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"통화 중에는 앱이 다른 앱을 설치하도록 허용할 수 없습니다.\n\n 사기범은 전화 통화 중에 종종 이러한 유형의 작업을 요청하므로, 사용자를 보호하기 위해 이러한 작업은 차단됩니다. 모르는 사람이 이 작업을 수행하도록 안내하는 경우 사기일 수도 있습니다."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>에 대한 앱의 액세스가 거부됨"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"앱에서 개인 정보 및 금융 정보가 위험에 노출될 수 있는 민감한 권한에 대한 액세스를 요청했습니다.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>이 제한된 권한 없이는 앱이 제대로 작동하지 않을 수 있습니다. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;액세스 허용 방법 알아보기&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"앱을 기본 <xliff:g id="ROLE_NAME">%1$s</xliff:g>으로 사용하기 위한 액세스가 거부됨"</string>
diff --git a/PermissionController/res/values-ky/strings.xml b/PermissionController/res/values-ky/strings.xml
index f72d72a1e..ff954634a 100644
--- a/PermissionController/res/values-ky/strings.xml
+++ b/PermissionController/res/values-ky/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Чектелген параметрлерге уруксат берүү"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Чектелген функция"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Коопсуздук максатында бул параметр азырынча иштебейт."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Телефон чалуу учурунда аракет жеткиликсиз"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Телефон чалуу учурунда колдонмолорго башка колдонмолорду орнотууга уруксат берүүгө болбойт.\n\n Телефон чалуу аркылуу сүйлөшүп жатканда шылуундар көбүнчө ушул сыяктуу аракетти аткарууну суранышат, андыктан коопсуздугуңузду коргоо үчүн бул нерсе бөгөттөлдү. Эгер сиз тааныбаган адам мындай аракетти аткарууну айтса, ал кесепчилик болушу мүмкүн."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Колдонмого <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> параметрин колдонууга тыюу салынды"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Колдонмо жеке жана каржы маалыматыңызга коркунуч жаратышы мүмкүн болгон купуя маалыматты көрүүгө уруксат сурады.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Мындай уруксатсыз колдонмо ойдогудай иштебей коюшу мүмкүн. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Мүмкүнчүлүк берүү жөнүндө кеңири маалымат&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Колдонмого демейки <xliff:g id="ROLE_NAME">%1$s</xliff:g> ролуна тыюу салынды"</string>
diff --git a/PermissionController/res/values-lo/strings.xml b/PermissionController/res/values-lo/strings.xml
index f71900d86..d3fe288a3 100644
--- a/PermissionController/res/values-lo/strings.xml
+++ b/PermissionController/res/values-lo/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"ອະນຸຍາດການຕັ້ງຄ່າທີ່ຈຳກັດ"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ການຕັ້ງຄ່າທີ່ຈຳກັດ"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ເພື່ອຄວາມປອດໄພຂອງທ່ານ, ຕອນນີ້ຈຶ່ງບໍ່ສາມາດໃຊ້ການຕັ້ງຄ່ານີ້ໄດ້."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ການດຳເນີນການບໍ່ພ້ອມນຳໃຊ້ໃນຂະນະທີ່ກຳລັງໂທລະສັບ"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ບໍ່ອະນຸຍາດໃຫ້ແອັບຕິດຕັ້ງແອັບອື່ນໆໃນລະຫວ່າງການໂທ.\n\n ສະແກມເມີມັກຈະຮ້ອງຂໍການດຳເນີນການປະເພດນີ້ໃນລະຫວ່າງການສົນທະນາທາງໂທລະສັບ, ດັ່ງນັ້ນລະບົບຈຶ່ງບລັອກໄວ້ເພື່ອປົກປ້ອງທ່ານ. ຫາກມີຄົນທີ່ທ່ານບໍ່ຮູ້ຈັກບອກໃຫ້ທ່ານດຳເນີນການດັ່ງກ່າວ, ນັ້ນອາດເປັນສະແກມ."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ແອັບຖືກປະຕິເສດສິດເຂົ້າເຖິງຫາ <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ແອັບໄດ້ຮ້ອງຂໍການສິດເຖິງການອະນຸຍາດທີ່ລະອຽດອ່ອນ ເຊິ່ງສາມາດເຮັດໃຫ້ຂໍ້ມູນສ່ວນຕົວ ແລະ ຂໍ້ມູນການເງິນຂອງທ່ານມີຄວາມສ່ຽງ.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ມັນເປັນໄປໄດ້ວ່າແອັບດັ່ງກ່າວຈະບໍ່ເຮັດວຽກຢ່າງຖືກຕ້ອງໂດຍບໍ່ມີການອະນຸຍາດທີ່ຖືກຈຳກັດໄວ້ນີ້. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ສຶກສາວິທີອະນຸຍາດສິດເຂົ້າເຖິງ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ແອັບຖືກປະຕິເສດສິດເຂົ້າເຖິງໃຫ້ເປັນ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ຄ່າເລີ່ມຕົ້ນ"</string>
diff --git a/PermissionController/res/values-lt/strings.xml b/PermissionController/res/values-lt/strings.xml
index 59cd88b5b..12718ca65 100644
--- a/PermissionController/res/values-lt/strings.xml
+++ b/PermissionController/res/values-lt/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Leisti apribotus nustatymus"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Apribotas nustatymas"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Šis nustatymas šiuo metu nepasiekiamas dėl jūsų saugumo."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Veiksmas nepasiekiamas vykstant pokalbiui telefonu"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Leidimas programoms įdiegti kitas programas neveikia vykstant pokalbiui telefonu.\n\n Sukčiai dažnai prašo atlikti tokio tipo veiksmus vykstant pokalbiui telefonu, todėl šis veiksmas blokuojamas siekiant jus apsaugoti. Jei nepažįstamas asmuo prašo atlikti šį veiksmą, tai gali būti sukčiavimas."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Programai prieiga nesuteikta:<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Programa pateikė leidimo pasiekti neskelbtiną informaciją, dėl kurio gali kilti pavojus jūsų asmens ir finansinei informacijai, užklausą.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Gali būti, kad be šio apriboto leidimo programa neveiks tinkamai. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Sužinokite, kaip suteikti prieigą&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Programai nesuteikta prieiga kaip numatytojo šio vaidmens: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-lv/strings.xml b/PermissionController/res/values-lv/strings.xml
index 297279e73..f6f852bfd 100644
--- a/PermissionController/res/values-lv/strings.xml
+++ b/PermissionController/res/values-lv/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Atļaut ierobežotos iestatījumus"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ierobežots iestatījums"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Drošības apsvērumu dēļ šis iestatījums pašlaik nav pieejams."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Darbība nav pieejama tālruņa zvana laikā"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Tālruņa zvana laikā lietotnēm nav atļauts instalēt citas lietotnes.\n\n Šāda veida darbību tālruņa zvana laikā bieži pieprasa krāpnieki, tāpēc šī iespēja ir bloķēta, lai jūs aizsargātu. Ja jums nepazīstama persona jums iesaka veikt šo darbību, tas var būt krāpniecības mēģinājums."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Lietotnes piekļuve atļaujai “<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>” tika liegta"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Lietotne ir pieprasījusi piekļuvi sensitīvai atļaujai, kas var apdraudēt jūsu personas un finanšu informāciju.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Iespējams, lietotne nedarbosies pareizi bez šīs ierobežotās atļaujas. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Uzziniet, kā piešķirt piekļuvi.&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Lietotnes piekļuve noklusējuma lomai “<xliff:g id="ROLE_NAME">%1$s</xliff:g>” tika liegta"</string>
diff --git a/PermissionController/res/values-mk/strings.xml b/PermissionController/res/values-mk/strings.xml
index 2fdd35649..2f10498c5 100644
--- a/PermissionController/res/values-mk/strings.xml
+++ b/PermissionController/res/values-mk/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Дозволете ограничени поставки"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ограничена поставка"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"За ваша безбедност, поставкава е недостапна во моментов."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Дејството не е достапно при телефонски повик"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Дозволувањето апликациите да инсталираат други апликации е оневозможено при телефонски повик.\n\n Измамниците често бараат ваков тип дејства при телефонските разговори, па блокирано е за да ве заштити. Ако некој што не го познавате ве води да го преземете дејствово, тоа може да биде измама."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Барањето за пристап на апликацијата до <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> е одбиено"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Апликацијата побара пристап до дозвола за чувствителни податоци што може да ја загрози безбедноста на вашите лични и финансиски податоци.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Можно е апликацијата да не функционира правилно без ограниченава дозвола. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Дознајте како да дозволите пристап&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Барањето на апликацијата да биде стандарднa <xliff:g id="ROLE_NAME">%1$s</xliff:g> е одбиено"</string>
diff --git a/PermissionController/res/values-ml/strings.xml b/PermissionController/res/values-ml/strings.xml
index 09ea9e376..e7ff97ea7 100644
--- a/PermissionController/res/values-ml/strings.xml
+++ b/PermissionController/res/values-ml/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"നിയന്ത്രിത ക്രമീകരണം അനുവദിക്കുക"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"നിയന്ത്രിത ക്രമീകരണം"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"നിങ്ങളുടെ സുരക്ഷയ്ക്ക്, ഈ ക്രമീകരണം നിലവിൽ ലഭ്യമല്ല."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ഫോൺ കോളിനിടെ പ്രവർത്തനം ലഭ്യമല്ല"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ഫോൺ കോളിനിടെ മറ്റ് ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാനുള്ള അനുവാദം ആപ്പുകൾക്കില്ല.\n\n ഫോൺ കോൾ സംഭാഷണത്തിനിടെ സ്‌കാമർമാർ പലപ്പോഴും ഇത്തരത്തിലുള്ള പ്രവർത്തനം അഭ്യർത്ഥിക്കാറുണ്ട്, അതിനാൽ നിങ്ങളെ പരിരക്ഷിക്കുന്നതിനായി ഇത് ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു. പരിചയമില്ലാത്ത ആരെങ്കിലുമാണ് ഈ പ്രവർത്തനം നിങ്ങളോട് നിർദ്ദേശിക്കുന്നതെങ്കിൽ, അതൊരു സ്കാം ആയേക്കാം."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ആപ്പിന് <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> എന്നതിലേക്കുള്ള ആക്‌സസ് നിരസിച്ചു"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ആപ്പ് സെൻസിറ്റീവ് വിവരങ്ങൾക്കുള്ള അനുമതിയിലേക്ക് ആക്‌സസ് അഭ്യർത്ഥിച്ചു, ഇത് നിങ്ങളുടെ വ്യക്തിപരവും സാമ്പത്തികവുമായ വിവരങ്ങളെ അപകടത്തിലാക്കിയേക്കാം.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ഈ നിയന്ത്രിത അനുമതിയില്ലാതെ ആപ്പ് ശരിയായി പ്രവർത്തിച്ചേക്കില്ല. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ആക്സസ് എങ്ങനെ അനുവദിക്കുന്നുവെന്നറിയുക&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ആപ്പിന് ഡിഫോൾട്ട് <xliff:g id="ROLE_NAME">%1$s</xliff:g> ആകാനുള്ള ആക്‌സസ് നിരസിച്ചു"</string>
diff --git a/PermissionController/res/values-mn/strings.xml b/PermissionController/res/values-mn/strings.xml
index c705e388b..0330dc6d2 100644
--- a/PermissionController/res/values-mn/strings.xml
+++ b/PermissionController/res/values-mn/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Хязгаарлагдсан тохиргоог зөвшөөрөх"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Хязгаарлагдсан тохиргоо"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Таны аюулгүй байдлын үүднээс энэ тохиргоо одоогоор боломжгүй байна."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Утасны дуудлага хийж байхад үйлдэл хийх боломжгүй"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Утасны дуудлагын үеэр аппуудад бусад апп суулгахыг зөвшөөрдөггүй.\n\n Залилагчид утасны дуудлагын харилцан ярианы үеэр ийм төрлийн үйлдлийг ихэвчлэн хүсдэг тул таныг хамгаалахаар уг үйлдлийг блоклосон. Хэрэв таны танихгүй хүн ийм үйлдэл хийлгэхээр таныг чиглүүлж байгаа бол энэ нь заль мэх байж болно."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Аппад <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>-д хандах эрх олгохоос татгалзсан"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Апп нь таны хувийн болон санхүүгийн мэдээллийг эрсдэлд оруулж болох эмзэг зөвшөөрөлд хандах эрх хүссэн.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Энэ хязгаарлагдмал зөвшөөрөлгүйгээр уг апп зохих ёсоор ажиллахгүй байх боломжтой. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Хандах эрхийг хэрхэн зөвшөөрөх талаар мэдэж авах&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Аппад өгөгдмөл <xliff:g id="ROLE_NAME">%1$s</xliff:g> болох эрх олгохоос татгалзсан"</string>
diff --git a/PermissionController/res/values-mr/strings.xml b/PermissionController/res/values-mr/strings.xml
index c8c259d93..8f8172131 100644
--- a/PermissionController/res/values-mr/strings.xml
+++ b/PermissionController/res/values-mr/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"प्रतिबंधित सेटिंग्जना अनुमती द्या"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"प्रतिबंधित सेटिंग"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"तुमच्या सुरक्षेसाठी, हे सेटिंग सध्या उपलब्ध नाही."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"फोन कॉलवर असताना कृती उपलब्ध नाही"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"फोन कॉलदरम्यान ॲप्सना इतर ॲप्स इंस्टॉल करण्याची अनुमती नाही.\n\n घोटाळेबाज व्यक्ती फोन कॉल संभाषणांदरम्यान, याप्रकारच्या कृतीची विनंती करतात, यामुळे तुमचे संरक्षण करण्यासाठी ती ब्लॉक केली आहे. तुम्ही ओळखत नसलेल्या एखाद्या व्यक्तीने ही कारवाई करण्यासाठी तुम्हाला मार्गदर्शन केले जात असल्यास, हा घोटाळा असू शकतो."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"अ‍ॅपचा <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> साठीचा अ‍ॅक्सेस नाकारला गेला आहे"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ॲपने संवेदनशील परवानगीसाठी अ‍ॅक्सेसची विनंती केली आहे, ज्यामुळे तुमची वैयक्तिक आणि आर्थिक माहिती धोक्यात येऊ शकते.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>या प्रतिबंधित परवानगीशिवाय ॲप कदाचित योग्यरीत्या काम करणार नाही. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;अ‍ॅक्सेसला अनुमती कशी द्यावी हे जाणून घ्या&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"अ‍ॅपचा डीफॉल्ट <xliff:g id="ROLE_NAME">%1$s</xliff:g> असण्यासाठीचा अ‍ॅक्सेस नाकारला गेला आहे"</string>
diff --git a/PermissionController/res/values-ms/strings.xml b/PermissionController/res/values-ms/strings.xml
index 6b65594b4..420e6244c 100644
--- a/PermissionController/res/values-ms/strings.xml
+++ b/PermissionController/res/values-ms/strings.xml
@@ -674,10 +674,12 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Benarkan tetapan terhad"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Tetapan terhad"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Untuk keselamatan anda, tetapan ini tidak tersedia pada masa ini."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Tindakan tidak tersedia semasa panggilan telefon"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Tindakan membenarkan apl memasang apl lain adalah tidak dibenarkan semasa panggilan telefon.\n\n Penipu sering meminta jenis tindakan ini semasa perbualan panggilan telefon, maka tindakan ini disekat untuk melindungi anda. Jika anda dibimbing untuk mengambil tindakan ini oleh seseorang yang anda tidak kenali, perkara ini mungkin merupakan komplot."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Akses apl kepada <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> telah ditolak"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Apl meminta akses kepada kebenaran sensitif yang boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Apl tersebut mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ketahui cara membenarkan akses&lt;/a&gt;"</string>
- <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Akses apl untuk menjadi <xliff:g id="ROLE_NAME">%1$s</xliff:g> secara lalai telah ditolak"</string>
- <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Apl meminta akses kepada kebenaran sensitif yang boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Apl tersebut mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ketahui cara membenarkan akses&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Akses apl untuk menjadi <xliff:g id="ROLE_NAME">%1$s</xliff:g> lalai telah ditolak"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Apl meminta akses kepada kebenaran sensitif yang boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Apl mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ketahui cara membenarkan akses&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Akses apl telah ditolak"</string>
<string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Akses kepada kebenaran ini boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Apl tersebut mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ketahui cara membenarkan akses&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Ketahui lebih lanjut"</string>
diff --git a/PermissionController/res/values-my/strings.xml b/PermissionController/res/values-my/strings.xml
index c488a296c..df6541f42 100644
--- a/PermissionController/res/values-my/strings.xml
+++ b/PermissionController/res/values-my/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"ကန့်သတ်ဆက်တင်များ ခွင့်ပြုရန်"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ကန့်သတ်ဆက်တင်"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"သင့်လုံခြုံရေးအတွက် ဤဆက်တင်ကို လောလောဆယ် မရနိုင်ပါ။"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ဖုန်းခေါ်နေစဉ်အတွင်း လုပ်ဆောင်ချက်များ မရနိုင်ပါ"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ဖုန်းခေါ်နေစဉ်အတွင်း အခြားအက်ပ်များ ထည့်သွင်းရန် အက်ပ်များအား ခွင့်ပြုခြင်းကို လုပ်ခွင့်မပြုပါ။\n\n လူလိမ်များသည် ဤကဲ့သို့ လုပ်ဆောင်ချက်ကို ဖုန်းပြောဆိုနေစဉ်အတွင်း တောင်းဆိုလေ့ရှိကြသောကြောင့် သင့်အား ကာကွယ်ပေးရန် ၎င်းကိုပိတ်ထားသည်။ သင်မသိသော ပုဂ္ဂိုလ်တစ်ဦးဦးက ဤလုပ်ဆောင်ချက်ကို ဆောင်ရွက်ရန် သင့်အားပြောဆိုနေပါက လိမ်လည်မှုဖြစ်နိုင်သည်။"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"အက်ပ်ကို <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> သုံးခွင့် ငြင်းပယ်ထားသည်"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"အက်ပ်သည် သင်၏ ပုဂ္ဂိုလ်ရေးနှင့် ငွေကြေးဆိုင်ရာ အချက်အလက်များကို အန္တရာယ်ဖြစ်စေနိုင်သော သတိထားရမည့် ခွင့်ပြုချက်သုံးရန် တောင်းဆိုထားသည်။<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>အက်ပ်သည် ဤကန့်သတ်ထားသော ခွင့်ပြုချက်မရှိပါက ကောင်းစွာမလုပ်ဆောင်ခြင်း ဖြစ်နိုင်သည်။ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;သုံးခွင့်ပြုနည်းကို လေ့လာရန်&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"အက်ပ်အား မူရင်း <xliff:g id="ROLE_NAME">%1$s</xliff:g> အဖြစ် လုပ်ဆောင်ခွင့် ငြင်းပယ်ထားသည်"</string>
diff --git a/PermissionController/res/values-nb/strings.xml b/PermissionController/res/values-nb/strings.xml
index bb6cb00d5..26f134e5d 100644
--- a/PermissionController/res/values-nb/strings.xml
+++ b/PermissionController/res/values-nb/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Tillat begrensede innstillinger"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Begrenset innstilling"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Av sikkerhetshensyn er denne innstillingen utilgjengelig for øyeblikket."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Handlingen er utilgjengelig under telefonsamtaler"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Det er ikke tillatt å la apper installere andre apper under telefonsamtaler.\n\n Svindlere ber ofte om slike handlinger under telefonsamtaler, så det er blokkert for å beskytte deg. Hvis noen du ikke kjenner, ber deg om å gjøre dette, kan det være svindel."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Appens tilgang til <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ble avvist"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Appen ba om tilgang til en sensitiv tillatelse som kan utsette den personlige og økonomiske informasjonen din for fare.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Det kan hende at appen ikke fungerer skikkelig uten denne begrensede tillatelsen. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Finn ut hvordan du gir tilgang&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appen fikk ikke tillatelse til å være standard <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-ne/strings.xml b/PermissionController/res/values-ne/strings.xml
index 70d474bfc..9a7a4739a 100644
--- a/PermissionController/res/values-ne/strings.xml
+++ b/PermissionController/res/values-ne/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"प्रतिबन्ध लगाइएका सेटिङ अनलक गर्नुहोस्"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"प्रतिबन्ध लगाइएका सेटिङ"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"तपाईंको खाताको सुरक्षार्थ यो सेटिङ हाल उपलब्ध छैन।"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"फोन कल चलिरहेका बेला यो कारबाही गर्न मिल्दैन"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"फोन कल चलिरहेका बेला एपहरूलाई अन्य एप इन्स्टल गर्ने अनुमति दिन मिल्दैन।\n\n स्क्याम गर्ने व्यक्तिले फोन कलका क्रममा प्रायः यस प्रकारको कारबाही गर्न अनुरोध गर्ने भएकाले तपाईंको सुरक्षार्थ यो कारबाही गर्न रोक लगाइएको हो। तपाईंलाई कुनै अपरिचित व्यक्तिले यो कारबाही गर्ने मार्गदर्शन गर्दै छ भने त्यो जालसाजी हुन सक्छ।"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"एपले <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> प्रयोग गर्न मागेको अनुमति अस्वीकार गरिएको छ"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"यो एपले तपाईंको व्यक्तिगत तथा वित्तीय जानकारी जोखिममा पार्न सक्ने खालको संवेदनशील अनुमति मागेको छ।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>तपाईले उक्त प्रतिबन्धित अनुमति नदिएका खण्डमा यो एपले राम्रोसँग काम नगर्न सक्छ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;अनुमति दिने तरिका सिक्नुहोस्&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"यो एपले डिफल्ट <xliff:g id="ROLE_NAME">%1$s</xliff:g> का रूपमा काम गर्न मागेको अनुमति अस्वीकार गरिएको छ"</string>
diff --git a/PermissionController/res/values-night-v33/themes.xml b/PermissionController/res/values-night-v33/themes.xml
index 9b6f638a6..31940ede5 100644
--- a/PermissionController/res/values-night-v33/themes.xml
+++ b/PermissionController/res/values-night-v33/themes.xml
@@ -52,6 +52,8 @@
@style/SecondarySafetyCenterActionButton.Responsive
</item>
+ <item name="scCardSideMargin">@dimen/sc_spacing_large</item>
+
<item name="textColorScActionButton">@color/sc_primary_action_button_text</item>
<item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
diff --git a/PermissionController/res/values-nl/strings.xml b/PermissionController/res/values-nl/strings.xml
index 64cba5c57..310d02ef1 100644
--- a/PermissionController/res/values-nl/strings.xml
+++ b/PermissionController/res/values-nl/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Beperkte instellingen toestaan"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Beperkte instelling"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ter beveiliging is deze instelling op dit moment niet beschikbaar."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Actie niet beschikbaar tijdens een telefoongesprek"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Apps toestaan om andere apps te installeren is niet toegestaan tijdens een telefoongesprek.\n\n Scammers vragen vaak om dit soort acties tijdens telefoongesprekken. Daarom is deze actie geblokkeerd om je te beschermen. Als iemand die je niet kent je aanspoort om deze actie uit te voeren, kan dit een scam zijn."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App heeft geen toegang gekregen tot <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"De app heeft toegang gevraagd tot een gevoelig recht, waardoor je persoonlijke en financiële informatie risico kunnen lopen.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Het is mogelijk dat de app niet goed werkt zonder dit beperkte recht. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Meer informatie over hoe je toegang geeft&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App heeft geen toegang gekregen om de standaard <xliff:g id="ROLE_NAME">%1$s</xliff:g> te worden"</string>
diff --git a/PermissionController/res/values-or/strings.xml b/PermissionController/res/values-or/strings.xml
index 69f4fded7..621e54e1a 100644
--- a/PermissionController/res/values-or/strings.xml
+++ b/PermissionController/res/values-or/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂସକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂ"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ ଏହି ସେଟିଂ ବର୍ତ୍ତମାନ ଅନୁପଲବ୍ଧ ଅଟେ।"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"କୌଣସି ଫୋନ କଲରେ ଥିବା ସମୟରେ କାର୍ଯ୍ୟ ଉପଲବ୍ଧ ନାହିଁ"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"କୌଣସି ଫୋନ କଲ ସମୟରେ ଅନ୍ୟ ଆପ୍ସକୁ ଇନଷ୍ଟଲ କରିବା ପାଇଁ ଆପ୍ସକୁ ଅନୁମତି ଦିଆଯାଏ ନାହିଁ।\n\n ଫୋନ କଲ ବାର୍ତ୍ତାଳାପ ସମୟରେ ସ୍କାମରମାନେ ପ୍ରାୟତଃ ଏହି ପ୍ରକାରର କାର୍ଯ୍ୟ ପାଇଁ ଅନୁରୋଧ କରନ୍ତି, ତେଣୁ ଆପଣଙ୍କୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ ଏହାକୁ ବ୍ଲକ କରାଯାଏ। ଯଦି ଆପଣଙ୍କୁ ଏହି ପଦକ୍ଷେପ ନେବାକୁ ଆପଣ ଜାଣିନଥିବା ବ୍ୟକ୍ତିଙ୍କ ଦ୍ୱାରା ମାର୍ଗଦର୍ଶନ କରାଯାଉଛି, ତେବେ ଏହା ଏକ ସ୍କାମ ହୋଇପାରେ।"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>କୁ ଆପର ଆକ୍ସେସକୁ ଅଗ୍ରାହ୍ୟ କରାଯାଇଛି"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଏବଂ ଆର୍ଥିକ ସୂଚନାକୁ ବିପଦରେ ପକାଇପାରୁଥିବା ଏକ ସମ୍ବେଦନଶୀଳ ଅନୁମତିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପଟି ଅନୁରୋଧ କରିଛି।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ଏହା ସମ୍ଭବ ଯେ ଏହି ପ୍ରତିବନ୍ଧିତ ଅନୁମତି ବିନା ଆପ ସଠିକ ଭାବେ କାର୍ଯ୍ୟ କରିବ ନାହିଁ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ଆକ୍ସେସକୁ କିପରି ଅନୁମତି ଦେବେ ତାହା ଜାଣନ୍ତୁ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ଡିଫଲ୍ଟ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ହେବା ପାଇଁ ଆପର ଆକ୍ସେସକୁ ଅଗ୍ରାହ୍ୟ କରାଯାଇଛି"</string>
diff --git a/PermissionController/res/values-pa/strings.xml b/PermissionController/res/values-pa/strings.xml
index f2f099d61..8984bf0fa 100644
--- a/PermissionController/res/values-pa/strings.xml
+++ b/PermissionController/res/values-pa/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗਾਂ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗਾਂ"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਲਈ, ਫ਼ਿਲਹਾਲ ਇਹ ਸੈਟਿੰਗ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ਫ਼ੋਨ ਕਾਲ \'ਤੇ ਹੋਣ ਦੌਰਾਨ ਕਾਰਵਾਈ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ਫ਼ੋਨ ਕਾਲ ਦੌਰਾਨ ਐਪਾਂ ਨੂੰ ਹੋਰ ਐਪਾਂ ਨੂੰ ਸਥਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੱਤੀ ਜਾਂਦੀ।\n\n ਘਪਲੇਬਾਜ਼ ਅਕਸਰ ਫ਼ੋਨ ਕਾਲ ਦੀਆਂ ਗੱਲਾਂਬਾਤਾਂ ਦੌਰਾਨ ਇਸ ਕਿਸਮ ਦੀ ਕਾਰਵਾਈ ਦੀ ਬੇਨਤੀ ਕਰਦੇ ਹਨ, ਇਸ ਲਈ ਤੁਹਾਨੂੰ ਸੁਰੱਖਿਅਤ ਰੱਖਣ ਲਈ ਇਸਨੂੰ ਬਲਾਕ ਕੀਤਾ ਹੁੰਦਾ ਹੈ। ਜੇ ਤੁਹਾਨੂੰ ਕਿਸੇ ਅਨਜਾਣ ਵਿਅਕਤੀ ਵੱਲੋਂ ਇਹ ਕਾਰਵਾਈ ਕਰਨ ਲਈ ਸੇਧਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਹ ਇੱਕ ਘਪਲਾ ਹੋਵੇ।"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ਐਪ ਨੂੰ <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੱਤੀ ਗਈ"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ਐਪ ਨੇ ਸੰਵੇਦਨਸ਼ੀਲ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੀ ਨਿੱਜੀ ਅਤੇ ਵਿੱਤੀ ਜਾਣਕਾਰੀ ਜੋਖਮ ਵਿੱਚ ਪੈ ਸਕਦੀ ਹੈ।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ਇਹ ਸੰਭਵ ਹੈ, ਕਿ ਐਪ ਇਸ ਪ੍ਰਤਿਬੰਧਿਤ ਇਜਾਜ਼ਤ ਤੋਂ ਬਿਨਾਂ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣ ਦੇ ਤਰੀਕੇ ਬਾਰੇ ਜਾਣੋ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ਐਪ ਨੂੰ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੱਤੀ ਗਈ"</string>
diff --git a/PermissionController/res/values-pl/strings.xml b/PermissionController/res/values-pl/strings.xml
index 0b40b41f6..ae98edba8 100644
--- a/PermissionController/res/values-pl/strings.xml
+++ b/PermissionController/res/values-pl/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Zezwól na ustawienia z ograniczonym dostępem"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ustawienie z ograniczonym dostępem"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ze względów bezpieczeństwa to ustawienie jest obecnie niedostępne."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Działanie niedostępne podczas rozmowy telefonicznej"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Podczas rozmowy telefonicznej nie można zezwolić aplikacjom na instalowanie innych aplikacji.\n\n Oszuści często proszą o takie działania podczas rozmów telefonicznych, dlatego blokujemy je, aby Cię chronić. Jeśli ktoś, kogo nie znasz, prosi Cię o podjęcie tego działania, może to być oszustwo."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikacja nie otrzymała dostępu do uprawnień <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacja wymaga dostępu do uprawnień newralgicznych, co może zagrażać Twoim danym osobowym i informacjom finansowym.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Możliwe, że bez tego uprawnienia z ograniczeniami aplikacja nie będzie działać poprawnie. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Jak zezwolić na dostęp&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikacja nie otrzymała dostępu jako domyślna <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-pt-rBR/strings.xml b/PermissionController/res/values-pt-rBR/strings.xml
index fea62d7e7..e49acd0c8 100644
--- a/PermissionController/res/values-pt-rBR/strings.xml
+++ b/PermissionController/res/values-pt-rBR/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir configurações restritas"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Configuração restrita"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sua segurança, essa configuração está indisponível no momento."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Ação indisponível durante uma ligação"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Não é possível permitir que apps instalem outros apps durante uma ligação.\n\n Golpistas costumam pedir esse tipo de ação durante ligações, então ela fica bloqueada para sua proteção. Se você está recebendo instruções de uma pessoa desconhecida para realizar essa ação, isso pode ser um golpe."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"O app não recebeu a seguinte permissão de acesso: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"O app solicitou acesso a uma permissão sensível que pode colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que o app não funcione corretamente sem essa permissão restrita. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Aprenda a conceder acesso&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"O app não recebeu o acesso para ser a escolha padrão de: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-pt-rPT/strings.xml b/PermissionController/res/values-pt-rPT/strings.xml
index 0ad01f7a5..9989caf2e 100644
--- a/PermissionController/res/values-pt-rPT/strings.xml
+++ b/PermissionController/res/values-pt-rPT/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir definições restritas"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Definição restrita"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sua segurança, esta definição está indisponível atualmente."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Ação não disponível durante uma chamada"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Não é permitido permitir que as apps instalem outras apps durante uma chamada telefónica.\n\n Os autores de esquemas pedem frequentemente este tipo de ação durante as conversas telefónicas, por isso, a opção é bloqueada para sua proteção. Se alguém que não conhece lhe pedir para realizar esta ação, pode tratar-se de um esquema."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"O acesso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> foi negado à app"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"A app pediu acesso a uma autorização confidencial que pode pôr em risco as suas informações pessoais e financeiras.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que a app não funcione corretamente sem esta autorização restrita. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saiba como permitir o acesso&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"O acesso à função predefinida <xliff:g id="ROLE_NAME">%1$s</xliff:g> foi negado à app"</string>
diff --git a/PermissionController/res/values-pt/strings.xml b/PermissionController/res/values-pt/strings.xml
index fea62d7e7..e49acd0c8 100644
--- a/PermissionController/res/values-pt/strings.xml
+++ b/PermissionController/res/values-pt/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir configurações restritas"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Configuração restrita"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sua segurança, essa configuração está indisponível no momento."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Ação indisponível durante uma ligação"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Não é possível permitir que apps instalem outros apps durante uma ligação.\n\n Golpistas costumam pedir esse tipo de ação durante ligações, então ela fica bloqueada para sua proteção. Se você está recebendo instruções de uma pessoa desconhecida para realizar essa ação, isso pode ser um golpe."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"O app não recebeu a seguinte permissão de acesso: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"O app solicitou acesso a uma permissão sensível que pode colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que o app não funcione corretamente sem essa permissão restrita. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Aprenda a conceder acesso&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"O app não recebeu o acesso para ser a escolha padrão de: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-ro/strings.xml b/PermissionController/res/values-ro/strings.xml
index 5f88c1376..6544022cf 100644
--- a/PermissionController/res/values-ro/strings.xml
+++ b/PermissionController/res/values-ro/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Permite setările restricționate"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Setare restricționată"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pentru securitatea ta, setarea este momentan indisponibilă."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Acțiune indisponibilă în timpul apelului telefonic"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Nu este permisă instalarea altor aplicații în timpul unui apel telefonic.\n\n Escrocii solicită adesea acest tip de acțiune în timpul conversațiilor telefonice, astfel că opțiunea este blocată pentru a te proteja. Dacă o persoană necunoscută te îndrumă să faci această acțiune, este posibil să fie o escrocherie."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Accesul aplicației la <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> a fost refuzat"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplicația a solicitat acces la o permisiune de accesare a informațiilor sensibile care îți poate pune în pericol informațiile financiare și cu caracter personal.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Este posibil ca aplicația să nu funcționeze corect fără această permisiune restricționată. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Află cum să permiți accesul&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Accesul aplicației pentru a deveni <xliff:g id="ROLE_NAME">%1$s</xliff:g> prestabilită a fost refuzat"</string>
diff --git a/PermissionController/res/values-ru/strings.xml b/PermissionController/res/values-ru/strings.xml
index a96a76df3..0aa620139 100644
--- a/PermissionController/res/values-ru/strings.xml
+++ b/PermissionController/res/values-ru/strings.xml
@@ -674,10 +674,12 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Разрешить доступ к настройкам"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Настройки с ограниченным доступом"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"В целях безопасности эти настройки пока недоступны."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Действие недоступно во время звонка."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Во время звонка нельзя давать приложениям разрешение на установку приложений.\n\n Об этом часто просят телефонные мошенники, поэтому мы заблокировали эту функцию, чтобы защитить вас. Если незнакомый человек предлагает вам предоставить такое разрешение, возможно, это мошенник."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Для приложения заблокировано разрешение \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\""</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Приложение запрашивает разрешение на доступ к конфиденциальной информации. Если вы предоставите его, ваши личные и финансовые данные могут оказаться под угрозой.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без такого разрешения приложение может работать неправильно. Узнайте, &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;как предоставить доступ к данным&lt;/a&gt;."</string>
- <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Запрещено использовать приложение в качестве стандартного в категории \"<xliff:g id="ROLE_NAME">%1$s</xliff:g>\""</string>
- <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Приложение запрашивает разрешения на доступ к конфиденциальной информации. Если вы предоставите их, ваши личные и финансовые данные могут оказаться под угрозой.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без таких разрешений приложение может работать неправильно. Узнайте, &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;как предоставить доступ к данным&lt;/a&gt;."</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Запрос на использование в качестве приложения по умолчанию (<xliff:g id="ROLE_NAME">%1$s</xliff:g>) отклонен"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Приложение запросило разрешения на доступ к конфиденциальной информации. Такие разрешения могут поставить ваши личные и финансовые данные под угрозу.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без них приложение может работать неправильно. Узнайте, &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;как предоставить доступ к данным&lt;/a&gt;."</string>
<string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Доступ запрещен"</string>
<string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Если вы предоставите это разрешение, ваши личные и финансовые данные могут оказаться под угрозой.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без него приложение может работать неправильно. Узнайте, &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;как предоставить доступ к данным&lt;/a&gt;."</string>
<string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Подробнее"</string>
diff --git a/PermissionController/res/values-si/strings.xml b/PermissionController/res/values-si/strings.xml
index fbec1af4f..4f0a78c39 100644
--- a/PermissionController/res/values-si/strings.xml
+++ b/PermissionController/res/values-si/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"සීමා කළ සැකසීම්වලට ඉඩ දෙන්න"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"සීමා කළ සැකසීම"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ඔබේ ආරක්ෂාව සඳහා, මෙම සැකසීම දැනට නොමැත."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"දුරකථන ඇමතුමක සිටින අතරතුර ක්‍රියාව ලබා ගත නොහැක."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"දුරකථන ඇමතුමක් අතරතුර යෙදුම් වලට වෙනත් යෙදුම් ස්ථාපනය කිරීමට ඉඩ නොදෙයි.\n\n වංචාකරුවන් බොහෝ විට දුරකථන ඇමතුම් සංවාද අතරතුර මෙවැනි ක්‍රියාමාර්ග ඉල්ලා සිටින බැවින්, ඔබව ආරක්ෂා කිරීමට එය අවහිර කර ඇත. ඔබ නොදන්නා කෙනෙකු විසින් මෙම ක්‍රියාමාර්ගය ගැනීමට ඔබට මඟ පෙන්වනු ලැබුවහොත්, එය වංචාවක් විය හැක."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"යෙදුම <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> වෙත ප්‍රවේශය ප්‍රතික්ෂේප කරන ලදි"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"යෙදුම ඔබේ පුද්ගලික සහ මූල්‍ය තොරතුරු අවදානමට ලක් කළ හැකි සංවේදී අවසරයකට ප්‍රවේශය ඉල්ලා ඇත.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>මෙම සීමා කළ අවසරය නොමැතිව යෙදුම නිසි ලෙස ක්‍රියා නොකරනු ඇත. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ප්‍රවේශයට ඉඩ දෙන ආකාරය දැන ගන්න&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"යෙදුම පෙරනිමි <xliff:g id="ROLE_NAME">%1$s</xliff:g> වීමට ප්‍රවේශය ප්‍රතික්ෂේප කරන ලදි"</string>
diff --git a/PermissionController/res/values-sk/strings.xml b/PermissionController/res/values-sk/strings.xml
index 2b0c845c5..d743a2237 100644
--- a/PermissionController/res/values-sk/strings.xml
+++ b/PermissionController/res/values-sk/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Povoliť obmedzené nastavenia"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Obmedzené nastavenie"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Z bezpečnostných dôvodov nie je toto nastavenie momentálne k dispozícii."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Akcia nie je počas telefonovania k dispozícii"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Počas telefonického hovoru nie je povolená inštalácia aplikácií inými aplikáciami.\n\n Podvodníci často žiadajú o tento typ akcie počas telefonických hovorov, preto je daná akcia zablokovaná z dôvodu vašej ochrany. Ak vás o túto akciu žiada niekto, koho nepoznáte, môže ísť o podvod."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikácii bol prístup k povoleniu <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> zamietnutý"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Táto aplikácia vyžaduje prístup k citlivému povoleniu, čo môže ohroziť vaše osobné údaje a finančné informácie.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Je možné, že bez tohto obmedzeného povolenia nebude aplikácia správne fungovať. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ako povoliť prístup&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikácii bol zamietnutý prístup a predvolene nemôže byť <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-sl/strings.xml b/PermissionController/res/values-sl/strings.xml
index 0e83c0d1f..d0317471e 100644
--- a/PermissionController/res/values-sl/strings.xml
+++ b/PermissionController/res/values-sl/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Dovoli omejene nastavitve"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Omejena nastavitev"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Zaradi vaše varnosti ta nastavitev trenutno ni na voljo."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Dejanje ni na voljo med telefonskim klicem"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Med telefonskim klicem ne morete aplikacijam omogočiti nameščanja drugih aplikacij.\n\n Prevaranti med pogovori v telefonskih klicih pogosto zahtevajo to vrsto dejanja, zato je to dejanje blokirano zaradi vaše zaščite. Če vas k temu dejanju napeljuje nekdo, ki ga ne poznate, gre morda za poskus prevare."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji je bil zavrnjen dostop do dovoljenja »<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>«"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zahtevala dostop do občutljivega dovoljenja, ki lahko ogrozi vaše osebne in finančne podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikacija morda ne bo pravilno delovala brez tega omejenega dovoljenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Preberite, kako omogočite dostop&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je bil zavrnjen dostop do privzete vloge »<xliff:g id="ROLE_NAME">%1$s</xliff:g>«"</string>
diff --git a/PermissionController/res/values-sq/strings.xml b/PermissionController/res/values-sq/strings.xml
index 4cca434cb..9979f58ad 100644
--- a/PermissionController/res/values-sq/strings.xml
+++ b/PermissionController/res/values-sq/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Lejo cilësimet e kufizuara"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Cilësim i kufizuar"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Për sigurinë tënde, ky cilësim nuk ofrohet për momentin."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Veprimi nuk ofrohet kur je në një telefonatë"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Nuk lejohet që aplikacionet të lejohen të instalojnë aplikacione të tjera gjatë një telefonate.\n\n Mashtruesit e kërkojnë shpesh këtë lloj veprimi gjatë bisedave të telefonatave, prandaj kjo është bllokuar për të të mbrojtur ty. Nëse po udhëzohesh ta kryesh këtë veprim nga dikush që nuk e njeh, mund të jetë një mashtrim."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikacionit iu refuzua qasja te: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacioni kërkoi qasje në një leje delikate që mund t\'i vendosë në rrezik informacionet e tua personale dhe financiare.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Ka mundësi që aplikacioni të mos funksionojë si duhet pa këtë leje të kufizuar. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Mëso se si të lejosh qasjen&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikacionit iu refuzua qasja për të qenë parazgjedhja për: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-sr/strings.xml b/PermissionController/res/values-sr/strings.xml
index d89ebba68..3cb9925be 100644
--- a/PermissionController/res/values-sr/strings.xml
+++ b/PermissionController/res/values-sr/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Дозволи ограничена подешавања"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ограничено подешавање"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ово подешавање је тренутно недоступно ради ваше безбедности."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Радња није доступна током телефонског позива"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Није дозвољено да апликације инсталирају друге апликације током телефонског позива.\n\n Преваранти често траже овај тип радње током телефонског позива, па смо је блокирали да бисмо вас заштитили. Ако вас неко кога не познајете упућује на ову радњу, то би могла да буде превара."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Апликацији није дозвољен приступ дозволи: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Апликација је затражила приступ осетљивој дозволи, што може да угрози безбедност личних и финансијских података.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Апликација можда неће радити исправно без ове ограничене дозволе. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Сазнајте како да дозволите приступ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Апликацији није дозвољен приступ да постане подразумевана: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-sv/strings.xml b/PermissionController/res/values-sv/strings.xml
index c5d3969de..0b3b3a4f6 100644
--- a/PermissionController/res/values-sv/strings.xml
+++ b/PermissionController/res/values-sv/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Tillåt blockerade inställningar"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Begränsad inställning"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Av säkerhetsskäl är den här inställningen inte tillgänglig för närvarande."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Åtgärden är inte tillgänglig under pågående samtal"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Det går inte att tillåta att appar installerar andra appar under ett telefonsamtal.\n\n Bedragare vill ofta att man gör sådana här saker under telefonsamtalet, så vi möjligheten blockeras för att skydda dig. Om du blir tillsagd att göra detta av någon du inte känner kan det vara ett bedrägeri."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Appen nekades åtkomst till <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Appen begärde åtkomstbehörighet till känsliga uppgifter, vilket kan utsätta din personliga och finansiella information för risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Appen kanske inte fungerar som den ska utan denna begränsade behörighet. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&amp;gtLäs mer om hur du tillåter åtkomst&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appen nekades åtkomst till standardrollen för <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-sw/strings.xml b/PermissionController/res/values-sw/strings.xml
index e774d92c1..102281bde 100644
--- a/PermissionController/res/values-sw/strings.xml
+++ b/PermissionController/res/values-sw/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Ruhusu mipangilio yenye mipaka"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Mipangilio imezuiwa"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ili kulinda usalama wako, mipangilio hii haipatikani kwa sasa."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Kitendo hiki hakipatikani unapopiga simu"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Programu haziruhusiwi kuweka programu nyingine kwenye kifaa wakati wa mazungumzo ya simu.\n\n Mara nyingi, walaghai hukuomba utekeleze vitendo vya aina hii wakati wa mazungumzo ya simu, kwa hivyo kimezuiwa ili kukulinda. Iwapo unaelekezwa utekeleze kitendo hiki na mtu usiyemjua, huenda ikawa ni ulaghai."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Programu haijapewa idhini ya kufikia <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Programu imeomba kufikia ruhusa nyeti, hali ambayo inaweza kuhatarisha maelezo yako ya kifedha na ya binafsi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Kuna uwezekano kuwa programu haitafanya kazi vizuri bila ruhusa hii inayodhibitiwa. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Pata maelezo kuhusu jinsi ya kuruhusu ufikiaji&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Programu haijapewa idhini ya kuwa <xliff:g id="ROLE_NAME">%1$s</xliff:g> chaguomsingi"</string>
diff --git a/PermissionController/res/values-ta/strings.xml b/PermissionController/res/values-ta/strings.xml
index e4f158e6f..8e1b71b97 100644
--- a/PermissionController/res/values-ta/strings.xml
+++ b/PermissionController/res/values-ta/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"கட்டுப்படுத்தப்பட்ட அமைப்பை அனுமதி"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"கட்டுப்படுத்தப்பட்ட அமைப்பு"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"உங்கள் பாதுகாப்பிற்காக, இந்த அமைப்பு தற்போது இல்லை."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"மொபைல் அழைப்பின்போது இந்தச் செயலைச் செய்ய முடியாது"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"மொபைல் அழைப்பின்போது ஓர் ஆப்ஸ் பிற ஆப்ஸை நிறுவ அனுமதிப்பது அனுமதிக்கப்படாது.\n\n மொபைல் அழைப்பு உரையாடல்களின்போது, மோசடி செய்பவர்கள் அடிக்கடி இதுபோன்ற செயலைச் செய்யுமாறு கேட்கின்றனர், எனவே இவற்றிலிருந்து உங்களைப் பாதுகாக்கவே அனுமதி தடுக்கப்பட்டுள்ளது. உங்களுக்குத் தெரியாத ஒருவரால் இந்தச் செயலைச் செய்வதற்கு நீங்கள் வழிகாட்டப்பட்டால் அது மோசடியாக இருக்கலாம்."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>க்கான ஆப்ஸ் அணுகல் நிராகரிக்கப்பட்டது"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"பாதுகாக்க வேண்டிய தகவல்களுக்கான அனுமதியை வழங்க ஆப்ஸ் கேட்டுள்ளது, இது உங்கள் தனிப்பட்ட மற்றும் நிதித் தகவல்களை ஆபத்துக்குள்ளாக்கலாம்.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>இந்தக் கட்டுப்படுத்தப்பட்ட அனுமதி இல்லாமல் ஆப்ஸ் சரியாக வேலை செய்யாமல் போகக்கூடும். &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;எப்படி அணுகல் வழங்குவதென அறிக&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"இயல்புநிலை <xliff:g id="ROLE_NAME">%1$s</xliff:g>க்கான ஆப்ஸ் அணுகல் நிராகரிக்கப்பட்டது"</string>
diff --git a/PermissionController/res/values-te/strings.xml b/PermissionController/res/values-te/strings.xml
index a461319a4..e920ad698 100644
--- a/PermissionController/res/values-te/strings.xml
+++ b/PermissionController/res/values-te/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"పరిమితం చేసిన సెట్టింగ్‌లను అనుమతించండి"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"పరిమితం చేయబడిన సెట్టింగ్"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"మీ సెక్యూరిటీ కోసం, ఈ సెట్టింగ్ ప్రస్తుతం అందుబాటులో లేదు."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ఫోన్ కాల్‌లో ఉండగా ఈ చర్య అందుబాటులో ఉండదు"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ఫోన్ కాల్ మాట్లాడే సమయంలో ఇతర యాప్‌లను ఇన్‌స్టాల్ చేయడానికి యాప్‌లను అనుమతించడానికి వీలు లేదు.\n\n ఫోన్ కాల్ సంభాషణల సమయంలో స్కామర్‌లు తరచుగా ఈ రకమైన చర్యను రిక్వెస్ట్ చేస్తారు, కాబట్టి మిమ్మల్ని రక్షించడానికి ఈ ఫీచర్ బ్లాక్ చేయబడుతుంది. మీకు తెలియని వారు ఈ చర్య తీసుకోవాల్సిందిగా మిమ్మల్ని గైడ్ చేస్తే, అది స్కామ్ కావచ్చు."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"యాప్ <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>‌కు యాక్సెస్ తిరస్కరించబడింది"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"మీ వ్యక్తిగత, ఆర్థిక సమాచారాన్ని ప్రమాదంలో పడేసే గోప్యమైన సమాచార యాక్సెస్ అనుమతికి యాప్ యాక్సెస్‌ను రిక్వెస్ట్ చేయడం జరిగింది.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ఈ అదనపు అవసరాలు గల అనుమతి లేకుండా యాప్ సరిగ్గా పని చేయకపోయే అవకాశం ఉంది. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;యాక్సెస్‌ను అనుమతించడం ఎలాగో తెలుసుకోండి&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ఆటోమేటిక్ <xliff:g id="ROLE_NAME">%1$s</xliff:g>‌గా ఉండటానికి యాప్‌నకు యాక్సెస్ నిరాకరించబడింది"</string>
diff --git a/PermissionController/res/values-th/strings.xml b/PermissionController/res/values-th/strings.xml
index 0943c6011..45b34108b 100644
--- a/PermissionController/res/values-th/strings.xml
+++ b/PermissionController/res/values-th/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"อนุญาตการตั้งค่าที่จำกัด"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"การตั้งค่าที่จำกัด"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"การตั้งค่านี้ใช้ไม่ได้ในตอนนี้เพื่อความปลอดภัยของคุณ"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"การดำเนินการไม่พร้อมใช้งานขณะกำลังโทรศัพท์"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ไม่อนุญาตให้แอปติดตั้งแอปอื่นๆ ระหว่างการโทร\n\n สแกมเมอร์มักขอการดำเนินการประเภทนี้ระหว่างการโทร ดังนั้นระบบจึงบล็อกไว้เพื่อปกป้องคุณ หากมีคนที่คุณไม่รู้จักบอกให้คุณดำเนินการดังกล่าว นั่นอาจเป็นการหลอกลวง"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"แอปถูกปฏิเสธไม่ให้เข้าถึง <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"แอปขอเข้าถึงสิทธิ์ที่มีความละเอียดอ่อนซึ่งอาจทำให้ข้อมูลส่วนบุคคลและข้อมูลทางการเงินของคุณมีความเสี่ยง<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>แอปอาจทำงานได้ไม่ถูกต้องหากไม่มีสิทธิ์ที่จำกัดนี้ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ดูวิธีอนุญาตให้เข้าถึง&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"แอปถูกปฏิเสธไม่ให้เข้าถึงเพื่อเป็น<xliff:g id="ROLE_NAME">%1$s</xliff:g> เริ่มต้น"</string>
diff --git a/PermissionController/res/values-tl/strings.xml b/PermissionController/res/values-tl/strings.xml
index f08b97531..70bcec228 100644
--- a/PermissionController/res/values-tl/strings.xml
+++ b/PermissionController/res/values-tl/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Payagan ang mga pinaghihigpitang setting"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Pinaghihigpitang setting"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sa iyong seguridad, hindi available ang setting na ito sa ngayon."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Hindi available ang pagkilos habang may tumatawag"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Hindi pinapayagan ang pagpayag sa pag-install ng mga app ng iba pang app habang may tawag sa telepono.\n\n Madalas na humihiling ng ganitong uri ng pagkilos ang mga scammer habang tumatawag sa telepono, kaya na-block ito para protektahan ka. Kung ginagabayan kang gawin ang pagkilos na ito ng taong hindi mo kilala, posibleng scam ito."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Tinanggihan ang access ng appp sa <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Humiling ang app ng access sa pahintulot sa sensitibong impormasyon na posibleng maglagay ng iyong personal at pinansyal na impormasyon sa panganib.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Posibleng hindi gumana nang maayos ang app kung wala ang pinaghihigpitang pahintulot na ito. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Alamin kung paano payagan ang pag-access&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Tinanggihan ang access ng app na maging default na <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-tr/strings.xml b/PermissionController/res/values-tr/strings.xml
index 467d98bdc..0b2d72f64 100644
--- a/PermissionController/res/values-tr/strings.xml
+++ b/PermissionController/res/values-tr/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Kısıtlanmış ayarlara izin verme"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Kısıtlanmış ayar"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Güvenliğiniz için bu ayar şu anda kullanılamıyor."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"İşlem, telefon görüşmesi sırasında yapılamaz"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefon görüşmesi sırasında uygulamaların başka uygulama yüklemesine izin verilmez.\n\n Dolandırıcıların genellikle telefon görüşmesi sırasında talep ettiği bu tür işlemler sizi korumak için engellenir. Tanımadığınız biri tarafından bu işlemi yapmanız isteniyorsa dolandırılıyor olabilirsiniz."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Uygulamanın <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> iznine erişimi reddedildi"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Uygulama, kişisel ve finansal bilgilerinizi riske atabilecek hassas bir izne erişim istedi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Bu kısıtlı izin olmadan uygulama düzgün çalışmayabilir. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Erişime nasıl izin vereceğinizi öğrenin&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Uygulamanın varsayılan <xliff:g id="ROLE_NAME">%1$s</xliff:g> olarak kullanılma erişimi reddedildi"</string>
diff --git a/PermissionController/res/values-uk/strings.xml b/PermissionController/res/values-uk/strings.xml
index b05fb81e4..7f94a193c 100644
--- a/PermissionController/res/values-uk/strings.xml
+++ b/PermissionController/res/values-uk/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Дозволити налаштування з обмеженнями"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Налаштування з обмеженнями"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"З міркувань безпеки це налаштування наразі недоступне."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Дія недоступна під час телефонного виклику"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Під час телефонного виклику не можна надавати додаткам дозвіл на встановлення інших додатків.\n\n Шахраї часто просять виконати такі дії під час телефонних розмов, тому цю можливість заблоковано задля вашого захисту. Якщо незнайома особа просить вас виконати таку дію, це може бути шахрайством."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Додатку не надано дозвіл \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\""</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Додаток запитав дозвіл на доступ до чутливих даних, що може поставити під загрозу вашу особисту й фінансову інформацію.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без цього обмеженого дозволу додаток може не працювати належним чином. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Як надати доступ&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Додатку не надано доступ до ролі \"<xliff:g id="ROLE_NAME">%1$s</xliff:g>\" за умовчанням"</string>
diff --git a/PermissionController/res/values-ur/strings.xml b/PermissionController/res/values-ur/strings.xml
index 3b4fa91db..cc9182c1e 100644
--- a/PermissionController/res/values-ur/strings.xml
+++ b/PermissionController/res/values-ur/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"پابندی والی ترتیبات کی اجازت دیں"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"محدود ترتیب"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"آپ کی سیکیورٹی کیلئے، یہ ترتیب فی الحال دستیاب نہیں ہے۔"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"فون کال کے دوران کارروائی دستیاب نہیں ہے"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"فون کال کے دوران ایپس کو دوسری ایپس انسٹال کرنے کی اجازت دینا ممنوع ہے۔\n\n دھوکے باز اکثر فون کال گفتگوؤں کے دوران اس قسم کی کارروائی کی درخواست کرتے ہیں، اس لیے اسے آپ کی حفاظت کے لیے مسدود کر دیا جاتا ہے۔ اگر آپ کو کسی ایسے شخص کے ذریعے سے یہ کارروائی کرنے کے لیے رہنمائی کی جا رہی ہے جس کو آپ نہیں جانتے ہیں تو یہ ایک فریب کاری ہو سکتی ہے۔"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> تک ایپ کی رسائی کو مسترد کر دیا گیا"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"‏ایپ نے ایک حساس اجازت تک رسائی کی درخواست کی ہے جو آپ کی ذاتی اور مالی معلومات کو خطرے میں ڈال سکتی ہے۔<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>یہ ممکن ہے کہ اس محدود اجازت کے بغیر ایپ ٹھیک سے کام نہ کرے۔ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;رسائی کی اجازت دینے کا طریقہ جانیں&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ڈیفالٹ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ہونے کے لیے ایپ کی رسائی کو مسترد کر دیا گیا"</string>
diff --git a/PermissionController/res/values-uz/strings.xml b/PermissionController/res/values-uz/strings.xml
index a1f04e4cf..55a271e39 100644
--- a/PermissionController/res/values-uz/strings.xml
+++ b/PermissionController/res/values-uz/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Cheklangan sozlamalarga ruxsat berish"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Cheklangan sozlama"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Xavfsizlik maqsadida bu sozlama hozir ishlamaydi."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Telefon chaqiruvi paytida amal ishlamaydi"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefon chaqiruvi paytida ilovalarga boshqa ilovalarni oʻrnatishga ruxsat berish mumkin emas.\n\n Tovlamachilar koʻpincha telefon chaqiruvi orqali suhbatlar paytida shu turdagi amalni bajarishni soʻraydi, shu bois u sizni himoyalash uchun bloklanadi. Sizga notanish biror kishi shu amalni bajarishga undasa, bu tovlamachilik boʻlishi mumkin."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Ilovaga <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ruxsati berilmadi"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Ilova maxfiy maʼlumotlarga kirish uchun ruxsat soʻramoqda. Ruxsat bersangiz, shaxsiy va moliyaviy maʼlumotlaringiz xavf ostida qolishi mumkin.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g> Bu cheklangan ruxsatsiz ilova toʻgʻri ishlamasligi mumkin. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Qanday ruxsat berish haqida batafsil&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Ilovadan <xliff:g id="ROLE_NAME">%1$s</xliff:g> turkumidagi standart sifatida foydalanish taqiqlangan"</string>
diff --git a/PermissionController/res/values-v33/attrs.xml b/PermissionController/res/values-v33/attrs.xml
index 4a417076d..b136ab718 100644
--- a/PermissionController/res/values-v33/attrs.xml
+++ b/PermissionController/res/values-v33/attrs.xml
@@ -39,8 +39,9 @@
<attr name="scStatusButtonStyle" format="reference" />
<attr name="scActionButtonListLayout" format="reference" />
<attr name="scActionButtonTheme" format="reference" />
- <attr name="scActionButtonStyle" format="reference"/>
- <attr name="scSecondaryActionButtonStyle" format="reference"/>
+ <attr name="scActionButtonStyle" format="reference" />
+ <attr name="scSecondaryActionButtonStyle" format="reference" />
+ <attr name="scCardSideMargin" format="dimension" />
<attr name="colorScShieldAccent" format="color" />
</resources> \ No newline at end of file
diff --git a/PermissionController/res/values-v33/styles.xml b/PermissionController/res/values-v33/styles.xml
index 94344615b..7e959c2f4 100644
--- a/PermissionController/res/values-v33/styles.xml
+++ b/PermissionController/res/values-v33/styles.xml
@@ -317,8 +317,8 @@
<item name="android:paddingEnd">@dimen/sc_spacing_xxxlarge</item>
<item name="android:paddingTop">@dimen/sc_spacing_xxxlarge</item>
<item name="android:paddingBottom">@dimen/sc_card_margin_bottom</item>
- <item name="android:layout_marginStart">@dimen/sc_spacing_large</item>
- <item name="android:layout_marginEnd">@dimen/sc_spacing_large</item>
+ <item name="android:layout_marginStart">?attr/scCardSideMargin</item>
+ <item name="android:layout_marginEnd">?attr/scCardSideMargin</item>
<item name="android:background">@drawable/safety_center_card_background</item>
</style>
diff --git a/PermissionController/res/values-v33/themes.xml b/PermissionController/res/values-v33/themes.xml
index 82a8ef5b6..a3d1b16c3 100644
--- a/PermissionController/res/values-v33/themes.xml
+++ b/PermissionController/res/values-v33/themes.xml
@@ -57,6 +57,8 @@
@style/SecondarySafetyCenterActionButton.Fixed
</item>
+ <item name="scCardSideMargin">@dimen/sc_spacing_large</item>
+
<item name="textColorScActionButton">@color/sc_primary_action_button_text</item>
<item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
@@ -69,6 +71,10 @@
<style name="Theme.SafetyCenterQs" parent="Theme.SafetyCenterQsBase" />
+ <style name="Theme.SafetyCenterQsExpressive" parent="Theme.SafetyCenterQs">
+ <item name="scCardSideMargin">0dp</item>
+ </style>
+
<style name="Theme.SafetyCenterBase" parent="Theme.PermissionController.Settings.FilterTouches">
<item name="colorSurface">@color/sc_surface_light</item>
<item name="colorSurfaceVariant">@color/sc_surface_variant_light</item>
@@ -104,6 +110,8 @@
@style/SecondarySafetyCenterActionButton.Responsive
</item>
+ <item name="scCardSideMargin">@dimen/sc_spacing_large</item>
+
<item name="textColorScActionButton">@color/sc_primary_action_button_text</item>
<item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
@@ -115,4 +123,8 @@
</style>
<style name="Theme.SafetyCenter" parent="Theme.SafetyCenterBase" />
+
+ <style name="Theme.SafetyCenterExpressive" parent="Theme.SafetyCenter">
+ <item name="scCardSideMargin">0dp</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/PermissionController/res/values-v35/styles.xml b/PermissionController/res/values-v35/styles.xml
index 612125fb1..513754bb8 100644
--- a/PermissionController/res/values-v35/styles.xml
+++ b/PermissionController/res/values-v35/styles.xml
@@ -197,5 +197,221 @@
<item name="android:visibility">gone</item>
</style>
+ <style name="PermissionSelectorWithWidgetPreferenceRootLayoutStyle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
+ <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceWidgetFrameStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:paddingHorizontal">20dp</item>
+ <item name="android:gravity">center</item>
+ <item name="android:minWidth">56dp</item>
+ <item name="android:orientation">vertical</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceIconFrameStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:minWidth">32dp</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:layout_marginEnd">16dp</item>
+ <item name="android:paddingTop">4dp</item>
+ <item name="android:paddingBottom">4dp</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceIconStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="maxWidth">@dimen/secondary_app_icon_size</item>
+ <item name="maxHeight">@dimen/secondary_app_icon_size</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceTextContainerStyle">
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_weight">1</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:paddingTop">16dp</item>
+ <item name="android:paddingBottom">16dp</item>
+ <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceTitleStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:maxLines">2</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceListItem</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceSummaryContainerStyle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:visibility">gone</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceSummaryStyle">
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_weight">1</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textAlignment">viewStart</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceAppendixStyle">
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_weight">1</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textAlignment">viewEnd</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:maxLines">1</item>
+ <item name="android:visibility">gone</item>
+ <item name="android:ellipsize">end</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceExtraWidgetContainerStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:gravity">center_vertical</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceExtraWidgetDividerStyle">
+ <item name="android:layout_width">.75dp</item>
+ <item name="android:layout_height">32dp</item>
+ <item name="android:layout_marginTop">16dp</item>
+ <item name="android:layout_marginBottom">16dp</item>
+ <item name="android:background">?android:attr/dividerVertical</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceExtraWidgetImageStyle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:minWidth">@dimen/two_target_min_width</item>
+ <item name="android:layout_height">fill_parent</item>
+ <item name="android:src">@drawable/ic_settings_accent</item>
+ <item name="android:paddingStart">24dp</item>
+ <item name="android:paddingEnd">24dp</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ </style>
+
+ <style name="PermissionSelectorWithWidgetPreferenceWidgetRadioButton">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:background">@null</item>
+ <item name="android:focusable">false</item>
+ <item name="android:clickable">false</item>
+ </style>
+
+ <style name="PermissionTwoTargetPreferenceRootLayoutStyle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
+ <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item>
+ <item name="android:clipToPadding">false</item>
+ </style>
+
+ <style name="PermissionTwoTargetPreferenceTextContainerStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_weight">1</item>
+ <item name="android:paddingTop">16dp</item>
+ <item name="android:paddingBottom">16dp</item>
+ </style>
+
+ <style name="PermissionTwoTargetPreferenceTitleStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:maxLines">2</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceListItem</item>
+ <item name="android:ellipsize">marquee</item>
+ </style>
+
+ <style name="PermissionTwoTargetPreferenceSummaryStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_below">@android:id/title</item>
+ <item name="android:layout_alignStart">@android:id/title</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceListItemSecondary</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
+ <item name="android:lineBreakWordStyle">phrase</item>
+ <item name="android:maxLines">10</item>
+ </style>
+
+ <style name="PermissionTwoTargetPreferenceWidgetFrameStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:minWidth">@dimen/two_target_min_width</item>
+ <item name="android:gravity">center</item>
+ <item name="android:orientation">vertical</item>
+ </style>
+
+ <style name="PermissionTwoTargetPreferenceIconFrameStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:minWidth">48dp</item>
+ <item name="android:gravity">start|center_vertical</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:paddingLeft">0dp</item>
+ <item name="android:paddingStart">0dp</item>
+ <item name="android:paddingRight">8dp</item>
+ <item name="android:paddingEnd">8dp</item>
+ <item name="android:paddingTop">4dp</item>
+ <item name="android:paddingBottom">4dp</item>
+ </style>
+
+ <style name="PermissionTwoTargetPreferenceIconStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="maxWidth">48dp</item>
+ <item name="maxHeight">48dp</item>
+ </style>
+
+ <style name="PermissionTwoTargetPreferenceDividerContainerStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:gravity">start|center_vertical</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:paddingStart">?android:attr/listPreferredItemPaddingEnd</item>
+ <item name="android:paddingLeft">?android:attr/listPreferredItemPaddingEnd</item>
+ <item name="android:paddingTop">16dp</item>
+ <item name="android:paddingBottom">16dp</item>
+ </style>
+
+ <style name="PermissionTwoTargetPreferenceDividerStyle">
+ <item name="android:layout_width">1dp</item>
+ <item name="android:layout_height">32dp</item>
+ <item name="android:background">?android:attr/listDivider</item>
+ </style>
+
+ <style name="AppPermissionFooterLinkPreferenceRootLayoutStyle"
+ parent="PermissionPreferenceRootLayoutStyle" />
+
+ <style name="AppPermissionFooterLinkPreferenceTextLayoutStyle"
+ parent="PermissionPreferenceTextRelativeLayoutStyle" />
+
+ <style name="AppPermissionFooterLinkPreferenceSummaryStyle"
+ parent="PermissionPreferenceSummaryTextStyle" />
+
<!-- END PREFERENCE STYLES -->
</resources> \ No newline at end of file
diff --git a/PermissionController/res/values-vi/strings.xml b/PermissionController/res/values-vi/strings.xml
index b9a077e77..fe798bab0 100644
--- a/PermissionController/res/values-vi/strings.xml
+++ b/PermissionController/res/values-vi/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Cho phép các chế độ cài đặt bị hạn chế"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Chế độ cài đặt bị hạn chế"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Để đảm bảo an toàn cho bạn, chế độ cài đặt này hiện không dùng được."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Không thao tác được khi đang gọi điện"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Không cho phép ứng dụng cài đặt các ứng dụng khác trong khi bạn gọi điện.\n\n Kẻ lừa đảo thường yêu cầu bạn cài đặt ứng dụng trong khi gọi điện, vì vậy, hành động này bị chặn để bảo vệ bạn. Nếu người mà bạn không biết hướng dẫn bạn cài đặt ứng dụng, thì đó có thể là thủ đoạn lừa đảo."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Ứng dụng đã bị từ chối cấp quyền truy cập vào <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Ứng dụng này yêu cầu quyền truy cập vào thông tin nhạy cảm. Việc đó có thể khiến thông tin cá nhân và thông tin tài chính của bạn gặp rủi ro.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Có thể ứng dụng sẽ hoạt động không đúng cách nếu không được cấp quyền bị hạn chế này. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Tìm hiểu cách cấp quyền&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Ứng dụng đã bị từ chối cấp quyền để làm <xliff:g id="ROLE_NAME">%1$s</xliff:g> mặc định"</string>
diff --git a/PermissionController/res/values-watch/donottranslate.xml b/PermissionController/res/values-watch/donottranslate.xml
index c3ab3cbb1..43830a93c 100644
--- a/PermissionController/res/values-watch/donottranslate.xml
+++ b/PermissionController/res/values-watch/donottranslate.xml
@@ -28,4 +28,55 @@
<string name="wear_material_compose_caption_1_font_family">font-family-text-medium-device-default</string>
<string name="wear_material_compose_caption_2_font_family">font-family-text-medium-device-default</string>
<string name="wear_material_compose_caption_3_font_family">font-family-text-medium-device-default</string>
+
+ <string name="wear_compose_material3_arc_small_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_arc_medium_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_arc_large_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_body_extra_small_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_body_small_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_body_medium_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_body_large_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_display_small_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_display_medium_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_display_large_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_label_small_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_label_medium_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_label_large_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_numeral_extra_small_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_numeral_small_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_numeral_medium_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_numeral_large_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_numeral_extra_large_font_family">font-family-flex-device-default</string>
+ <string name="wear_compose_material3_title_small_font_family">font-family-flex-device-default</string>
+ <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">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">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-rCN/strings.xml b/PermissionController/res/values-zh-rCN/strings.xml
index ccd1d3183..ee6f9e3bf 100644
--- a/PermissionController/res/values-zh-rCN/strings.xml
+++ b/PermissionController/res/values-zh-rCN/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"允许受限制的设置"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"受限制的设置"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"出于安全考虑,此设置目前不可用。"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"通话期间无法执行此类操作"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"在通话期间,不允许应用安装其他应用。\n\n诈骗者经常会在通话过程中要求执行此类操作,所以系统阻止了此类操作来保障您的安全。如果您正在陌生人的指引下执行此类操作,要当心,这可能是骗局。"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"系统已拒绝向此应用授予<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>访问权限"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"该应用请求获得敏感权限,授予这项权限可能会导致您的个人信息和财务信息面临风险。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如果不授予这项受限权限,该应用可能无法正常运行。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;了解如何授予访问权限&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系统已拒绝向此应用授予作为默认<xliff:g id="ROLE_NAME">%1$s</xliff:g>的访问权限"</string>
diff --git a/PermissionController/res/values-zh-rHK/strings.xml b/PermissionController/res/values-zh-rHK/strings.xml
index 07a401ab3..96ab9ae32 100644
--- a/PermissionController/res/values-zh-rHK/strings.xml
+++ b/PermissionController/res/values-zh-rHK/strings.xml
@@ -674,9 +674,11 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"允許受限設定"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"受限設定"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"為安全起見,系統目前不提供此設定。"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"通話期間無法執行此操作"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"通話期間,應用程式不可安裝其他應用程式。\n\n由於騙徒經常在電話對話中要求這類操作,因此我們封鎖此功能以保障你的安全。如果有陌生人引導你執行此操作,那可能是詐騙。"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"系統已拒絕授予應用程式「<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>」存取權"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"應用程式要求存取敏感資料權限,授予此權限可能會危害你的個人和財務資料。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如沒有此受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;進一步瞭解如何授予存取權&lt;/a&gt;"</string>
- <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系統已拒絕授予應用程式預設<xliff:g id="ROLE_NAME">%1$s</xliff:g>存取權"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系統已拒絕授予應用程式作為預設<xliff:g id="ROLE_NAME">%1$s</xliff:g>的存取權"</string>
<string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"應用程式要求存取敏感資料權限,授予此權限可能會危害你的個人和財務資料。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如沒有此受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;進一步瞭解如何授予存取權&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"系統已拒絕授予應用程式存取權"</string>
<string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"授予此權限可能會危害你的個人和財務資料。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如沒有此受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;進一步瞭解如何授予存取權&lt;/a&gt;"</string>
diff --git a/PermissionController/res/values-zh-rTW/strings.xml b/PermissionController/res/values-zh-rTW/strings.xml
index 408107f8a..e98024c52 100644
--- a/PermissionController/res/values-zh-rTW/strings.xml
+++ b/PermissionController/res/values-zh-rTW/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"解除受限制的設定"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"受限制的設定"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"為了安全起見,目前無法使用這項設定。"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"無法在通話期間執行這項操作"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"通話期間不允許應用程式安裝其他應用程式。\n\n詐騙者經常會在電話對話中要求這類操作,因此系統加以封鎖來保護你的安全。如果有陌生人引導你採取這類操作,請提高警覺,這可能是詐騙行為。"</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"系統已拒絕授予應用程式「<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>」存取權"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"這個應用程式要求取得私密資訊權限,授予這項權限可能導致你的個人資訊和財務資訊面臨風險。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如果未取得這項受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;瞭解如何授予權限&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系統已拒絕授予應用程式做為預設「<xliff:g id="ROLE_NAME">%1$s</xliff:g>」的存取權"</string>
diff --git a/PermissionController/res/values-zu/strings.xml b/PermissionController/res/values-zu/strings.xml
index 4669b2506..95f837307 100644
--- a/PermissionController/res/values-zu/strings.xml
+++ b/PermissionController/res/values-zu/strings.xml
@@ -674,6 +674,8 @@
<string name="allow_restricted_settings" msgid="8073000189478396881">"Vumela amasethingi akhawulelwe"</string>
<string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Amasethingi akhawulelwe"</string>
<string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ukuze uphephe, leli sethingi okwamanje alitholakali."</string>
+ <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Isenzo asitholakali ngenkathi ufona"</string>
+ <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Ukuvumela ama-app ukuthi afake amanye ama-app akuvunyelwe phakathi nekholi yefoni.\n\n Amaqola, ngokuvamile acela lolu hlobo lwesenzo phakathi nezingxoxo zekholi, ngakho-ke kuvinjiwe ukuze uvikeleke. Uma uqondiswa ukuthi wenze lesi senzo ngumuntu ongamazi, kungase kube umkhonyovu."</string>
<string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"I-app inqatshelwe ukufinyelela ku-<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
<string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"I-app icele ukufinyelela emvumweni ezwelayo okungabeka imininingwane yakho siqu neyezimali engozini.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Kungenzeka ukuthi i-app ingasebenzi kahle ngaphandle kwemvume enomkhawulo. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Funda ukuthi ungakuvumela kanjani ukufinyelela&lt;/a&gt;"</string>
<string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"I-app iye yanqatshelwa ukufinyelela ukuze ibe yi-<xliff:g id="ROLE_NAME">%1$s</xliff:g> ezenzakalelayo"</string>
diff --git a/PermissionController/res/values/bools.xml b/PermissionController/res/values/bools.xml
index b5f33b081..4483fc48f 100644
--- a/PermissionController/res/values/bools.xml
+++ b/PermissionController/res/values/bools.xml
@@ -20,4 +20,6 @@
<bool name="is_at_least_t">false</bool>
<bool name="is_at_least_u">false</bool>
<bool name="is_at_least_v">false</bool>
+ <bool name="config_usePreferenceForAppPermissionSettings">false</bool>
+ <bool name="config_appPermissionFooterLinkPreferenceSummaryUnderlined">false</bool>
</resources>
diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml
index 9075fa67c..ea7929746 100644
--- a/PermissionController/res/values/overlayable.xml
+++ b/PermissionController/res/values/overlayable.xml
@@ -48,7 +48,37 @@
<item type="style" name="PermissionFooterPreferenceTitleTextStyle" />
<item type="style" name="PermissionFooterPreferenceLearnMoreTextStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceRootLayoutStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceWidgetFrameStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceIconFrameStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceIconStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceTextContainerStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceTitleStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceSummaryContainerStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceSummaryStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceAppendixStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceExtraWidgetContainerStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceExtraWidgetDividerStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceExtraWidgetImageStyle" />
+ <item type="style" name="PermissionSelectorWithWidgetPreferenceWidgetRadioButton" />
+
+ <item type="style" name="PermissionTwoTargetPreferenceRootLayoutStyle" />
+ <item type="style" name="PermissionTwoTargetPreferenceTextContainerStyle" />
+ <item type="style" name="PermissionTwoTargetPreferenceTitleStyle" />
+ <item type="style" name="PermissionTwoTargetPreferenceSummaryStyle" />
+ <item type="style" name="PermissionTwoTargetPreferenceWidgetFrameStyle" />
+ <item type="style" name="PermissionTwoTargetPreferenceIconFrameStyle" />
+ <item type="style" name="PermissionTwoTargetPreferenceIconStyle" />
+ <item type="style" name="PermissionTwoTargetPreferenceDividerContainerStyle" />
+ <item type="style" name="PermissionTwoTargetPreferenceDividerStyle" />
+
+ <item type="style" name="AppPermissionFooterLinkPreferenceRootLayoutStyle" />
+ <item type="style" name="AppPermissionFooterLinkPreferenceTextLayoutStyle" />
+ <item type="style" name="AppPermissionFooterLinkPreferenceSummaryStyle" />
+
+ <item type="bool" name="config_usePreferenceForAppPermissionSettings" />
<item type="bool" name="config_permissionFooterPreferenceIconVisible" />
+ <item type="bool" name="config_appPermissionFooterLinkPreferenceSummaryUnderlined" />
<item type="dimen" name="permission_preference_app_icon_size" />
<item type="dimen" name="permission_preference_permission_group_icon_size" />
@@ -431,7 +461,7 @@
<item type="style" name="AppDataSharingUpdateSettingsIcon" />
<!-- END SAFETY LABELS STYLE -->
- <!--START WEAR SPECIFIC FONT STRINGS -->
+ <!--START WEAR SPECIFIC MATERIAL2 FONT STRINGS -->
<item type="string" name="wear_material_compose_display_1_font_family" />
<item type="string" name="wear_material_compose_display_2_font_family" />
<item type="string" name="wear_material_compose_display_3_font_family" />
@@ -444,7 +474,75 @@
<item type="string" name="wear_material_compose_caption_1_font_family" />
<item type="string" name="wear_material_compose_caption_2_font_family" />
<item type="string" name="wear_material_compose_caption_3_font_family" />
- <!--END WEAR SPECIFIC FONT STRINGS -->
+ <!--END WEAR SPECIFIC MATERIAL2 FONT STRINGS -->
+
+ <!--START WEAR SPECIFIC MATERIAL3 FONT FACE TOKENS-->
+ <item type="string" name="wear_compose_material3_arc_small_font_family" />
+ <item type="string" name="wear_compose_material3_arc_medium_font_family" />
+ <item type="string" name="wear_compose_material3_arc_large_font_family" />
+
+ <item type="string" name="wear_compose_material3_body_extra_small_font_family" />
+ <item type="string" name="wear_compose_material3_body_small_font_family" />
+ <item type="string" name="wear_compose_material3_body_medium_font_family" />
+ <item type="string" name="wear_compose_material3_body_large_font_family" />
+
+ <item type="string" name="wear_compose_material3_display_small_font_family" />
+ <item type="string" name="wear_compose_material3_display_medium_font_family" />
+ <item type="string" name="wear_compose_material3_display_large_font_family" />
+
+ <item type="string" name="wear_compose_material3_label_small_font_family" />
+ <item type="string" name="wear_compose_material3_label_medium_font_family" />
+ <item type="string" name="wear_compose_material3_label_large_font_family" />
+
+ <item type="string" name="wear_compose_material3_numeral_extra_small_font_family" />
+ <item type="string" name="wear_compose_material3_numeral_small_font_family" />
+ <item type="string" name="wear_compose_material3_numeral_medium_font_family" />
+ <item type="string" name="wear_compose_material3_numeral_large_font_family" />
+ <item type="string" name="wear_compose_material3_numeral_extra_large_font_family" />
+
+ <item type="string" name="wear_compose_material3_title_small_font_family" />
+ <item type="string" name="wear_compose_material3_title_medium_font_family" />
+ <item type="string" name="wear_compose_material3_title_large_font_family" />
+ <!--END WEAR SPECIFIC MATERIAL3 FONT FACE TOKENS-->
+
+
+ <!--START WEAR SPECIFIC MATERIAL3 FONT SIZE TOKENS-->
+ <item type="dimen" name="wear_compose_material3_arc_small_font_size" />
+ <item type="dimen" name="wear_compose_material3_arc_medium_font_size" />
+ <item type="dimen" name="wear_compose_material3_arc_large_font_size" />
+
+ <item type="dimen" name="wear_compose_material3_body_extra_small_font_size" />
+ <item type="dimen" name="wear_compose_material3_body_small_font_size" />
+ <item type="dimen" name="wear_compose_material3_body_medium_font_size" />
+ <item type="dimen" name="wear_compose_material3_body_large_font_size" />
+
+ <item type="dimen" name="wear_compose_material3_display_small_font_size" />
+ <item type="dimen" name="wear_compose_material3_display_medium_font_size" />
+ <item type="dimen" name="wear_compose_material3_display_large_font_size" />
+
+ <item type="dimen" name="wear_compose_material3_label_small_font_size" />
+ <item type="dimen" name="wear_compose_material3_label_medium_font_size" />
+ <item type="dimen" name="wear_compose_material3_label_large_font_size" />
+
+ <item type="dimen" name="wear_compose_material3_numeral_extra_small_font_size" />
+ <item type="dimen" name="wear_compose_material3_numeral_small_font_size" />
+ <item type="dimen" name="wear_compose_material3_numeral_medium_font_size" />
+ <item type="dimen" name="wear_compose_material3_numeral_large_font_size" />
+ <item type="dimen" name="wear_compose_material3_numeral_extra_large_font_size" />
+
+ <item type="dimen" name="wear_compose_material3_title_small_font_size" />
+ <item type="dimen" name="wear_compose_material3_title_medium_font_size" />
+ <item type="dimen" name="wear_compose_material3_title_large_font_size" />
+ <!--END WEAR SPECIFIC MATERIAL3 FONT SIZE TOKENS-->
+
+ <!--START WEAR SPECIFIC MATERIAL3 SHAPE TOKENS-->
+ <item type="dimen" name="wear_compose_material3_shape_corner_extra_small_size" />
+ <item type="dimen" name="wear_compose_material3_shape_corner_small_size" />
+ <item type="dimen" name="wear_compose_material3_shape_corner_medium_size" />
+ <item type="dimen" name="wear_compose_material3_shape_corner_large_size" />
+ <item type="dimen" name="wear_compose_material3_shape_corner_extra_large_size" />
+ <!--END WEAR SPECIFIC MATERIAL3 SHAPE TOKENS-->
+
<!-- START ENHANCED CONFIRMATION DIALOG -->
<item type="style" name="Theme.EnhancedConfirmationDialog" />
diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml
index d67a3582d..82c7889c6 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -1243,6 +1243,17 @@
<string name="role_wallet_request_title">Set <xliff:g id="app_name" example="Super Wallet">%1$s</xliff:g> as your default wallet app?</string>
<string name="role_wallet_request_description">No permissions needed</string>
+ <!-- Label for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] -->
+ <string name="role_for_testing_profile_group_exclusivity_label" translatable="false">Default test profile group exclusive role app</string>
+ <!-- Short label for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] -->
+ <string name="role_for_testing_profile_group_exclusivity_short_label" translatable="false">Test profile group exclusive role app</string>
+ <!-- Description for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] -->
+ <string name="role_for_testing_profile_group_exclusivity_description" translatable="false">Test profile group exclusive role apps are for tests only and should not be held by production apps.</string>
+ <!-- Request title for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] -->
+ <string name="role_for_testing_profile_group_exclusivity_request_title" translatable="false">Set <xliff:g id="app_name" example="Super test app">%1$s</xliff:g> as your default test profile group exclusive role app?</string>
+ <!-- Request description for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] -->
+ <string name="role_for_testing_profile_group_exclusivity_request_description" translatable="false">No permissions needed</string>
+
<!-- Subtitle for the application that is the current default application [CHAR LIMIT=30] -->
<string name="request_role_current_default">Current default</string>
@@ -2005,6 +2016,13 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo
<!--Content for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=NONE] -->
<string name="enhanced_confirmation_dialog_desc">For your security, this setting is currently unavailable.</string>
+ <!--Title for dialog displayed to tell user that settings are blocked due to the phone state (such as being in a call with an unknown number) [CHAR LIMIT=50] -->
+ <string name="enhanced_confirmation_phone_state_dialog_title">Action not available while on a phone call</string>
+ <!--Content for dialog displayed to tell user that settings are blocked due to the phone state (such as being in a call with an unknown number) [CHAR LIMIT=NONE] -->
+ <string name="enhanced_confirmation_phone_state_dialog_desc">Allowing apps to install other apps is not allowed during a phone call.\n\n
+ Scammers often request this type of action during phone call conversations, so it\u2019s blocked to protect you. If you are being guided to take this action
+ by someone you don\u2019t know, it might be a scam.</string>
+
<!--Title for dialog displayed to tell user that permissions are blocked by setting restrictions [CHAR LIMIT=50] -->
<string name="enhanced_confirmation_dialog_title_permission">App was denied access to <xliff:g id="permission_name" example="contacts">%1$s</xliff:g></string>
<!--Content for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=NONE] -->
diff --git a/PermissionController/res/xml/app_permission.xml b/PermissionController/res/xml-v35/app_permission.xml
index 5e6857185..87315815d 100644
--- a/PermissionController/res/xml/app_permission.xml
+++ b/PermissionController/res/xml-v35/app_permission.xml
@@ -19,46 +19,46 @@
android:key="app_permission_button_category"
android:title="@string/app_permission_header">
- <com.android.permissioncontroller.permission.ui.handheld.PermissionSelectorWithWidgetPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference
android:key="app_permission_allow_radio_button"
android:title="@string/app_permission_button_allow"
app:checkboxId="@+id/allow_radio_button" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionSelectorWithWidgetPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference
android:key="app_permission_allow_always_radio_button"
android:title="@string/app_permission_button_allow_always"
app:checkboxId="@+id/allow_always_radio_button"
app:isPreferenceVisible="false" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionSelectorWithWidgetPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference
android:key="app_permission_allow_foreground_only_radio_button"
android:title="@string/app_permission_button_allow_foreground"
app:checkboxId="@+id/allow_foreground_only_radio_button" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionSelectorWithWidgetPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference
android:key="app_permission_select_photos_radio_button"
android:title="@string/app_permission_button_allow_limited_access"
app:checkboxId="@+id/select_radio_button"
app:extraWidgetIcon="@drawable/ic_edit"
app:extraWidgetId="@+id/edit_selected_button" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionSelectorWithWidgetPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference
android:key="app_permission_ask_one_time_radio_button"
android:title="@string/app_permission_button_ask"
app:checkboxId="@+id/ask_one_time_radio_button" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionSelectorWithWidgetPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference
android:key="app_permission_ask_radio_button"
android:text="@string/app_permission_button_ask"
android:title="@string/app_permission_button_ask"
app:checkboxId="@+id/ask_radio_button" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionSelectorWithWidgetPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference
android:key="app_permission_deny_radio_button"
android:title="@string/app_permission_button_deny"
app:checkboxId="@+id/deny_radio_button" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionSelectorWithWidgetPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference
android:key="app_permission_deny_foreground_radio_button"
android:title="@string/app_permission_button_deny"
app:checkboxId="@+id/deny_foreground_radio_button" />
@@ -71,7 +71,7 @@
android:title="@string/app_permission_location_accuracy"
app:isPreferenceVisible="false" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionTwoTargetPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionTwoTargetPreference
android:key="app_permission_details"
android:selectable="false"
android:summary="@string/permission_summary_enabled_system_fixed"
@@ -79,26 +79,24 @@
app:iconSpaceReserved="true"
app:isPreferenceVisible="false" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.AppPermissionFooterLinkPreference
android:key="app_permission_footer_link_1"
android:summary="@string/app_permission_footer_app_permissions_link" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionPreference
+ <com.android.permissioncontroller.permission.ui.handheld.v36.AppPermissionFooterLinkPreference
android:key="app_permission_footer_link_2"
android:summary="@string/app_permission_footer_permission_apps_link" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionPreference
+ <com.android.permissioncontroller.permission.ui.handheld.PermissionFooterPreference
android:key="app_permission_footer_storage_special_app_access"
android:icon="@drawable/ic_info_outline"
- android:selectable="false"
- android:summary="@string/app_permission_footer_special_file_access"
+ android:title="@string/app_permission_footer_special_file_access"
app:isPreferenceVisible="false" />
- <com.android.permissioncontroller.permission.ui.handheld.PermissionPreference
+ <com.android.permissioncontroller.permission.ui.handheld.PermissionFooterPreference
android:key="app_permission_additional_info"
android:icon="@drawable/ic_info_outline"
- android:selectable="false"
- android:summary="@string/exempt_info_label"
+ android:title="@string/exempt_info_label"
app:isPreferenceVisible="false" />
</PreferenceScreen>
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index 1d01f8208..fb12ed0d0 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"
@@ -154,9 +155,13 @@
minSdkVersion="35" optionalMinSdkVersion="34" />
<permission name="android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE"
minSdkVersion="35" />
+ <permission name="android.permission.READ_SYSTEM_PREFERENCES"
+ featureFlag="com.android.settingslib.flags.Flags.settingsCatalyst" />
</permissions>
<app-op-permissions>
<app-op-permission name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <app-op-permission name="android.permission.WRITE_SYSTEM_PREFERENCES"
+ featureFlag="com.android.settingslib.flags.Flags.writeSystemPreferencePermissionEnabled" />
</app-op-permissions>
</role>
@@ -173,6 +178,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 +222,7 @@
defaultHolders="config_defaultDialer"
description="@string/role_dialer_description"
exclusive="true"
+ exclusivity="user"
fallBackToDefaultHolder="true"
label="@string/role_dialer_label"
overrideUserWhenGranting="true"
@@ -304,6 +311,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 +402,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 +435,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 +482,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 +504,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 +530,7 @@
name="android.app.role.SYSTEM_GALLERY"
defaultHolders="config_systemGallery"
exclusive="true"
+ exclusivity="user"
static="true"
systemOnly="true"
visible="false">
@@ -537,6 +550,7 @@
behavior="v31.AutomotiveRoleBehavior"
defaultHolders="config_systemAutomotiveCluster"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -554,6 +568,7 @@
behavior="v31.CompanionDeviceWatchRoleBehavior"
description="@string/role_watch_description"
exclusive="false"
+ exclusivity="none"
minSdkVersion="31"
systemOnly="false"
visible="false">
@@ -582,6 +597,7 @@
name="android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION"
defaultHolders="config_systemAutomotiveProjection"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -619,13 +635,13 @@
behavior="v31.SystemShellRoleBehavior"
defaultHolders="config_systemShell"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
visible="false">
<permissions>
<!-- Used for CTS testing -->
- <permission name="android.permission.CREATE_VIRTUAL_DEVICE" minSdkVersion="33" />
<permission name="android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE" />
<permission name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" minSdkVersion="33"/>
<permission name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION" />
@@ -642,8 +658,6 @@
minSdkVersion="33" />
<permission name="android.permission.MANAGE_SAFETY_CENTER"
minSdkVersion="33" />
- <permission name="android.permission.ADD_TRUSTED_DISPLAY" minSdkVersion="33" />
- <permission name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" minSdkVersion="33" />
<permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
minSdkVersion="33" />
<permission name="android.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT"
@@ -710,6 +724,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.app.admin.flags.Flags.splitCreateManagedProfileEnabled" />
+ <permission name="android.permission.REMOVE_ACCOUNTS"
+ featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" />
</permissions>
</role>
@@ -717,6 +735,7 @@
name="android.app.role.SYSTEM_CONTACTS"
defaultHolders="config_systemContacts"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -733,6 +752,7 @@
allowBypassingQualification="true"
defaultHolders="config_systemSpeechRecognizer"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -754,6 +774,7 @@
name="android.app.role.SYSTEM_WIFI_COEX_MANAGER"
defaultHolders="config_systemWifiCoexManager"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -768,6 +789,7 @@
name="android.app.role.SYSTEM_WELLBEING"
defaultHolders="config_systemWellbeing"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -796,6 +818,7 @@
behavior="v31.TelevisionRoleBehavior"
defaultHolders="config_systemTelevisionNotificationHandler"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -814,6 +837,7 @@
name="android.app.role.SYSTEM_COMPANION_DEVICE_PROVIDER"
defaultHolders="config_systemCompanionDeviceProvider"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -835,7 +859,8 @@
~ In addition, packages MUST NOT:
~ - Request INTERNET permission. Instead packages MUST access the internet through
~ well-defined APIs in an open source project.
- ~ - Perform direct binds to other applications, except the following system packages:
+ ~ - Perform direct binds to other applications, except the following system packages or
+ ~ other preloaded packages conforming with the requirements here:
~ - Bluetooth
~ - Contacts
~ - Media
@@ -849,6 +874,7 @@
name="android.app.role.SYSTEM_UI_INTELLIGENCE"
defaultHolders="config_systemUiIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -904,6 +930,7 @@
name="android.app.role.SYSTEM_AMBIENT_AUDIO_INTELLIGENCE"
defaultHolders="config_systemAmbientAudioIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -950,6 +977,7 @@
name="android.app.role.SYSTEM_AUDIO_INTELLIGENCE"
defaultHolders="config_systemAudioIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -996,6 +1024,7 @@
name="android.app.role.SYSTEM_NOTIFICATION_INTELLIGENCE"
defaultHolders="config_systemNotificationIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1038,6 +1067,7 @@
name="android.app.role.SYSTEM_TEXT_INTELLIGENCE"
defaultHolders="config_systemTextIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1078,6 +1108,7 @@
name="android.app.role.SYSTEM_VISUAL_INTELLIGENCE"
defaultHolders="config_systemVisualIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1103,6 +1134,7 @@
name="android.app.role.SYSTEM_DOCUMENT_MANAGER"
behavior="v33.DocumentManagerRoleBehavior"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1135,6 +1167,7 @@
allowBypassingQualification="true"
defaultHolders="config_systemActivityRecognizer"
exclusive="false"
+ exclusivity="none"
static="true"
systemOnly="true"
visible="false">
@@ -1154,6 +1187,7 @@
name="android.app.role.SYSTEM_UI"
defaultHolders="config_systemUi"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1180,6 +1214,7 @@
behavior="v31.TelevisionRoleBehavior"
defaultHolders="config_systemTelevisionRemoteService"
exclusive="true"
+ exclusivity="user"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -1199,6 +1234,7 @@
behavior="v33.CompanionDeviceAppStreamingRoleBehavior"
description="@string/role_app_streaming_description"
exclusive="false"
+ exclusivity="none"
minSdkVersion="33"
systemOnly="true"
visible="false">
@@ -1207,7 +1243,9 @@
<permission-set name="virtual_device" />
<!-- For capturing audio from the app on the device. -->
<permission name="android.permission.RECORD_AUDIO" />
-
+ <permission
+ name="android.permission.ADD_MIRROR_DISPLAY"
+ featureFlag="android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole" />
<!--TODO(b/201605314) For calling Telecom framework API for audio streaming-->
<!--<permission name="android.permission.PROVIDE_CALL_ENDPOINTS" />-->
</permissions>
@@ -1223,6 +1261,7 @@
behavior="v33.CompanionDeviceComputerRoleBehavior"
description="@string/role_companion_device_computer_description"
exclusive="false"
+ exclusivity="none"
minSdkVersion="33"
systemOnly="true"
visible="false">
@@ -1242,6 +1281,7 @@
name="android.app.role.COMPANION_DEVICE_GLASSES"
behavior="v34.CompanionDeviceGlassesRoleBehavior"
exclusive="false"
+ exclusivity="none"
minSdkVersion="34"
systemOnly="false"
visible="false">
@@ -1268,12 +1308,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>
@@ -1281,6 +1324,7 @@
name="android.app.role.SYSTEM_SUPERVISION"
defaultHolders="config_systemSupervision"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1291,6 +1335,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>
@@ -1303,6 +1387,7 @@
behavior="v33.DevicePolicyManagementRoleBehavior"
defaultHolders="config_devicePolicyManagement"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="false"
@@ -1408,6 +1493,12 @@
<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.MANAGE_DEVICE_POLICY_APP_FUNCTIONS"
+ featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" />
+ <permission name="android.permission.COPY_ACCOUNTS"
+ featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" />
+ <permission name="android.permission.REMOVE_ACCOUNTS"
+ featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" />
</permissions>
</role>
@@ -1415,6 +1506,7 @@
name="android.app.role.SYSTEM_APP_PROTECTION_SERVICE"
defaultHolders="config_systemAppProtectionService"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1443,6 +1535,7 @@
behavior="v31.AutomotiveRoleBehavior"
defaultHolders="config_systemAutomotiveCalendarSyncManager"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1464,6 +1557,7 @@
defaultHolders="config_defaultAutomotiveNavigation"
description="@string/role_automotive_navigation_description"
exclusive="true"
+ exclusivity="user"
label="@string/role_automotive_navigation_label"
minSdkVersion="33"
overrideUserWhenGranting="true"
@@ -1537,6 +1631,7 @@
name="android.app.role.SYSTEM_SETTINGS_INTELLIGENCE"
defaultHolders="config_systemSettingsIntelligence"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1554,6 +1649,7 @@
name="android.app.role.SYSTEM_BLUETOOTH_STACK"
defaultHolders="config_systemBluetoothStack"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1578,6 +1674,7 @@
<role
name="android.app.role.FINANCED_DEVICE_KIOSK"
exclusive="true"
+ exclusivity="user"
minSdkVersion="34"
visible="false">
<permissions>
@@ -1593,6 +1690,7 @@
name="android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER"
defaultHolders="config_systemFinancedDeviceController"
exclusive="true"
+ exclusivity="user"
minSdkVersion="34"
static="true"
systemOnly="true"
@@ -1620,6 +1718,7 @@
behavior="v33.SystemWearHealthServiceRoleBehavior"
defaultHolders="config_systemWearHealthService"
exclusive="true"
+ exclusivity="user"
minSdkVersion="33"
static="true"
systemOnly="true"
@@ -1629,6 +1728,12 @@
<permission-set name="location" />
<permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<permission name="android.permission.ACTIVITY_RECOGNITION" />
+ <permission
+ name="android.permission.health.READ_HEART_RATE"
+ featureFlag="android.permission.flags.Flags.replaceBodySensorPermissionEnabled" />
+ <permission
+ name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND"
+ featureFlag="android.permission.flags.Flags.replaceBodySensorPermissionEnabled" />
</permissions>
</role>
@@ -1641,6 +1746,7 @@
defaultHolders="config_defaultNotes"
description="@string/role_notes_description"
exclusive="true"
+ exclusivity="user"
label="@string/role_notes_label"
minSdkVersion="34"
overrideUserWhenGranting="true"
@@ -1682,6 +1788,7 @@
allowBypassingQualification="true"
defaultHolders="config_systemCallStreaming"
exclusive="true"
+ exclusivity="user"
minSdkVersion="34"
static="true"
systemOnly="true"
@@ -1704,6 +1811,7 @@
behavior="v35.RetailDemoRoleBehavior"
defaultHolders="config_defaultRetailDemo"
exclusive="true"
+ exclusivity="user"
minSdkVersion="35"
static="true"
visible="false">
@@ -1730,6 +1838,7 @@
defaultHolders="config_defaultWallet"
description="@string/role_wallet_description"
exclusive="true"
+ exclusivity="user"
label="@string/role_wallet_label"
minSdkVersion="35"
overrideUserWhenGranting="true"
@@ -1740,5 +1849,102 @@
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"
+ behavior="ReservedForTestingProfileGroupExclusivityRoleBehavior"
+ description="@string/role_for_testing_profile_group_exclusivity_description"
+ exclusive="true"
+ exclusivity="profileGroup"
+ fallBackToDefaultHolder="true"
+ featureFlag="com.android.permission.flags.Flags.crossUserRoleEnabled"
+ label="@string/role_for_testing_profile_group_exclusivity_label"
+ requestable="true"
+ requestDescription="@string/role_for_testing_profile_group_exclusivity_request_description"
+ requestTitle="@string/role_for_testing_profile_group_exclusivity_request_title"
+ shortLabel="@string/role_for_testing_profile_group_exclusivity_short_label"
+ showNone="true"
+ uiBehavior="ReservedForTestingProfileGroupExclusivityRoleUiBehavior"
+ visible="true"/>
+
+ <!---
+ ~ A role for the vendor package that provides privacy-preserving intelligent processor for
+ ~ vendor specific features.
+ ~
+ ~ A package holding this role MUST comply with requirements outlined in the Android CDD
+ ~ section "9.8.6 Content Capture".
+ ~ Example link for Android 15:
+ ~ https://source.android.com/docs/compatibility/15/android-15-cdd#986_os-level_and_ambient_data
+ ~
+ ~ In addition, packages MUST NOT:
+ ~ - Request INTERNET permission. Instead packages MUST access the internet through
+ ~ well-defined APIs in an open source project.
+ ~ - Perform direct binds to other applications, except the following system packages or
+ ~ other preloaded packages conforming with the requirements here:
+ ~ - Bluetooth
+ ~ - Contacts
+ ~ - Media
+ ~ - Telephony
+ ~ - System UI
+ ~ - Component providing internet APIs (see above)
+ ~ To achieve this packages MUST set up explicit <allow-association> configuration in the
+ ~ system config.
+ -->
+ <role
+ name="android.app.role.SYSTEM_VENDOR_INTELLIGENCE"
+ defaultHolders="config_systemVendorIntelligence"
+ exclusive="true"
+ exclusivity="user"
+ featureFlag="android.permission.flags.Flags.systemVendorIntelligenceRoleEnabled"
+ static="true"
+ systemOnly="true"
+ visible="false">
+ <permissions>
+ <permission name="android.permission.READ_GLOBAL_APP_SEARCH_DATA" />
+ <permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED"
+ featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" />
+ </permissions>
+ </role>
+
+ <role
+ name="android.app.role.COMPANION_DEVICE_SENSOR_DEVICE_STREAMING"
+ allowBypassingQualification="true"
+ exclusive="false"
+ exclusivity="none"
+ featureFlag="android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole"
+ systemOnly="true"
+ visible="false">
+ <permissions>
+ <permission-set name="nearby_devices" />
+ <permission-set name="notifications" />
+ <permission name="android.permission.CREATE_VIRTUAL_DEVICE"/>
+ </permissions>
+ </role>
</roles>
diff --git a/PermissionController/role-controller/Android.bp b/PermissionController/role-controller/Android.bp
index 166823b08..f89fd140c 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,13 +27,18 @@ java_library {
],
libs: [
"androidx.annotation_annotation",
+ "com.android.permission.flags-aconfig-java",
+ "framework-annotations-lib",
],
static_libs: [
"modules-utils-build_system",
+ "aconfig_settingslib_exported_flags_java_lib",
"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",
+ "device_policy_aconfig_flags_java_export",
],
apex_available: [
"com.android.permission",
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java
new file mode 100644
index 000000000..f02b4d90c
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java
@@ -0,0 +1,89 @@
+/*
+ * 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.role.controller.behavior;
+
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.util.PackageUtils;
+import com.android.role.controller.util.RoleFlags;
+import com.android.role.controller.util.UserUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// TODO(b/383538899): make minSdk36
+public class ReservedForTestingProfileGroupExclusivityRoleBehavior implements RoleBehavior {
+ @Nullable
+ @Override
+ public List<String> getDefaultHoldersAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ if (RoleFlags.isProfileGroupExclusivityAvailable()) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ RoleManager userRoleManager = userContext.getSystemService(RoleManager.class);
+ return userRoleManager.getDefaultHoldersForTest(role.getName());
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ if (RoleFlags.isProfileGroupExclusivityAvailable()) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ RoleManager userRoleManager = userContext.getSystemService(RoleManager.class);
+ return userRoleManager.isRoleVisibleForTest(role.getName());
+ } else {
+ return false;
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<String> getQualifyingPackagesAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ if (RoleFlags.isProfileGroupExclusivityAvailable()) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ RoleManager userRoleManager = userContext.getSystemService(RoleManager.class);
+ List<String> qualifyingPackageNames =
+ userRoleManager.getDefaultHoldersForTest(role.getName());
+
+ // When getQualifyingPackagesAsUser returns a package that isn't installed, Default App
+ // Settings fails to load. Only return available packages.
+ List<String> availableQualifyingPackageNames = new ArrayList<>();
+ for (int i = 0; i < qualifyingPackageNames.size(); i++) {
+ String qualifyingPackage = qualifyingPackageNames.get(i);
+ ApplicationInfo applicationInfo =
+ PackageUtils.getApplicationInfoAsUser(qualifyingPackage, user, context);
+ if (applicationInfo != null) {
+ availableQualifyingPackageNames.add(qualifyingPackage);
+ }
+ }
+ return availableQualifyingPackageNames;
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java b/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java
index 1c71e0c99..56c4944a0 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java
@@ -23,6 +23,7 @@ import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.modules.utils.build.SdkLevel;
import com.android.role.controller.util.PackageUtils;
@@ -130,7 +131,8 @@ public class AppOp {
return Permissions.setAppOpUidModeAsUser(packageName, mName, defaultMode, user, context);
}
- boolean isAvailableByFeatureFlagAndSdkVersion() {
+ @VisibleForTesting
+ public boolean isAvailableByFeatureFlagAndSdkVersion() {
if (mFeatureFlag != null && !mFeatureFlag.get()) {
return false;
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java b/PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java
index e788fdce1..820ff3d4e 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java
@@ -51,11 +51,13 @@ public class Permissions {
private static final boolean DEBUG = false;
+ private static final Object sPermissionInfoLock = new Object();
+ private static final ArrayMap<String, Boolean> sIsRuntimePermission = new ArrayMap<>();
+ private static final ArrayMap<String, Boolean> sIsRestrictedPermission = new ArrayMap<>();
+
+ private static final Object sForegroundBackgroundPermissionMappingsLock = new Object();
private static ArrayMap<String, String> sForegroundToBackgroundPermission;
private static ArrayMap<String, List<String>> sBackgroundToForegroundPermissions;
- private static final Object sForegroundBackgroundPermissionMappingsLock = new Object();
-
- private static final ArrayMap<String, Boolean> sRestrictedPermissions = new ArrayMap<>();
/**
* Filter a list of permissions based on their SDK versions.
@@ -86,8 +88,9 @@ public class Permissions {
*
* @param packageName the package name of the application to be granted permissions to
* @param permissions the list of permissions to be granted
- * @param overrideDisabledSystemPackage whether to ignore the permissions of a disabled system
- * package (if this package is an updated system package)
+ * @param ignoreDisabledSystemPackage whether to ignore the requested permissions of a disabled
+ * system package (if this package is an updated system
+ * package) when granting runtime permissions
* @param overrideUserSetAndFixed whether to override user set and fixed flags on the permission
* @param setGrantedByRole whether the permissions will be granted as granted-by-role
* @param setGrantedByDefault whether the permissions will be granted as granted-by-default
@@ -101,7 +104,7 @@ public class Permissions {
* PackageInfo, java.util.Set, boolean, boolean, int)
*/
public static boolean grantAsUser(@NonNull String packageName,
- @NonNull List<String> permissions, boolean overrideDisabledSystemPackage,
+ @NonNull List<String> permissions, boolean ignoreDisabledSystemPackage,
boolean overrideUserSetAndFixed, boolean setGrantedByRole, boolean setGrantedByDefault,
boolean setSystemFixed, @NonNull UserHandle user, @NonNull Context context) {
if (setGrantedByRole == setGrantedByDefault) {
@@ -145,15 +148,17 @@ public class Permissions {
// choice to grant this app the permissions needed to function. For all other
// apps, (default grants on first boot and user creation) we don't grant default
// permissions if the version on the system image does not declare them.
- if (!overrideDisabledSystemPackage && isUpdatedSystemApp(packageInfo)) {
+ if (!ignoreDisabledSystemPackage && isUpdatedSystemApp(packageInfo)) {
PackageInfo disabledSystemPackageInfo = getFactoryPackageInfoAsUser(packageName, user,
context);
if (disabledSystemPackageInfo != null) {
- if (ArrayUtils.isEmpty(disabledSystemPackageInfo.requestedPermissions)) {
- return false;
+ for (int i = permissionsToGrant.size() - 1; i >= 0; i--) {
+ String permission = permissionsToGrant.valueAt(i);
+ if (isRuntimePermission(permission, context) && !ArrayUtils.contains(
+ disabledSystemPackageInfo.requestedPermissions, permission)) {
+ permissionsToGrant.removeAt(i);
+ }
}
- CollectionUtils.retainAll(permissionsToGrant,
- disabledSystemPackageInfo.requestedPermissions);
if (permissionsToGrant.isEmpty()) {
return false;
}
@@ -258,7 +263,8 @@ public class Permissions {
if (!wasPermissionOrAppOpGranted) {
// If we've granted a permission which wasn't granted, it's no longer user set or fixed.
newMask |= PackageManager.FLAG_PERMISSION_USER_FIXED
- | PackageManager.FLAG_PERMISSION_USER_SET;
+ | PackageManager.FLAG_PERMISSION_USER_SET
+ | PackageManager.FLAG_PERMISSION_ONE_TIME;
}
// If a component gets a permission for being the default handler A and also default handler
// B, we grant the weaker grant form. This only applies to default permission grant.
@@ -629,7 +635,8 @@ public class Permissions {
}
if (!overrideUserSetAndFixed) {
fixedFlags |= PackageManager.FLAG_PERMISSION_USER_FIXED
- | PackageManager.FLAG_PERMISSION_USER_SET;
+ | PackageManager.FLAG_PERMISSION_USER_SET
+ | PackageManager.FLAG_PERMISSION_ONE_TIME;
}
return (flags & fixedFlags) != 0;
}
@@ -701,6 +708,50 @@ public class Permissions {
return true;
}
+ private static boolean isRuntimePermission(@NonNull String permission,
+ @NonNull Context context) {
+ synchronized (sPermissionInfoLock) {
+ Boolean isRuntimePermission = sIsRuntimePermission.get(permission);
+ if (isRuntimePermission != null) {
+ return isRuntimePermission;
+ }
+ fetchPermissionInfoLocked(permission, context);
+ return sIsRuntimePermission.get(permission);
+ }
+ }
+
+ private static boolean isRestrictedPermission(@NonNull String permission,
+ @NonNull Context context) {
+ synchronized (sPermissionInfoLock) {
+ Boolean isRestrictedPermission = sIsRestrictedPermission.get(permission);
+ if (isRestrictedPermission != null) {
+ return isRestrictedPermission;
+ }
+ fetchPermissionInfoLocked(permission, context);
+ return sIsRestrictedPermission.get(permission);
+ }
+ }
+
+ private static void fetchPermissionInfoLocked(@NonNull String permission,
+ @NonNull Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ PermissionInfo permissionInfo = null;
+ try {
+ permissionInfo = packageManager.getPermissionInfo(permission, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Cannot get PermissionInfo for permission: " + permission);
+ }
+
+ // Don't expect that to be a transient error, so we can still cache the failed information.
+ boolean isRuntimePermission = permissionInfo != null
+ && permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS;
+ boolean isRestrictedPermission = permissionInfo != null
+ && (permissionInfo.flags & (PermissionInfo.FLAG_SOFT_RESTRICTED
+ | PermissionInfo.FLAG_HARD_RESTRICTED)) != 0;
+ sIsRuntimePermission.put(permission, isRuntimePermission);
+ sIsRestrictedPermission.put(permission, isRestrictedPermission);
+ }
+
private static boolean isForegroundPermission(@NonNull String permission,
@NonNull Context context) {
ensureForegroundBackgroundPermissionMappings(context);
@@ -731,40 +782,13 @@ public class Permissions {
synchronized (sForegroundBackgroundPermissionMappingsLock) {
if (sForegroundToBackgroundPermission == null
&& sBackgroundToForegroundPermissions == null) {
- createForegroundBackgroundPermissionMappings(context);
+ createForegroundBackgroundPermissionMappingsLocked(context);
}
}
}
- private static boolean isRestrictedPermission(@NonNull String permission,
+ private static void createForegroundBackgroundPermissionMappingsLocked(
@NonNull Context context) {
- synchronized (sRestrictedPermissions) {
- if (sRestrictedPermissions.containsKey(permission)) {
- return sRestrictedPermissions.get(permission);
- }
- }
-
- PackageManager packageManager = context.getPackageManager();
- PermissionInfo permissionInfo = null;
- try {
- permissionInfo = packageManager.getPermissionInfo(permission, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(LOG_TAG, "Cannot get PermissionInfo for permission: " + permission);
- }
-
- // Don't expect that to be a transient error, so we can still cache the failed information.
- boolean isRestrictedPermission = permissionInfo != null
- && (permissionInfo.flags & (PermissionInfo.FLAG_SOFT_RESTRICTED
- | PermissionInfo.FLAG_HARD_RESTRICTED)) != 0;
-
- synchronized (sRestrictedPermissions) {
- sRestrictedPermissions.put(permission, isRestrictedPermission);
- }
-
- return isRestrictedPermission;
- }
-
- private static void createForegroundBackgroundPermissionMappings(@NonNull Context context) {
List<String> permissions = new ArrayList<>();
sBackgroundToForegroundPermissions = new ArrayMap<>();
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 04fd615e1..1d49b3c1a 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,17 +37,23 @@ 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;
+import androidx.annotation.VisibleForTesting;
import com.android.modules.utils.build.SdkLevel;
import com.android.role.controller.util.CollectionUtils;
import com.android.role.controller.util.PackageUtils;
+import com.android.role.controller.util.RoleFlags;
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;
@@ -82,6 +88,36 @@ 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,
+ RoleFlags.isProfileGroupExclusivityAvailable());
+ }
+
/**
* The name of this role. Must be unique.
*/
@@ -109,9 +145,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.
@@ -185,8 +222,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;
@@ -240,14 +277,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) {
@@ -256,7 +293,7 @@ public class Role {
mBehavior = behavior;
mDefaultHoldersResourceName = defaultHoldersResourceName;
mDescriptionResource = descriptionResource;
- mExclusive = exclusive;
+ mExclusivity = exclusivity;
mFallBackToDefaultHolder = fallBackToDefaultHolder;
mFeatureFlag = featureFlag;
mLabelResource = labelResource;
@@ -297,7 +334,29 @@ 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);
+ }
+ if (!mPreferredActivities.isEmpty() && exclusivity == EXCLUSIVITY_PROFILE_GROUP) {
+ throw new IllegalArgumentException(
+ "Role cannot have preferred activities when exclusivity is profileGroup");
+ }
+ return exclusivity;
+ }
+ }
+ return mExclusivity;
}
@Nullable
@@ -413,6 +472,19 @@ public class Role {
if (!isAvailableByFeatureFlagAndSdkVersion()) {
return false;
}
+ if (getExclusivity() == EXCLUSIVITY_PROFILE_GROUP) {
+ if (UserUtils.isPrivateProfile(user, context)) {
+ return false;
+ }
+
+ // A profile group exclusive role may only be available in a profile when it's
+ // available in the profile parent.
+ UserHandle profileParent = UserUtils.getProfileParentOrSelf(user, context);
+ if (!Objects.equals(user, profileParent)
+ && !isAvailableAsUser(profileParent, context)) {
+ return false;
+ }
+ }
if (mBehavior != null) {
return mBehavior.isAvailableAsUser(this, user, context);
}
@@ -424,7 +496,8 @@ public class Role {
*
* @return whether this role is available based on SDK version
*/
- boolean isAvailableByFeatureFlagAndSdkVersion() {
+ @VisibleForTesting
+ public boolean isAvailableByFeatureFlagAndSdkVersion() {
if (mFeatureFlag != null && !mFeatureFlag.get()) {
return false;
}
@@ -449,6 +522,12 @@ public class Role {
@NonNull
public List<String> getDefaultHoldersAsUser(@NonNull UserHandle user,
@NonNull Context context) {
+ // Do not allow default role holder for non-active user if the role is exclusive to profile
+ // group
+ if (isNonActiveUserForProfileGroupExclusiveRole(user, context)) {
+ return Collections.emptyList();
+ }
+
if (mBehavior != null) {
List<String> defaultHolders = mBehavior.getDefaultHoldersAsUser(this, user, context);
if (defaultHolders != null) {
@@ -560,6 +639,10 @@ public class Role {
if (!RoleManagerCompat.isRoleFallbackEnabledAsUser(this, user, context)) {
return null;
}
+ // Do not fall back for non-active user if the role is exclusive to profile group
+ if (isNonActiveUserForProfileGroupExclusiveRole(user, context)) {
+ return null;
+ }
if (mFallBackToDefaultHolder) {
return CollectionUtils.firstOrNull(getDefaultHoldersAsUser(user, context));
}
@@ -569,6 +652,17 @@ public class Role {
return null;
}
+ private boolean isNonActiveUserForProfileGroupExclusiveRole(@NonNull UserHandle user,
+ @NonNull Context context) {
+ if (RoleFlags.isProfileGroupExclusivityAvailable()
+ && getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ RoleManager userRoleManager = userContext.getSystemService(RoleManager.class);
+ return !Objects.equals(userRoleManager.getActiveUserForRole(mName), user);
+ }
+ return false;
+ }
+
/**
* Check whether this role is allowed to bypass qualification, if enabled globally.
*
@@ -988,6 +1082,11 @@ public class Role {
*/
public void onNoneHolderSelectedAsUser(@NonNull UserHandle user, @NonNull Context context) {
RoleManagerCompat.setRoleFallbackEnabledAsUser(this, false, user, context);
+ if (RoleFlags.isProfileGroupExclusivityAvailable()
+ && getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
+ RoleManager roleManager = context.getSystemService(RoleManager.class);
+ roleManager.setActiveUserForRole(mName, user, 0);
+ }
}
/**
@@ -1039,7 +1138,9 @@ public class Role {
*/
@Nullable
public Intent getRestrictionIntentAsUser(@NonNull UserHandle user, @NonNull Context context) {
- if (SdkLevel.isAtLeastU() && mExclusive) {
+ if (SdkLevel.isAtLeastU() && isExclusive()) {
+ // TODO(b/379143953): if role is profile group exclusive
+ // check DISALLOW_CONFIG_DEFAULT_APPS for all users
UserManager userManager = context.getSystemService(UserManager.class);
if (userManager.hasUserRestrictionForUser(UserManager.DISALLOW_CONFIG_DEFAULT_APPS,
user)) {
@@ -1102,7 +1203,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 f1a275daf..4b05554e3 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
@@ -19,12 +19,9 @@ package com.android.role.controller.model;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Build;
-import android.os.Process;
import android.permission.flags.Flags;
import android.util.ArrayMap;
import android.util.Log;
@@ -92,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";
@@ -138,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<>();
@@ -156,16 +158,16 @@ public class RoleParser {
@NonNull
private final Context mContext;
- private final boolean mValidationEnabled;
+ private final boolean mThrowOnError;
public RoleParser(@NonNull Context context) {
this(context, false);
}
@VisibleForTesting
- public RoleParser(@NonNull Context context, boolean validationEnabled) {
+ public RoleParser(@NonNull Context context, boolean throwOnError) {
mContext = context;
- mValidationEnabled = validationEnabled;
+ mThrowOnError = throwOnError;
}
/**
@@ -175,18 +177,21 @@ public class RoleParser {
*/
@NonNull
public ArrayMap<String, Role> parse() {
+ Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> xml = parseRolesXml();
+ if (xml == null) {
+ return new ArrayMap<>();
+ }
+ return xml.second;
+ }
+
+ @Nullable
+ @VisibleForTesting
+ public Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> parseRolesXml() {
try (XmlResourceParser parser = getRolesXml()) {
- Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> xml = parseXml(parser);
- if (xml == null) {
- return new ArrayMap<>();
- }
- ArrayMap<String, PermissionSet> permissionSets = xml.first;
- ArrayMap<String, Role> roles = xml.second;
- validateResult(permissionSets, roles);
- return roles;
+ return parseXml(parser);
} catch (XmlPullParserException | IOException e) {
throwOrLogMessage("Unable to parse roles.xml", e);
- return new ArrayMap<>();
+ return null;
}
}
@@ -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;
@@ -543,6 +580,12 @@ public class RoleParser {
skipCurrentTag(parser);
continue;
}
+ if (exclusivity == Role.EXCLUSIVITY_PROFILE_GROUP) {
+ throwOrLogMessage("<preferred-activities> is not supported for a"
+ + " profile-group-exclusive role: " + name);
+ skipCurrentTag(parser);
+ continue;
+ }
preferredActivities = parsePreferredActivities(parser);
break;
default:
@@ -567,12 +610,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
@@ -624,7 +667,7 @@ public class RoleParser {
int queryFlags = getAttributeIntValue(parser, ATTRIBUTE_QUERY_FLAGS, 0);
IntentFilterData intentFilterData = null;
List<RequiredMetaData> metaData = new ArrayList<>();
- List<String> validationMetaDataNames = mValidationEnabled ? new ArrayList<>() : null;
+ List<String> validationMetaDataNames = mThrowOnError ? new ArrayList<>() : null;
int type;
int depth;
@@ -651,7 +694,7 @@ public class RoleParser {
if (metaDataName == null) {
continue;
}
- if (mValidationEnabled) {
+ if (mThrowOnError) {
validateNoDuplicateElement(metaDataName, validationMetaDataNames,
"meta data");
}
@@ -668,7 +711,7 @@ public class RoleParser {
RequiredMetaData requiredMetaData = new RequiredMetaData(metaDataName,
metaDataValue, metaDataProhibited);
metaData.add(requiredMetaData);
- if (mValidationEnabled) {
+ if (mThrowOnError) {
validationMetaDataNames.add(metaDataName);
}
break;
@@ -1204,7 +1247,7 @@ public class RoleParser {
}
private void throwOrLogMessage(String message) {
- if (mValidationEnabled) {
+ if (mThrowOnError) {
throw new IllegalArgumentException(message);
} else {
Log.wtf(LOG_TAG, message);
@@ -1212,7 +1255,7 @@ public class RoleParser {
}
private void throwOrLogMessage(String message, Throwable cause) {
- if (mValidationEnabled) {
+ if (mThrowOnError) {
throw new IllegalArgumentException(message, cause);
} else {
Log.wtf(LOG_TAG, message, cause);
@@ -1222,168 +1265,4 @@ public class RoleParser {
private void throwOrLogForUnknownTag(@NonNull XmlResourceParser parser) {
throwOrLogMessage("Unknown tag: " + parser.getName());
}
-
- /**
- * Validates the permission names with {@code PackageManager} and ensures that all app ops with
- * a permission in {@code AppOpsManager} have declared that permission in its role and ensures
- * that all preferred activities are listed in the required components.
- */
- private void validateResult(@NonNull ArrayMap<String, PermissionSet> permissionSets,
- @NonNull ArrayMap<String, Role> roles) {
- if (!mValidationEnabled) {
- return;
- }
-
- int permissionSetsSize = permissionSets.size();
- for (int permissionSetsIndex = 0; permissionSetsIndex < permissionSetsSize;
- permissionSetsIndex++) {
- PermissionSet permissionSet = permissionSets.valueAt(permissionSetsIndex);
-
- List<Permission> permissions = permissionSet.getPermissions();
- int permissionsSize = permissions.size();
- for (int permissionsIndex = 0; permissionsIndex < permissionsSize; permissionsIndex++) {
- Permission permission = permissions.get(permissionsIndex);
-
- validatePermission(permission);
- }
- }
-
- int rolesSize = roles.size();
- for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
- Role role = roles.valueAt(rolesIndex);
-
- if (!role.isAvailableByFeatureFlagAndSdkVersion()) {
- continue;
- }
-
- List<RequiredComponent> requiredComponents = role.getRequiredComponents();
- int requiredComponentsSize = requiredComponents.size();
- for (int requiredComponentsIndex = 0; requiredComponentsIndex < requiredComponentsSize;
- requiredComponentsIndex++) {
- RequiredComponent requiredComponent = requiredComponents.get(
- requiredComponentsIndex);
-
- String permission = requiredComponent.getPermission();
- if (permission != null) {
- validatePermission(permission);
- }
- }
-
- List<Permission> permissions = role.getPermissions();
- int permissionsSize = permissions.size();
- for (int i = 0; i < permissionsSize; i++) {
- Permission permission = permissions.get(i);
-
- validatePermission(permission);
- }
-
- List<AppOp> appOps = role.getAppOps();
- int appOpsSize = appOps.size();
- for (int i = 0; i < appOpsSize; i++) {
- AppOp appOp = appOps.get(i);
-
- validateAppOp(appOp);
- }
-
- List<Permission> appOpPermissions = role.getAppOpPermissions();
- int appOpPermissionsSize = appOpPermissions.size();
- for (int i = 0; i < appOpPermissionsSize; i++) {
- validateAppOpPermission(appOpPermissions.get(i));
- }
-
- List<PreferredActivity> preferredActivities = role.getPreferredActivities();
- int preferredActivitiesSize = preferredActivities.size();
- for (int preferredActivitiesIndex = 0;
- preferredActivitiesIndex < preferredActivitiesSize;
- preferredActivitiesIndex++) {
- PreferredActivity preferredActivity = preferredActivities.get(
- preferredActivitiesIndex);
-
- if (!role.getRequiredComponents().contains(preferredActivity.getActivity())) {
- throw new IllegalArgumentException("<activity> of <preferred-activity> not"
- + " required in <required-components>, role: " + role.getName()
- + ", preferred activity: " + preferredActivity);
- }
- }
- }
- }
-
- private void validatePermission(@NonNull Permission permission) {
- if (!permission.isAvailableAsUser(Process.myUserHandle(), mContext)) {
- return;
- }
- validatePermission(permission.getName(), true);
- }
-
- private void validatePermission(@NonNull String permission) {
- validatePermission(permission, false);
- }
-
- private void validatePermission(@NonNull String permission, boolean enforceIsRuntimeOrRole) {
- PackageManager packageManager = mContext.getPackageManager();
- boolean isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
- // Skip validation for car permissions which may not be available on all build targets.
- if (!isAutomotive && permission.startsWith("android.car")) {
- return;
- }
-
- PermissionInfo permissionInfo;
- try {
- permissionInfo = packageManager.getPermissionInfo(permission, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException("Unknown permission: " + permission, e);
- }
-
- if (enforceIsRuntimeOrRole) {
- if (!(permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS
- || (permissionInfo.getProtectionFlags() & PermissionInfo.PROTECTION_FLAG_ROLE)
- == PermissionInfo.PROTECTION_FLAG_ROLE)) {
- throw new IllegalArgumentException(
- "Permission is not a runtime or role permission: " + permission);
- }
- }
- }
-
- private void validateAppOpPermission(@NonNull Permission appOpPermission) {
- if (!appOpPermission.isAvailableAsUser(Process.myUserHandle(), mContext)) {
- return;
- }
- validateAppOpPermission(appOpPermission.getName());
- }
-
- private void validateAppOpPermission(@NonNull String appOpPermission) {
- PackageManager packageManager = mContext.getPackageManager();
- PermissionInfo permissionInfo;
- try {
- permissionInfo = packageManager.getPermissionInfo(appOpPermission, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException("Unknown app op permission: " + appOpPermission, e);
- }
- if ((permissionInfo.getProtectionFlags() & PermissionInfo.PROTECTION_FLAG_APPOP)
- != PermissionInfo.PROTECTION_FLAG_APPOP) {
- throw new IllegalArgumentException("Permission is not an app op permission: "
- + appOpPermission);
- }
- }
-
- private void validateAppOp(@NonNull AppOp appOp) {
- if (!appOp.isAvailableByFeatureFlagAndSdkVersion()) {
- return;
- }
- // This throws IllegalArgumentException if app op is unknown.
- String permission = AppOpsManager.opToPermission(appOp.getName());
- if (permission != null) {
- PackageManager packageManager = mContext.getPackageManager();
- PermissionInfo permissionInfo;
- try {
- permissionInfo = packageManager.getPermissionInfo(permission, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- if (permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS) {
- throw new IllegalArgumentException("App op has an associated runtime permission: "
- + appOp.getName());
- }
- }
- }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java
index bc7562c11..d00fd47af 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java
@@ -16,6 +16,7 @@
package com.android.role.controller.service;
+import android.annotation.UserIdInt;
import android.app.role.RoleControllerService;
import android.app.role.RoleManager;
import android.content.Context;
@@ -34,6 +35,7 @@ import com.android.role.controller.model.Roles;
import com.android.role.controller.util.CollectionUtils;
import com.android.role.controller.util.LegacyRoleFallbackEnabledUtils;
import com.android.role.controller.util.PackageUtils;
+import com.android.role.controller.util.RoleFlags;
import com.android.role.controller.util.UserUtils;
import java.util.ArrayList;
@@ -49,11 +51,21 @@ public class RoleControllerServiceImpl extends RoleControllerService {
private static final boolean DEBUG = false;
+ public static volatile SetActiveUserForRoleMethod sSetActiveUserForRoleMethod;
private UserHandle mUser;
private Context mContext;
private RoleManager mUserRoleManager;
+ /** Method for setting active user from role controller */
+ public interface SetActiveUserForRoleMethod {
+ /**
+ * Sets user as active for the given role.
+ * @see RoleManager#setActiveUserForRole(String, UserHandle, int)
+ */
+ void setActiveUserForRole(@NonNull String roleName, @UserIdInt int userId, int flags);
+ }
+
public RoleControllerServiceImpl() {}
public RoleControllerServiceImpl(@NonNull UserHandle user, @NonNull Context context) {
@@ -121,6 +133,19 @@ public class RoleControllerServiceImpl extends RoleControllerService {
String roleName = role.getName();
+ if (RoleFlags.isProfileGroupExclusivityAvailable()
+ && role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
+ if (mUserRoleManager.getActiveUserForRole(roleName) == null) {
+ UserHandle profileParent = UserUtils.getProfileParentOrSelf(mUser, mContext);
+ if (Objects.equals(mUser, profileParent)) {
+ Log.i(LOG_TAG, "No active user for role: " + roleName + ", setting "
+ + "active user to user: " + mUser.getIdentifier());
+ sSetActiveUserForRoleMethod.setActiveUserForRole(roleName,
+ mUser.getIdentifier(), 0);
+ }
+ }
+ }
+
// For each of the current holders, check if it is still qualified, redo grant if so, or
// remove it otherwise.
List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
@@ -232,6 +257,11 @@ public class RoleControllerServiceImpl extends RoleControllerService {
boolean added = false;
if (role.isExclusive()) {
+ if (role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
+ sSetActiveUserForRoleMethod.setActiveUserForRole(roleName, mUser.getIdentifier(),
+ flags);
+ }
+
List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
int currentPackageNamesSize = currentPackageNames.size();
for (int i = 0; i < currentPackageNamesSize; i++) {
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java b/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java
new file mode 100644
index 000000000..2c5a247b6
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java
@@ -0,0 +1,45 @@
+/*
+ * 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.role.controller.util;
+
+import android.os.Build;
+
+import androidx.annotation.ChecksSdkIntAtLeast;
+
+import java.util.Objects;
+
+/** Util class for getting shared feature flag check logic. */
+public final class RoleFlags {
+ private RoleFlags() { /* cannot be instantiated */ }
+
+ /**
+ * Returns whether profile group exclusive roles are available. Profile exclusive roles are
+ * available on B+
+ */
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.BAKLAVA)
+ public static boolean isProfileGroupExclusivityAvailable() {
+ // TODO(b/372743073): change to isAtLeastB once available
+ return isAtLeastB() && com.android.permission.flags.Flags.crossUserRoleEnabled();
+ }
+
+ // TODO(b/372743073): remove once SdkLevel.isAtLeastB available
+ @ChecksSdkIntAtLeast(api = 36 /* BUILD_VERSION_CODES.Baklava */)
+ public static boolean isAtLeastB() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA
+ || Objects.equals(Build.VERSION.CODENAME, "Baklava");
+ }
+}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java
index 1b6926ef8..f3cb7926a 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java
@@ -24,6 +24,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
@@ -111,4 +112,24 @@ public final class UserUtils {
return context.createContextAsUser(user, 0);
}
}
+
+ /**
+ * Returns the parent of a given user, or user if it has no parent (e.g. it is the primary
+ * user)
+ */
+ @NonNull
+ public static UserHandle getProfileParentOrSelf(@NonNull UserHandle user,
+ @NonNull Context context) {
+ UserHandle profileParent = getProfileParent(user, context);
+ // If profile parent user is null, then original user is the parent
+ return profileParent != null ? profileParent : user;
+ }
+
+ /** Returns the parent of a given user. */
+ @Nullable
+ private static UserHandle getProfileParent(UserHandle user, @NonNull Context context) {
+ Context userContext = getUserContext(context, user);
+ UserManager userManager = userContext.getSystemService(UserManager.class);
+ return userManager.getProfileParent(user);
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt
index bc6f774ad..e6cf094e3 100644
--- a/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt
+++ b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt
@@ -18,6 +18,7 @@ package com.android.permissioncontroller.ecm
import android.annotation.SuppressLint
import android.app.AlertDialog
+import android.app.AppOpsManager
import android.app.Dialog
import android.app.ecm.EnhancedConfirmationManager
import android.content.Context
@@ -65,6 +66,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
finish()
return
}
+
if (savedInstanceState != null) {
wasClearRestrictionAllowed =
savedInstanceState.getBoolean(KEY_WAS_CLEAR_RESTRICTION_ALLOWED)
@@ -79,11 +81,19 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
require(uid != Process.INVALID_UID) { "EXTRA_UID cannot be null or invalid" }
require(!packageName.isNullOrEmpty()) { "EXTRA_PACKAGE_NAME cannot be null or empty" }
require(!settingIdentifier.isNullOrEmpty()) { "EXTRA_SUBJECT cannot be null or empty" }
-
wasClearRestrictionAllowed =
setClearRestrictionAllowed(packageName, UserHandle.getUserHandleForUid(uid))
val setting = Setting.fromIdentifier(this, settingIdentifier, isEcmInApp)
+ if (
+ SettingType.fromIdentifier(this, settingIdentifier, isEcmInApp) ==
+ SettingType.BLOCKED_DUE_TO_PHONE_STATE &&
+ !Flags.unknownCallPackageInstallBlockingEnabled()
+ ) {
+ finish()
+ return
+ }
+
if (DeviceUtils.isWear(this)) {
WearEnhancedConfirmationDialogFragment.newInstance(setting.title, setting.message)
.show(supportFragmentManager, WearEnhancedConfirmationDialogFragment.TAG)
@@ -116,7 +126,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
fun fromIdentifier(
context: Context,
settingIdentifier: String,
- isEcmInApp: Boolean
+ isEcmInApp: Boolean,
): Setting {
val settingType = SettingType.fromIdentifier(context, settingIdentifier, isEcmInApp)
val label =
@@ -124,7 +134,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
SettingType.PLATFORM_PERMISSION ->
KotlinUtils.getPermGroupLabel(
context,
- PermissionMapping.getGroupOfPlatformPermission(settingIdentifier)!!
+ PermissionMapping.getGroupOfPlatformPermission(settingIdentifier)!!,
)
SettingType.PLATFORM_PERMISSION_GROUP ->
KotlinUtils.getPermGroupLabel(context, settingIdentifier)
@@ -132,15 +142,22 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
context.getString(
Roles.get(context)[settingIdentifier]!!.shortLabelResource
)
+ SettingType.BLOCKED_DUE_TO_PHONE_STATE,
SettingType.OTHER -> null
}
- val url =
- context.getString(R.string.help_url_action_disabled_by_restricted_settings)
- return Setting(
- title = settingType.titleRes?.let { context.getString(it, label) },
+ var title: String?
+ var message: CharSequence?
+ if (settingType == SettingType.BLOCKED_DUE_TO_PHONE_STATE) {
+ title = settingType.titleRes?.let { context.getString(it) }
+ message = settingType.messageRes?.let { context.getString(it) }
+ } else {
+ val url =
+ context.getString(R.string.help_url_action_disabled_by_restricted_settings)
+ title = (settingType.titleRes?.let { context.getString(it, label) })
message =
settingType.messageRes?.let { Html.fromHtml(context.getString(it, url), 0) }
- )
+ }
+ return Setting(title, message)
}
}
}
@@ -148,29 +165,35 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
private enum class SettingType(val titleRes: Int?, val messageRes: Int?) {
PLATFORM_PERMISSION(
R.string.enhanced_confirmation_dialog_title_permission,
- R.string.enhanced_confirmation_dialog_desc_permission
+ R.string.enhanced_confirmation_dialog_desc_permission,
),
PLATFORM_PERMISSION_GROUP(
R.string.enhanced_confirmation_dialog_title_permission,
- R.string.enhanced_confirmation_dialog_desc_permission
+ R.string.enhanced_confirmation_dialog_desc_permission,
),
ROLE(
R.string.enhanced_confirmation_dialog_title_role,
- R.string.enhanced_confirmation_dialog_desc_role
+ R.string.enhanced_confirmation_dialog_desc_role,
),
OTHER(
R.string.enhanced_confirmation_dialog_title_settings_default,
- R.string.enhanced_confirmation_dialog_desc_settings_default
+ R.string.enhanced_confirmation_dialog_desc_settings_default,
+ ),
+ BLOCKED_DUE_TO_PHONE_STATE(
+ R.string.enhanced_confirmation_phone_state_dialog_title,
+ R.string.enhanced_confirmation_phone_state_dialog_desc,
);
companion object {
fun fromIdentifier(
context: Context,
settingIdentifier: String,
- isEcmInApp: Boolean
+ isEcmInApp: Boolean,
): SettingType {
- if (!isEcmInApp) return SettingType.OTHER
return when {
+ settingIdentifier == AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES ->
+ BLOCKED_DUE_TO_PHONE_STATE
+ !isEcmInApp -> OTHER
PermissionMapping.isRuntimePlatformPermission(settingIdentifier) &&
PermissionMapping.getGroupOfPlatformPermission(settingIdentifier) != null ->
PLATFORM_PERMISSION
@@ -178,7 +201,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
PLATFORM_PERMISSION_GROUP
settingIdentifier.startsWith("android.app.role.") &&
Roles.get(context).containsKey(settingIdentifier) -> ROLE
- else -> SettingType.OTHER
+ else -> OTHER
}
}
}
@@ -188,7 +211,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
this.dialogResult = dialogResult
setResult(
RESULT_OK,
- Intent().apply { putExtra(Intent.EXTRA_RETURN_RESULT, dialogResult.statsLogValue) }
+ Intent().apply { putExtra(Intent.EXTRA_RETURN_RESULT, dialogResult.statsLogValue) },
)
finish()
}
@@ -200,7 +223,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID),
settingIdentifier = intent.getStringExtra(Intent.EXTRA_SUBJECT)!!,
firstShowForApp = !wasClearRestrictionAllowed,
- dialogResult = dialogResult
+ dialogResult = dialogResult,
)
}
}
@@ -249,7 +272,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() {
private fun createDialogView(
context: Context,
title: String?,
- message: CharSequence?
+ message: CharSequence?,
): View =
LayoutInflater.from(context)
.inflate(R.layout.enhanced_confirmation_dialog, null)
diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
index d23225ed3..8b11036e8 100644
--- a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
+++ b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
@@ -73,6 +73,7 @@ import android.service.dreams.DreamService
import android.service.notification.NotificationListenerService
import android.service.voice.VoiceInteractionService
import android.service.wallpaper.WallpaperService
+import android.telecom.TelecomManager
import android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
import android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS
import android.util.Log
@@ -658,6 +659,36 @@ suspend fun isPackageHibernationExemptBySystem(
return true
}
+ // Note that it's fine to check permissions instead of app ops as all these permissions were
+ // introduced before auto-revoke / hibernation in R.
+ val hasCallRelatedPermissions =
+ context.checkPermission(Manifest.permission.MANAGE_OWN_CALLS, -1 /* pid */, pkg.uid) ==
+ PERMISSION_GRANTED
+ && context.checkPermission(Manifest.permission.RECORD_AUDIO, -1 /* pid */, pkg.uid) ==
+ PERMISSION_GRANTED
+ && context.checkPermission(Manifest.permission.WRITE_CALL_LOG, -1 /* pid */, pkg.uid) ==
+ PERMISSION_GRANTED
+ if (hasCallRelatedPermissions) {
+ val phoneAccounts = context.getSystemService(TelecomManager::class.java)!!
+ .selfManagedPhoneAccounts
+ var hasRegisteredPhoneAccount = false
+ for (phoneAccount in phoneAccounts) {
+ if (pkg.packageName == phoneAccount.componentName.packageName) {
+ hasRegisteredPhoneAccount = true
+ break
+ }
+ }
+ if (hasRegisteredPhoneAccount) {
+ if (DEBUG_HIBERNATION_POLICY) {
+ DumpableLog.i(
+ LOG_TAG,
+ "Exempted ${pkg.packageName} - caller app"
+ )
+ }
+ return true
+ }
+ }
+
if (SdkLevel.isAtLeastS()) {
val hasInstallOrUpdatePermissions =
context.checkPermission(Manifest.permission.INSTALL_PACKAGES, -1 /* pid */, pkg.uid) ==
diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING
index 69a8f74be..038b2f992 100644
--- a/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING
@@ -11,5 +11,15 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "PermissionControllerMockingTests",
+ "options": [
+ {
+ "include-filter": "com.android.permissioncontroller.tests.mocking.hibernation"
+ }
+ ]
+ }
]
}
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/compat/AppPermissionFragmentCompat.java b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java
index 188e3a9d0..de7404ead 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java
@@ -19,6 +19,7 @@ package com.android.permissioncontroller.permission.compat;
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
@@ -29,6 +30,7 @@ import androidx.preference.PreferenceFragmentCompat;
import com.android.modules.utils.build.SdkLevel;
import com.android.permission.flags.Flags;
+import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.ui.handheld.max35.LegacyAppPermissionFragment;
import com.android.permissioncontroller.permission.ui.handheld.v36.AppPermissionFragment;
@@ -41,8 +43,10 @@ public class AppPermissionFragmentCompat {
* Create an instance of this fragment
*/
@NonNull
- public static PreferenceFragmentCompat createFragment() {
- if (SdkLevel.isAtLeastV() && Flags.appPermissionFragmentUsesPreferences()) {
+ public static PreferenceFragmentCompat createFragment(@NonNull Context context) {
+ if (SdkLevel.isAtLeastV() && (Flags.appPermissionFragmentUsesPreferences()
+ || context.getResources().getBoolean(
+ R.bool.config_usePreferenceForAppPermissionSettings))) {
return new AppPermissionFragment();
} else {
return new LegacyAppPermissionFragment();
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/AppPermGroupUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt
index b17098a13..394cb3eb7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt
@@ -233,8 +233,8 @@ private constructor(
* user
*/
private fun isUserSet(permissionState: Map<String, PermState>): Boolean {
- val flagMask =
- PackageManager.FLAG_PERMISSION_USER_SET or PackageManager.FLAG_PERMISSION_USER_FIXED
+ val flagMask = PackageManager.FLAG_PERMISSION_USER_SET or
+ PackageManager.FLAG_PERMISSION_USER_FIXED or PackageManager.FLAG_PERMISSION_ONE_TIME
return permissionState.any { (it.value.permFlags and flagMask) != 0 }
}
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/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt
index 16eaaf6f2..5ba649fd3 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt
@@ -21,6 +21,7 @@ import android.app.AppOpsManager
import android.os.UserHandle
import android.permission.flags.Flags
import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel
import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel.DiscreteOpModel
import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository
@@ -29,6 +30,7 @@ import com.android.permissioncontroller.permission.domain.model.v31.PermissionTi
import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModelWrapper
import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.CLUSTER_SPACING_MINUTES
import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.ONE_MINUTE_MS
+import com.android.permissioncontroller.permission.utils.LocationUtils
import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository
import com.android.permissioncontroller.role.data.repository.v31.RoleRepository
@@ -48,6 +50,9 @@ class GetPermissionGroupUsageDetailsUseCase(
private val appOpRepository: AppOpRepository,
private val roleRepository: RoleRepository,
private val userRepository: UserRepository,
+ // Allow tests to inject as on T- READ_DEVICE_CONFIG permission check is enforced.
+ private val attributionLabelFix: Boolean =
+ com.android.permission.flags.Flags.permissionTimelineAttributionLabelFix(),
) {
operator fun invoke(coroutineScope: CoroutineScope): Flow<PermissionTimelineUsageModelWrapper> {
val opNames = requireNotNull(permissionGroupToOpNames[permissionGroup])
@@ -72,7 +77,7 @@ class GetPermissionGroupUsageDetailsUseCase(
permissionGroup,
packageOps.userId,
permissionRepository,
- packageRepository
+ packageRepository,
)
packageOps
}
@@ -86,43 +91,61 @@ class GetPermissionGroupUsageDetailsUseCase(
}
}
+ // show attribution on T+ for location provider only..
+ private fun shouldShowAttributionLabel(packageName: String): Boolean {
+ return if (attributionLabelFix) {
+ SdkLevel.isAtLeastT() &&
+ LocationUtils.isLocationProvider(PermissionControllerApplication.get(), packageName)
+ } else true
+ }
+
/** Group app op accesses by attribution label if it is available and user visible. */
private suspend fun List<DiscretePackageOpsModel>.groupByAttributionLabelIfNeeded() =
map { packageOps ->
- val attributionInfo =
- packageRepository.getPackageAttributionInfo(
- packageOps.packageName,
- UserHandle.of(packageOps.userId)
- )
- if (attributionInfo != null) {
- if (attributionInfo.areUserVisible && attributionInfo.tagResourceMap != null) {
- val attributionLabelOpsMap: Map<String?, List<DiscreteOpModel>> =
- packageOps.appOpAccesses
- .map { appOpEntry ->
- val resourceId =
- attributionInfo.tagResourceMap[appOpEntry.attributionTag]
- val label = attributionInfo.resourceLabelMap?.get(resourceId)
- label to appOpEntry
- }
- .groupBy { labelAppOpEntryPair -> labelAppOpEntryPair.first }
- .mapValues { (_, values) ->
- values.map { labelAppOpEntryPair -> labelAppOpEntryPair.second }
- }
+ if (!shouldShowAttributionLabel(packageOps.packageName)) {
+ listOf(packageOps)
+ } else {
+ val attributionInfo =
+ packageRepository.getPackageAttributionInfo(
+ packageOps.packageName,
+ UserHandle.of(packageOps.userId),
+ )
+ if (attributionInfo != null) {
+ if (
+ attributionInfo.areUserVisible && attributionInfo.tagResourceMap != null
+ ) {
+ val attributionLabelOpsMap: Map<String?, List<DiscreteOpModel>> =
+ packageOps.appOpAccesses
+ .map { appOpEntry ->
+ val resourceId =
+ attributionInfo.tagResourceMap[
+ appOpEntry.attributionTag]
+ val label =
+ attributionInfo.resourceLabelMap?.get(resourceId)
+ label to appOpEntry
+ }
+ .groupBy { labelAppOpEntryPair -> labelAppOpEntryPair.first }
+ .mapValues { (_, values) ->
+ values.map { labelAppOpEntryPair ->
+ labelAppOpEntryPair.second
+ }
+ }
- attributionLabelOpsMap.map { labelAppOpsEntry ->
- DiscretePackageOpsModel(
- packageOps.packageName,
- packageOps.userId,
- appOpAccesses = labelAppOpsEntry.value,
- attributionLabel = labelAppOpsEntry.key,
- isUserSensitive = packageOps.isUserSensitive,
- )
+ attributionLabelOpsMap.map { labelAppOpsEntry ->
+ DiscretePackageOpsModel(
+ packageOps.packageName,
+ packageOps.userId,
+ appOpAccesses = labelAppOpsEntry.value,
+ attributionLabel = labelAppOpsEntry.key,
+ isUserSensitive = packageOps.isUserSensitive,
+ )
+ }
+ } else {
+ listOf(packageOps)
}
} else {
listOf(packageOps)
}
- } else {
- listOf(packageOps)
}
}
.flatten()
@@ -147,7 +170,7 @@ class GetPermissionGroupUsageDetailsUseCase(
packageOps.userId,
currentCluster.toMutableList(),
packageOps.attributionLabel,
- packageOps.isUserSensitive
+ packageOps.isUserSensitive,
)
)
currentCluster.clear()
@@ -164,7 +187,7 @@ class GetPermissionGroupUsageDetailsUseCase(
packageOps.userId,
currentCluster.toMutableList(),
packageOps.attributionLabel,
- packageOps.isUserSensitive
+ packageOps.isUserSensitive,
)
)
}
@@ -220,7 +243,7 @@ class GetPermissionGroupUsageDetailsUseCase(
private fun canAccessBeAddedToCluster(
currentAccess: DiscreteOpModel,
- clusteredAccesses: List<DiscreteOpModel>
+ clusteredAccesses: List<DiscreteOpModel>,
): Boolean {
val clusterOp = clusteredAccesses.last().opName
if (
@@ -282,7 +305,7 @@ class GetPermissionGroupUsageDetailsUseCase(
listOf(
Manifest.permission_group.CAMERA,
Manifest.permission_group.LOCATION,
- Manifest.permission_group.MICROPHONE
+ Manifest.permission_group.MICROPHONE,
)
permissionGroups.forEach { permissionGroup ->
val opNames =
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java b/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java
index 24aab174c..2fa809c6d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java
@@ -96,6 +96,7 @@ public class BackupHelper {
private static final String ATTR_USER_SET = "set";
private static final String ATTR_USER_FIXED = "fixed";
private static final String ATTR_WAS_REVIEWED = "was-reviewed";
+ private static final String ATTR_ONE_TIME = "one-time";
/** Flags of permissions to <u>not</u> back up */
private static final int SYSTEM_RUNTIME_GRANT_MASK = FLAG_PERMISSION_POLICY_FIXED
@@ -452,19 +453,21 @@ public class BackupHelper {
private final boolean mIsUserSet;
private final boolean mIsUserFixed;
private final boolean mWasReviewed;
+ private final boolean mIsOneTime;
// Not persisted, used during parsing so explicitly defined state takes precedence
private final boolean mIsAddedFromSplit;
private BackupPermissionState(@NonNull String permissionName, boolean isGranted,
boolean isUserSet, boolean isUserFixed, boolean wasReviewed,
- boolean isAddedFromSplit) {
+ boolean isOneTime, boolean isAddedFromSplit) {
mPermissionName = permissionName;
mIsGranted = isGranted;
mIsUserSet = isUserSet;
mIsUserFixed = isUserFixed;
mWasReviewed = wasReviewed;
mIsAddedFromSplit = isAddedFromSplit;
+ mIsOneTime = isOneTime;
}
/**
@@ -512,6 +515,7 @@ public class BackupHelper {
"true".equals(parser.getAttributeValue(null, ATTR_USER_SET)),
"true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED)),
"true".equals(parser.getAttributeValue(null, ATTR_WAS_REVIEWED)),
+ "true".equals(parser.getAttributeValue(null, ATTR_ONE_TIME)),
/* isAddedFromSplit */ i > 0));
}
@@ -519,7 +523,8 @@ public class BackupHelper {
}
/**
- * Is the permission granted, also considering the app-op.
+ * Is the permission granted, also considering the app-op. Don't consider one time grant
+ * as a permission grant for backup/restore.
*
* <p>This does not consider the review-required state of the permission.
*
@@ -528,7 +533,8 @@ public class BackupHelper {
* @return {@code true} iff the permission and app-op is granted
*/
private static boolean isPermGrantedIncludingAppOp(@NonNull Permission perm) {
- return perm.isGranted() && (!perm.affectsAppOp() || perm.isAppOpAllowed());
+ return perm.isGranted() && (!perm.affectsAppOp() || perm.isAppOpAllowed())
+ && !perm.isOneTime();
}
/**
@@ -549,7 +555,7 @@ public class BackupHelper {
return null;
}
- if (!perm.isUserSet() && perm.isGrantedByDefault()) {
+ if (!perm.isUserSet() && !perm.isOneTime() && perm.isGrantedByDefault()) {
return null;
}
@@ -564,10 +570,10 @@ public class BackupHelper {
}
if (isNotInDefaultGrantState || perm.isUserSet() || perm.isUserFixed()
- || permissionWasReviewed) {
+ || perm.isOneTime() || permissionWasReviewed) {
return new BackupPermissionState(perm.getName(), isPermGrantedIncludingAppOp(perm),
perm.isUserSet(), perm.isUserFixed(), permissionWasReviewed,
- /* isAddedFromSplit */ false);
+ perm.isOneTime(), /* isAddedFromSplit */ false);
} else {
return null;
}
@@ -628,6 +634,10 @@ public class BackupHelper {
serializer.attribute(null, ATTR_WAS_REVIEWED, "true");
}
+ if (mIsOneTime) {
+ serializer.attribute(null, ATTR_ONE_TIME, "true");
+ }
+
serializer.endTag(null, TAG_PERMISSION);
}
@@ -671,6 +681,10 @@ public class BackupHelper {
perm.setUserSet(mIsUserSet);
}
+
+ if (!perm.isOneTime()) {
+ perm.setOneTime(mIsOneTime);
+ }
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
index 2734116dd..85145f346 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
@@ -566,10 +566,11 @@ object RuntimePermissionsUpgradeController {
appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION] ?: continue
if (
+ !perm.isGranted &&
!perm.isUserSet &&
- !perm.isSystemFixed &&
- !perm.isPolicyFixed &&
- !perm.isGranted
+ !perm.isOneTime &&
+ !perm.isSystemFixed &&
+ !perm.isPolicyFixed
) {
grants.add(
Grant(false, appPermGroup, listOf(permission.ACCESS_MEDIA_LOCATION))
@@ -610,20 +611,21 @@ object RuntimePermissionsUpgradeController {
// Upon upgrading to platform 33, for all targetSdk>=33 apps, do the following:
// If STORAGE is granted, and the user has not set READ_MEDIA_AURAL or
// READ_MEDIA_VISUAL, grant READ_MEDIA_AURAL and READ_MEDIA_VISUAL
- val storageAppPermGroups =
+ val grantedStorageAppPermGroups =
storageAndMediaAppPermGroups.filter {
it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
it.permGroupInfo.name == permission_group.STORAGE &&
it.isGranted &&
it.isUserSet
}
- for (storageAppPermGroup in storageAppPermGroups) {
+ for (storageAppPermGroup in grantedStorageAppPermGroups) {
val pkgName = storageAppPermGroup.packageInfo.packageName
val auralAppPermGroup =
storageAndMediaAppPermGroups.firstOrNull {
it.packageInfo.packageName == pkgName &&
it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL &&
!it.isUserSet &&
+ !it.isOneTime &&
!it.isUserFixed
}
val visualAppPermGroup =
@@ -632,7 +634,8 @@ object RuntimePermissionsUpgradeController {
it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
!it.permissions
.filter { it.key != permission.ACCESS_MEDIA_LOCATION }
- .any { it.value.isUserSet || it.value.isUserFixed }
+ .any { it.value.isUserSet || it.value.isOneTime ||
+ it.value.isUserFixed }
}
if (auralAppPermGroup != 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/auto/dashboard/AutoPermissionHistoryPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt
index 2d14260a2..36597a3a3 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt
@@ -23,30 +23,30 @@ import androidx.annotation.RequiresApi
import androidx.preference.Preference.OnPreferenceClickListener
import com.android.car.ui.preference.CarUiPreference
import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelLegacy
import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo
/** Preference that displays a permission usage for an app. */
@RequiresApi(Build.VERSION_CODES.S)
class AutoPermissionHistoryPreference(
context: Context,
- historyPreferenceData: PermissionUsageDetailsViewModelLegacy.HistoryPreferenceData
+ historyPreferenceData: AppPermissionAccessUiInfo,
) : CarUiPreference(context) {
init {
- title = historyPreferenceData.preferenceTitle
+ title = historyPreferenceData.packageLabel
summary =
if (historyPreferenceData.summaryText != null) {
context.getString(
R.string.auto_permission_usage_timeline_summary,
DateFormat.getTimeFormat(context).format(historyPreferenceData.accessEndTime),
- historyPreferenceData.summaryText
+ historyPreferenceData.summaryText,
)
} else {
DateFormat.getTimeFormat(context).format(historyPreferenceData.accessEndTime)
}
- if (historyPreferenceData.appIcon != null) {
- icon = historyPreferenceData.appIcon
+ if (historyPreferenceData.badgedPackageIcon != null) {
+ icon = historyPreferenceData.badgedPackageIcon
}
onPreferenceClickListener = OnPreferenceClickListener {
@@ -56,12 +56,12 @@ class AutoPermissionHistoryPreference(
PermissionUsageDetailsViewModel.createHistoryPreferenceClickIntent(
context = context,
userHandle = historyPreferenceData.userHandle,
- packageName = historyPreferenceData.pkgName,
+ packageName = historyPreferenceData.packageName,
permissionGroup = historyPreferenceData.permissionGroup,
accessEndTime = historyPreferenceData.accessEndTime,
accessStartTime = historyPreferenceData.accessStartTime,
showingAttribution = historyPreferenceData.showingAttribution,
- attributionTags = historyPreferenceData.attributionTags.toSet()
+ attributionTags = historyPreferenceData.attributionTags.toSet(),
)
)
true
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt
index 481543eb6..8edd39913 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt
@@ -13,11 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:Suppress("DEPRECATION")
-
package com.android.permissioncontroller.permission.ui.auto.dashboard
-import android.app.role.RoleManager
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -36,16 +33,12 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_
import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED
import com.android.permissioncontroller.R
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment
-import com.android.permissioncontroller.permission.model.legacy.PermissionApps.AppDataLoader
-import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
-import com.android.permissioncontroller.permission.model.v31.PermissionUsages
-import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
import com.android.permissioncontroller.permission.ui.auto.AutoDividerPreference
-import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelFactoryLegacy
-import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelLegacy
+import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUsageDetailsViewModel
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo
import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel
-import com.android.permissioncontroller.permission.utils.Utils
import java.time.Clock
import java.time.Instant
import java.time.ZoneId
@@ -54,9 +47,7 @@ import java.time.temporal.ChronoUnit
import java.util.concurrent.atomic.AtomicReference
@RequiresApi(Build.VERSION_CODES.S)
-class AutoPermissionUsageDetailsFragment :
- AutoSettingsFrameFragment(), PermissionsUsagesChangeCallback {
-
+class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment() {
companion object {
private const val LOG_TAG = "AutoPermissionUsageDetailsFragment"
private const val KEY_SESSION_ID = "_session_id"
@@ -70,14 +61,11 @@ class AutoPermissionUsageDetailsFragment :
.truncatedTo(ChronoUnit.DAYS)
.toEpochSecond() * 1000L
- // Only show the last 24 hours on Auto right now
- private const val SHOW_7_DAYS = false
-
/** Creates a new instance of [AutoPermissionUsageDetailsFragment]. */
fun newInstance(
groupName: String?,
showSystem: Boolean,
- sessionId: Long
+ sessionId: Long,
): AutoPermissionUsageDetailsFragment {
return AutoPermissionUsageDetailsFragment().apply {
arguments =
@@ -92,14 +80,10 @@ class AutoPermissionUsageDetailsFragment :
private val SESSION_ID_KEY = (AutoPermissionUsageFragment::class.java.name + KEY_SESSION_ID)
- private lateinit var permissionUsages: PermissionUsages
- private lateinit var usageViewModel: PermissionUsageDetailsViewModelLegacy
+ private lateinit var usageViewModel: BasePermissionUsageDetailsViewModel
private lateinit var filterGroup: String
- private lateinit var roleManager: RoleManager
- private var appPermissionUsages: List<AppPermissionUsage> = listOf()
private var showSystem = false
- private var finishedInitialLoad = false
private var hasSystemApps = false
/** Unique Id of a request */
@@ -116,7 +100,7 @@ class AutoPermissionUsageDetailsFragment :
!requireArguments().containsKey(Intent.EXTRA_PERMISSION_GROUP_NAME) or
(requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME) == null)
) {
- DumpableLog.e(LOG_TAG, "Missing argument ${Intent.EXTRA_USER}")
+ DumpableLog.e(LOG_TAG, "Missing argument ${Intent.EXTRA_PERMISSION_GROUP_NAME}")
activity?.finish()
return
}
@@ -130,28 +114,21 @@ class AutoPermissionUsageDetailsFragment :
headerLabel =
resources.getString(
R.string.permission_group_usage_title,
- getPermGroupLabel(requireContext(), filterGroup)
+ getPermGroupLabel(requireContext(), filterGroup),
)
-
- val context = preferenceManager.getContext()
- permissionUsages = PermissionUsages(context)
- roleManager = Utils.getSystemServiceSafe(context, RoleManager::class.java)
- val usageViewModelFactory =
- PermissionUsageDetailsViewModelFactoryLegacy(
+ val factory =
+ PermissionUsageDetailsViewModel.PermissionUsageDetailsViewModelFactory(
PermissionControllerApplication.get(),
- roleManager,
+ this,
filterGroup,
- sessionId
)
usageViewModel =
- ViewModelProvider(this, usageViewModelFactory)[
- PermissionUsageDetailsViewModelLegacy::class.java]
-
- reloadData()
+ ViewModelProvider(this, factory)[BasePermissionUsageDetailsViewModel::class.java]
+ usageViewModel.getPermissionUsagesDetailsInfoUiLiveData().observe(this, this::updateUI)
}
override fun onCreatePreferences(bundlle: Bundle?, s: String?) {
- preferenceScreen = preferenceManager.createPreferenceScreen(context!!)
+ preferenceScreen = preferenceManager.createPreferenceScreen(requireContext())
}
private fun setupHeaderPreferences() {
@@ -161,38 +138,16 @@ class AutoPermissionUsageDetailsFragment :
preferenceScreen.addPreference(AutoDividerPreference(context))
}
- /** Reloads the data to show. */
- private fun reloadData() {
- usageViewModel.loadPermissionUsages(
- requireActivity().getLoaderManager(),
- permissionUsages,
- this,
- FILTER_24_HOURS
- )
- if (finishedInitialLoad) {
- setLoading(true)
- }
- }
-
- override fun onPermissionUsagesChanged() {
- if (permissionUsages.usages.isEmpty()) {
- return
- }
- appPermissionUsages = ArrayList(permissionUsages.usages)
- updateUI()
- }
-
private fun updateSystemToggle() {
if (!showSystem) {
PermissionControllerStatsLog.write(
PERMISSION_USAGE_FRAGMENT_INTERACTION,
sessionId,
- PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED
+ PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED,
)
}
showSystem = !showSystem
updateAction()
- updateUI()
}
private fun updateAction() {
@@ -206,47 +161,36 @@ class AutoPermissionUsageDetailsFragment :
} else {
getString(R.string.menu_show_system)
}
- setAction(label) { updateSystemToggle() }
+ setAction(label) {
+ usageViewModel.updateShowSystemAppsToggle(!showSystem)
+ updateSystemToggle()
+ }
}
- private fun updateUI() {
- if (appPermissionUsages.isEmpty()) {
+ private fun updateUI(uiInfo: PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState) {
+ if (
+ activity == null ||
+ uiInfo is PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState.Loading
+ ) {
return
}
preferenceScreen.removeAll()
setupHeaderPreferences()
-
- val uiData =
- usageViewModel.buildPermissionUsageDetailsUiData(
- appPermissionUsages,
- showSystem,
- SHOW_7_DAYS
- )
-
- if (hasSystemApps != uiData.shouldDisplayShowSystemToggle) {
- hasSystemApps = uiData.shouldDisplayShowSystemToggle
+ val uiData = uiInfo as PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState.Success
+ if (hasSystemApps != uiData.containsSystemAppUsage) {
+ hasSystemApps = uiData.containsSystemAppUsage
updateAction()
}
-
val category = AtomicReference(PreferenceCategory(requireContext()))
preferenceScreen.addPreference(category.get())
- AppDataLoader(context) {
- renderHistoryPreferences(
- uiData.getHistoryPreferenceDataList(),
- category,
- preferenceScreen
- )
+ renderHistoryPreferences(uiData.appPermissionAccessUiInfoList, category, preferenceScreen)
- setLoading(false)
- finishedInitialLoad = true
- permissionUsages.stopLoader(requireActivity().getLoaderManager())
- }
- .execute(*uiData.permissionApps.toTypedArray())
+ setLoading(false)
}
fun createPermissionHistoryPreference(
- historyPreferenceData: PermissionUsageDetailsViewModelLegacy.HistoryPreferenceData
+ historyPreferenceData: AppPermissionAccessUiInfo
): Preference {
return AutoPermissionHistoryPreference(requireContext(), historyPreferenceData)
}
@@ -257,7 +201,7 @@ class AutoPermissionUsageDetailsFragment :
summary =
getString(
R.string.permission_group_usage_subtitle_24h,
- getPermGroupLabel(requireContext(), filterGroup)
+ getPermGroupLabel(requireContext(), filterGroup),
)
isSelectable = false
}
@@ -271,7 +215,7 @@ class AutoPermissionUsageDetailsFragment :
summary =
getString(
R.string.manage_permission_summary,
- getPermGroupLabel(requireContext(), filterGroup)
+ getPermGroupLabel(requireContext(), filterGroup),
)
onPreferenceClickListener =
Preference.OnPreferenceClickListener {
@@ -287,9 +231,8 @@ class AutoPermissionUsageDetailsFragment :
}
/** Render the provided [historyPreferenceDataList] into the [preferenceScreen] UI. */
- fun renderHistoryPreferences(
- historyPreferenceDataList:
- List<PermissionUsageDetailsViewModelLegacy.HistoryPreferenceData>,
+ private fun renderHistoryPreferences(
+ historyPreferenceDataList: List<AppPermissionAccessUiInfo>,
category: AtomicReference<PreferenceCategory>,
preferenceScreen: PreferenceScreen,
) {
@@ -299,7 +242,7 @@ class AutoPermissionUsageDetailsFragment :
val currentDateMs =
ZonedDateTime.ofInstant(
Instant.ofEpochMilli(usageTimestamp),
- Clock.system(ZoneId.systemDefault()).zone
+ Clock.system(ZoneId.systemDefault()).zone,
)
.truncatedTo(ChronoUnit.DAYS)
.toEpochSecond() * 1000L
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt
index 9f39bd785..36d867b11 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt
@@ -46,7 +46,7 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() {
Manifest.permission_group.CAMERA,
1,
Manifest.permission_group.MICROPHONE,
- 2
+ 2,
)
private const val DEFAULT_ORDER: Int = 3
}
@@ -54,7 +54,6 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() {
private val SESSION_ID_KEY = (AutoPermissionUsageFragment::class.java.name + KEY_SESSION_ID)
private var showSystem = false
- private var finishedInitialLoad = false
private var hasSystemApps = false
/** Unique Id of a request */
@@ -89,7 +88,7 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() {
PermissionControllerStatsLog.write(
PERMISSION_USAGE_FRAGMENT_INTERACTION,
sessionId,
- PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED
+ PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED,
)
}
showSystem = !showSystem
@@ -133,13 +132,13 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() {
Comparator.comparing { permissionGroupWithUsageCount: Map.Entry<String, Int> ->
PERMISSION_GROUP_ORDER.getOrDefault(
permissionGroupWithUsageCount.key,
- DEFAULT_ORDER
+ DEFAULT_ORDER,
)
}
.thenComparing { permissionGroupWithUsageCount: Map.Entry<String, Int> ->
mViewModel.getPermissionGroupLabel(
requireContext(),
- permissionGroupWithUsageCount.key
+ permissionGroupWithUsageCount.key,
)
}
)
@@ -153,11 +152,10 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() {
permissionGroupWithUsageCountsEntries[i].value,
showSystem,
sessionId,
- false
+ false,
)
getPreferenceScreen().addPreference(permissionUsagePreference)
}
- finishedInitialLoad = true
setLoading(false)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java
index 8650d99fc..080c7cfdc 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java
@@ -29,7 +29,7 @@ public class AppPermissionWrapperFragment extends PermissionsCollapsingToolbarBa
@NonNull
@Override
public PreferenceFragmentCompat createPreferenceFragment() {
- return AppPermissionFragmentCompat.createFragment();
+ return AppPermissionFragmentCompat.createFragment(getContext());
}
@Override
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsFragment.java
index 35236b8de..dd460aa2f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsFragment.java
@@ -24,6 +24,8 @@ import android.view.MenuItem;
import androidx.lifecycle.ViewModelProvider;
+import com.android.permission.flags.Flags;
+import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData;
import com.android.permissioncontroller.permission.ui.model.ManageCustomPermissionsViewModel;
import com.android.permissioncontroller.permission.ui.model.ManageCustomPermissionsViewModelFactory;
@@ -48,6 +50,14 @@ public class ManageCustomPermissionsFragment extends ManagePermissionsFragment {
return arguments;
}
+ private PermGroupsPackagesUiInfoLiveData getPermGroupsLiveData() {
+ if (Flags.declutteredPermissionManagerEnabled()) {
+ return mViewModel.getAdditionaPermGroupsUiInfo();
+ } else {
+ return mViewModel.getUiDataLiveData();
+ }
+ }
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -56,9 +66,9 @@ public class ManageCustomPermissionsFragment extends ManagePermissionsFragment {
new ManageCustomPermissionsViewModelFactory(getActivity().getApplication());
mViewModel = new ViewModelProvider(this, factory)
.get(ManageCustomPermissionsViewModel.class);
- mPermissionGroups = mViewModel.getUiDataLiveData().getValue();
+ mPermissionGroups = getPermGroupsLiveData().getValue();
- mViewModel.getUiDataLiveData().observe(this, permissionGroups -> {
+ getPermGroupsLiveData().observe(this, permissionGroups -> {
if (permissionGroups == null) {
mPermissionGroups = new HashMap<>();
} else {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java
index bf99b7134..51c0906a2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java
@@ -31,7 +31,9 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.modules.utils.build.SdkLevel;
+import com.android.permission.flags.Flags;
import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData;
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment;
import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel;
import com.android.permissioncontroller.permission.utils.StringUtils;
@@ -58,6 +60,14 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr
return arguments;
}
+ private PermGroupsPackagesUiInfoLiveData getPermGroupsLiveData() {
+ if (Flags.declutteredPermissionManagerEnabled()) {
+ return mViewModel.getUsedStandardPermGroupsUiInfo();
+ } else {
+ return mViewModel.getUiDataLiveData();
+ }
+ }
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -65,12 +75,12 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr
final Application application = getActivity().getApplication();
mViewModel = new ViewModelProvider(this, AndroidViewModelFactory.getInstance(application))
.get(ManageStandardPermissionsViewModel.class);
- mPermissionGroups = mViewModel.getUiDataLiveData().getValue();
+ mPermissionGroups = getPermGroupsLiveData().getValue();
- mViewModel.getUiDataLiveData().observe(this, permissionGroups -> {
+ getPermGroupsLiveData().observe(this, permissionGroups -> {
// Once we have loaded data for the first time, further loads should be staggered,
// for performance reasons.
- mViewModel.getUiDataLiveData().setLoadStaggered(true);
+ getPermGroupsLiveData().setLoadStaggered(true);
if (permissionGroups != null) {
mPermissionGroups = permissionGroups;
updatePermissionsUi();
@@ -80,13 +90,18 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr
}
// If we've loaded all LiveDatas, no need to prioritize loading any particular one
- if (!mViewModel.getUiDataLiveData().isStale()) {
- mViewModel.getUiDataLiveData().setFirstLoadGroup(null);
+ if (!getPermGroupsLiveData().isStale()) {
+ getPermGroupsLiveData().setFirstLoadGroup(null);
}
});
mViewModel.getNumCustomPermGroups().observe(this, permNames -> updatePermissionsUi());
mViewModel.getNumAutoRevoked().observe(this, show -> updatePermissionsUi());
+ if (Flags.declutteredPermissionManagerEnabled()) {
+ mViewModel.getNumUnusedStandardPermGroups().observe(
+ this, show -> updatePermissionsUi()
+ );
+ }
}
@Override
@@ -118,6 +133,14 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr
if (mViewModel.getNumCustomPermGroups().getValue() != null) {
numExtraPermissions = mViewModel.getNumCustomPermGroups().getValue();
}
+ if (Flags.declutteredPermissionManagerEnabled()) {
+ if (mViewModel.getNumUnusedStandardPermGroups().getValue() != null) {
+ // When decluttered permission manager is enabled, unused
+ // permission groups will also be displayed in the additional
+ // permissions screen.
+ numExtraPermissions += mViewModel.getNumUnusedStandardPermGroups().getValue();
+ }
+ }
Preference additionalPermissionsPreference = screen.findPreference(EXTRA_PREFS_KEY);
if (numExtraPermissions == 0) {
@@ -198,7 +221,7 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr
public void showPermissionApps(String permissionGroupName) {
// If we return to this page within a reasonable time, prioritize loading data from the
// permission group whose page we are going to, as that is group most likely to have changed
- mViewModel.getUiDataLiveData().setFirstLoadGroup(permissionGroupName);
+ getPermGroupsLiveData().setFirstLoadGroup(permissionGroupName);
mViewModel.showPermissionApps(this, PermissionAppsFragment.createArgs(
permissionGroupName, getArguments().getLong(EXTRA_SESSION_ID)));
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt
index 1cd4ed23a..e7749d827 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt
@@ -17,16 +17,21 @@
package com.android.permissioncontroller.permission.ui.handheld
import android.content.Context
+import android.util.AttributeSet
import android.view.View
import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.R
import com.android.settingslib.widget.FooterPreference
-class PermissionFooterPreference(c: Context) : FooterPreference(c) {
+class PermissionFooterPreference : FooterPreference {
+ constructor(context: Context) : super(context)
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
init {
if (SdkLevel.isAtLeastV()) {
layoutResource = R.layout.permission_footer_preference
- if (c.resources.getBoolean(R.bool.config_permissionFooterPreferenceIconVisible)) {
+ if (context.resources.getBoolean(R.bool.config_permissionFooterPreferenceIconVisible)) {
setIconVisibility(View.VISIBLE)
} else {
setIconVisibility(View.GONE)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt
index 5e30183ec..010ca28a7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt
@@ -18,15 +18,30 @@ package com.android.permissioncontroller.permission.ui.handheld
import android.content.Context
import android.util.AttributeSet
+import androidx.annotation.AttrRes
+import androidx.annotation.StyleRes
import androidx.preference.Preference
import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.DeviceUtils
import com.android.permissioncontroller.R
open class PermissionPreference : Preference {
- constructor(c: Context) : super(c)
+ constructor(context: Context) : super(context)
- constructor(c: Context, a: AttributeSet) : super(c, a)
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ @AttrRes defStyleAttr: Int,
+ ) : super(context, attrs, defStyleAttr)
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ @AttrRes defStyleAttr: Int,
+ @StyleRes defStyleRes: Int,
+ ) : super(context, attrs, defStyleAttr, defStyleRes)
init {
if (SdkLevel.isAtLeastV() && DeviceUtils.isHandheld(context)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt
index ef1752530..ef95c6c5c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt
@@ -18,15 +18,30 @@ package com.android.permissioncontroller.permission.ui.handheld
import android.content.Context
import android.util.AttributeSet
+import androidx.annotation.AttrRes
+import androidx.annotation.StyleRes
import androidx.preference.PreferenceCategory
import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.DeviceUtils
import com.android.permissioncontroller.R
open class PermissionPreferenceCategory : PreferenceCategory {
- constructor(c: Context) : super(c)
+ constructor(context: Context) : super(context)
- constructor(c: Context, a: AttributeSet) : super(c, a)
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ @AttrRes defStyleAttr: Int,
+ ) : super(context, attrs, defStyleAttr)
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ @AttrRes defStyleAttr: Int,
+ @StyleRes defStyleRes: Int,
+ ) : super(context, attrs, defStyleAttr, defStyleRes)
init {
if (SdkLevel.isAtLeastV() && DeviceUtils.isHandheld(context)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java
index 933911bff..3be9434b2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java
@@ -195,6 +195,7 @@ public class PermissionHistoryPreference extends Preference {
Intent intent = getViewPermissionUsageForPeriodIntent();
if (intent != null) {
dividerVerticalBar.setVisibility(View.VISIBLE);
+ widgetView.setVisibility(View.VISIBLE);
widgetView.setImageDrawable(mContext.getDrawable(R.drawable.ic_info_outline));
widgetView.setOnClickListener(v -> {
write(PERMISSION_DETAILS_INTERACTION,
@@ -214,6 +215,7 @@ public class PermissionHistoryPreference extends Preference {
} else {
dividerVerticalBar.setVisibility(View.GONE);
widgetView.setImageDrawable(null);
+ widgetView.setVisibility(View.GONE);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt
index 72e066777..e5dce40b0 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt
@@ -25,6 +25,7 @@ import androidx.preference.PreferenceGroup
import androidx.preference.PreferenceGroupAdapter
import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.handheld.v36.AppPermissionFooterLinkPreference
import com.android.settingslib.widget.FooterPreference
/**
@@ -106,7 +107,10 @@ class SectionPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) :
}
private val Preference.isSectionDivider: Boolean
- get() = this is PreferenceCategory || this is FooterPreference
+ get() =
+ this is PreferenceCategory ||
+ this is FooterPreference ||
+ this is AppPermissionFooterLinkPreference
override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) {
super.onBindViewHolder(holder, position)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFooterLinkPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFooterLinkPreference.kt
new file mode 100644
index 000000000..01554880a
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFooterLinkPreference.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.handheld.v36
+
+import android.content.Context
+import android.os.Build
+import android.util.AttributeSet
+import android.widget.TextView
+import androidx.annotation.AttrRes
+import androidx.annotation.RequiresApi
+import androidx.annotation.StyleRes
+import androidx.preference.PreferenceViewHolder
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.handheld.PermissionPreference
+
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+class AppPermissionFooterLinkPreference : PermissionPreference {
+ constructor(context: Context) : super(context)
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ @AttrRes defStyleAttr: Int,
+ ) : super(context, attrs, defStyleAttr)
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ @AttrRes defStyleAttr: Int,
+ @StyleRes defStyleRes: Int,
+ ) : super(context, attrs, defStyleAttr, defStyleRes)
+
+ init {
+ layoutResource = R.layout.app_permission_footer_link_preference
+ }
+
+ override fun onBindViewHolder(holder: PreferenceViewHolder) {
+ super.onBindViewHolder(holder)
+ if (
+ context.resources.getBoolean(
+ R.bool.config_appPermissionFooterLinkPreferenceSummaryUnderlined
+ )
+ ) {
+ val summary = holder.findViewById(android.R.id.summary) as TextView
+ summary.paint.isUnderlineText = true
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java
index 481dc0dac..4fde26c9d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java
@@ -79,11 +79,10 @@ import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandle
import com.android.permissioncontroller.permission.ui.handheld.AllAppPermissionsFragment;
import com.android.permissioncontroller.permission.ui.handheld.AppPermissionGroupsFragment;
import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment;
+import com.android.permissioncontroller.permission.ui.handheld.PermissionFooterPreference;
import com.android.permissioncontroller.permission.ui.handheld.PermissionPreference;
import com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory;
-import com.android.permissioncontroller.permission.ui.handheld.PermissionSelectorWithWidgetPreference;
import com.android.permissioncontroller.permission.ui.handheld.PermissionSwitchPreference;
-import com.android.permissioncontroller.permission.ui.handheld.PermissionTwoTargetPreference;
import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader;
import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel;
import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState;
@@ -133,10 +132,10 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
private @NonNull SelectorWithWidgetPreference mDenyForegroundButton;
private @NonNull PermissionSwitchPreference mLocationAccuracySwitch;
private @NonNull PermissionTwoTargetPreference mDetails;
- private @NonNull PermissionPreference mFooterLink1;
- private @NonNull PermissionPreference mFooterLink2;
- private @NonNull PermissionPreference mFooterStorageSpecialAppAccess;
- private @NonNull PermissionPreference mAdditionalInfo;
+ private @NonNull AppPermissionFooterLinkPreference mFooterLink1;
+ private @NonNull AppPermissionFooterLinkPreference mFooterLink2;
+ private @NonNull PermissionFooterPreference mFooterStorageSpecialAppAccess;
+ private @NonNull PermissionFooterPreference mAdditionalInfo;
private @NonNull String mPackageName;
private @NonNull String mPermGroupName;
@@ -268,7 +267,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
if (exemptedPackages.contains(mPackageName)) {
int additional_info_label = Utils.isStatusBarIndicatorPermission(mPermGroupName)
? R.string.exempt_mic_camera_info_label : R.string.exempt_info_label;
- mAdditionalInfo.setSummary(context.getString(additional_info_label, mPackageLabel));
+ mAdditionalInfo.setTitle(context.getString(additional_info_label, mPackageLabel));
mAdditionalInfo.setVisible(true);
} else {
mAdditionalInfo.setVisible(false);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionSelectorWithWidgetPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionSelectorWithWidgetPreference.kt
index 9d095c7dc..1574eaba3 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionSelectorWithWidgetPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionSelectorWithWidgetPreference.kt
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.handheld
+package com.android.permissioncontroller.permission.ui.handheld.v36
import android.content.Context
+import android.os.Build
import android.util.AttributeSet
import android.widget.ImageView
import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
+import androidx.annotation.RequiresApi
import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.utils.ResourceUtils
@@ -34,6 +36,7 @@ import com.android.settingslib.widget.SelectorWithWidgetPreference
* - Propagates the supplied `app:checkboxId` id to the checkbox (or radio button, on the left)
* - Allows defining a "disabled click listener" handler that handles clicks when disabled
*/
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
class PermissionSelectorWithWidgetPreference : SelectorWithWidgetPreference {
constructor(context: Context) : super(context) {
init(context, null)
@@ -46,7 +49,7 @@ class PermissionSelectorWithWidgetPreference : SelectorWithWidgetPreference {
constructor(
context: Context,
attrs: AttributeSet?,
- @AttrRes defStyleAttr: Int
+ @AttrRes defStyleAttr: Int,
) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
@@ -56,6 +59,8 @@ class PermissionSelectorWithWidgetPreference : SelectorWithWidgetPreference {
}
private fun init(context: Context, attrs: AttributeSet?) {
+ layoutResource = R.layout.permission_preference_selector_with_widget
+ widgetLayoutResource = R.layout.permission_preference_widget_radiobutton
extraWidgetIconRes =
ResourceUtils.getResourceIdByAttr(context, attrs, R.attr.extraWidgetIcon)
extraWidgetIdRes = ResourceUtils.getResourceIdByAttr(context, attrs, R.attr.extraWidgetId)
@@ -69,9 +74,10 @@ class PermissionSelectorWithWidgetPreference : SelectorWithWidgetPreference {
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
- val extraWidget = holder.findViewById(
- com.android.settingslib.widget.preference.selector.R.id.selector_extra_widget
- ) as? ImageView
+ val extraWidget =
+ holder.findViewById(
+ com.android.settingslib.widget.preference.selector.R.id.selector_extra_widget
+ ) as? ImageView
val checkbox = holder.findViewById(android.R.id.checkbox)
if (extraWidgetIconRes != 0) {
extraWidget?.setImageResource(extraWidgetIconRes)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionTwoTargetPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionTwoTargetPreference.kt
index 13c9ee7c4..cf6585f4d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionTwoTargetPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionTwoTargetPreference.kt
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.handheld
+package com.android.permissioncontroller.permission.ui.handheld.v36
import android.content.Context
+import android.os.Build
import android.util.AttributeSet
import android.widget.ImageView
import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes
+import androidx.annotation.RequiresApi
import androidx.annotation.StyleRes
import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.R
@@ -32,6 +34,7 @@ import com.android.settingslib.widget.TwoTargetPreference
* - Propagates the supplied `app:extraWidgetIcon` drawable to the second target
* - Allows defining a click listener on the second target (the icon on the right)
*/
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
class PermissionTwoTargetPreference : TwoTargetPreference {
constructor(context: Context) : super(context) {
init(context, null)
@@ -44,7 +47,7 @@ class PermissionTwoTargetPreference : TwoTargetPreference {
constructor(
context: Context,
attrs: AttributeSet?,
- @AttrRes defStyleAttr: Int
+ @AttrRes defStyleAttr: Int,
) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
@@ -53,12 +56,13 @@ class PermissionTwoTargetPreference : TwoTargetPreference {
context: Context,
attrs: AttributeSet?,
@AttrRes defStyleAttr: Int,
- @StyleRes defStyleRes: Int
+ @StyleRes defStyleRes: Int,
) : super(context, attrs, defStyleAttr, defStyleRes) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
+ layoutResource = R.layout.permission_preference_two_target
extraWidgetIconRes =
ResourceUtils.getResourceIdByAttr(context, attrs, R.attr.extraWidgetIcon)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt
deleted file mode 100644
index 1369bfdaa..000000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * Copyright (C) 2022 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.
- */
-@file:Suppress("DEPRECATION")
-
-package com.android.permissioncontroller.permission.ui.legacy
-
-import android.Manifest
-import android.app.AppOpsManager
-import android.app.Application
-import android.app.LoaderManager
-import android.app.role.RoleManager
-import android.content.Context
-import android.content.pm.ApplicationInfo
-import android.content.res.Resources
-import android.graphics.drawable.Drawable
-import android.os.Build
-import android.os.UserHandle
-import androidx.annotation.RequiresApi
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import com.android.permissioncontroller.PermissionControllerApplication
-import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.model.AppPermissionGroup
-import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp
-import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
-import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage.TimelineUsage
-import com.android.permissioncontroller.permission.model.v31.PermissionUsages
-import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr
-import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowSubattributionInPermissionsDashboard
-import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageLabel
-import com.android.permissioncontroller.permission.utils.PermissionMapping
-import com.android.permissioncontroller.permission.utils.StringUtils
-import com.android.permissioncontroller.permission.utils.Utils
-import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils
-import java.time.Instant
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.TimeUnit.DAYS
-import kotlin.math.max
-
-/** View model for the permission details fragment. */
-@RequiresApi(Build.VERSION_CODES.S)
-class PermissionUsageDetailsViewModelLegacy(
- val application: Application,
- val roleManager: RoleManager,
- private val permissionGroup: String,
- val sessionId: Long
-) : ViewModel() {
-
- companion object {
- private const val ONE_HOUR_MS = 3_600_000
- private const val ONE_MINUTE_MS = 60_000
- private const val CLUSTER_SPACING_MINUTES: Long = 1L
- private val TIME_7_DAYS_DURATION: Long = DAYS.toMillis(7)
- private val TIME_24_HOURS_DURATION: Long = DAYS.toMillis(1)
- }
-
- private val mTimeFilterItemMs = mutableListOf<TimeFilterItemMs>()
-
- init {
- initializeTimeFilterItems(application)
- }
-
- /** Loads permission usages using [PermissionUsages]. Response is returned to the [callback]. */
- fun loadPermissionUsages(
- loaderManager: LoaderManager,
- permissionUsages: PermissionUsages,
- callback: PermissionUsages.PermissionsUsagesChangeCallback,
- filterTimesIndex: Int
- ) {
- val timeFilterItemMs: TimeFilterItemMs = mTimeFilterItemMs[filterTimesIndex]
- val filterTimeBeginMillis = max(System.currentTimeMillis() - timeFilterItemMs.timeMs, 0)
- permissionUsages.load(
- /* filterPackageName= */ null,
- /* filterPermissionGroups= */ null,
- filterTimeBeginMillis,
- Long.MAX_VALUE,
- PermissionUsages.USAGE_FLAG_LAST or PermissionUsages.USAGE_FLAG_HISTORICAL,
- loaderManager,
- /* getUiInfo= */ false,
- /* getNonPlatformPermissions= */ false,
- /* callback= */ callback,
- /* sync= */ false
- )
- }
-
- /**
- * Create a [PermissionUsageDetailsUiData] based on the provided data.
- *
- * @param appPermissionUsages data about app permission usages
- * @param showSystem whether system apps should be shown
- * @param show7Days whether the last 7 days of history should be shown
- */
- fun buildPermissionUsageDetailsUiData(
- appPermissionUsages: List<AppPermissionUsage>,
- showSystem: Boolean,
- show7Days: Boolean
- ): PermissionUsageDetailsUiData {
- val showPermissionUsagesDuration =
- if (show7Days) {
- TIME_7_DAYS_DURATION
- } else {
- TIME_24_HOURS_DURATION
- }
- val startTime =
- (System.currentTimeMillis() - showPermissionUsagesDuration).coerceAtLeast(
- Instant.EPOCH.toEpochMilli()
- )
- val appPermissionTimelineUsages: List<AppPermissionTimelineUsage> =
- extractAppPermissionTimelineUsagesForGroup(appPermissionUsages, permissionGroup)
- val shouldDisplayShowSystemToggle =
- shouldDisplayShowSystemToggle(appPermissionTimelineUsages)
- val permissionApps: List<PermissionApp> =
- getPermissionAppsWithRecentDiscreteUsage(
- appPermissionTimelineUsages,
- showSystem,
- startTime
- )
- val appPermissionUsageEntries =
- buildDiscreteAccessClusterData(appPermissionTimelineUsages, showSystem, startTime)
-
- return PermissionUsageDetailsUiData(
- permissionApps,
- shouldDisplayShowSystemToggle,
- appPermissionUsageEntries
- )
- }
-
- private fun getHistoryPreferenceData(
- discreteAccessClusterData: DiscreteAccessClusterData,
- ): HistoryPreferenceData {
- val context = application
- val accessTimeList =
- discreteAccessClusterData.discreteAccessDataList.map { p -> p.accessTimeMs }
- val durationSummaryLabel =
- getDurationSummary(discreteAccessClusterData, accessTimeList, context)
- val proxyLabel = getProxyPackageLabel(discreteAccessClusterData)
- val subattributionLabel = getSubattributionLabel(discreteAccessClusterData)
- val showingSubattribution = subattributionLabel != null && subattributionLabel.isNotEmpty()
- val summary =
- buildUsageSummary(durationSummaryLabel, proxyLabel, subattributionLabel, context)
-
- return HistoryPreferenceData(
- UserHandle.getUserHandleForUid(
- discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.uid
- ),
- discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.packageName,
- discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.icon,
- discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.label,
- permissionGroup,
- discreteAccessClusterData.discreteAccessDataList.last().accessTimeMs,
- discreteAccessClusterData.discreteAccessDataList.first().accessTimeMs,
- summary,
- showingSubattribution,
- discreteAccessClusterData.appPermissionTimelineUsage.attributionTags,
- sessionId
- )
- }
-
- /**
- * Returns whether the provided [AppPermissionUsage] instances contains the provided platform
- * permission group.
- */
- fun containsPlatformAppPermissionGroup(
- appPermissionUsages: List<AppPermissionUsage>,
- groupName: String,
- ) = appPermissionUsages.extractAllPlatformAppPermissionGroups().any { it.name == groupName }
-
- /** Extracts a list of [AppPermissionTimelineUsage] for a particular permission group. */
- private fun extractAppPermissionTimelineUsagesForGroup(
- appPermissionUsages: List<AppPermissionUsage>,
- group: String
- ): List<AppPermissionTimelineUsage> {
- val exemptedPackages = Utils.getExemptedPackages(roleManager)
- return appPermissionUsages
- .filter { !exemptedPackages.contains(it.packageName) }
- .map { appPermissionUsage ->
- getAppPermissionTimelineUsages(
- appPermissionUsage.app,
- appPermissionUsage.groupUsages.firstOrNull { it.group.name == group }
- )
- }
- .flatten()
- }
-
- /** Returns whether the show/hide system toggle should be displayed in the UI. */
- private fun shouldDisplayShowSystemToggle(
- appPermissionTimelineUsages: List<AppPermissionTimelineUsage>,
- ): Boolean =
- appPermissionTimelineUsages
- .map { it.timelineUsage }
- .filter { it.hasDiscreteData() }
- .any { it.group.isSystem() }
-
- /**
- * Returns a list of [PermissionApp] instances which had recent discrete permission usage
- * (recent here refers to usages occurring after the provided start time).
- */
- private fun getPermissionAppsWithRecentDiscreteUsage(
- appPermissionTimelineUsageList: List<AppPermissionTimelineUsage>,
- showSystem: Boolean,
- startTime: Long,
- ): List<PermissionApp> =
- appPermissionTimelineUsageList
- .filter { it.timelineUsage.hasDiscreteData() }
- .filter { showSystem || !it.timelineUsage.group.isSystem() }
- .filter { it.timelineUsage.allDiscreteAccessTime.any { it.first >= startTime } }
- .map { it.permissionApp }
-
- /**
- * Builds a list of [DiscreteAccessClusterData] from the provided list of
- * [AppPermissionTimelineUsage].
- */
- private fun buildDiscreteAccessClusterData(
- appPermissionTimelineUsageList: List<AppPermissionTimelineUsage>,
- showSystem: Boolean,
- startTime: Long,
- ): List<DiscreteAccessClusterData> =
- appPermissionTimelineUsageList
- .map { appPermissionTimelineUsages ->
- val accessDataList =
- extractRecentDiscreteAccessData(
- appPermissionTimelineUsages.timelineUsage,
- showSystem,
- startTime
- )
-
- if (accessDataList.size <= 1) {
- return@map accessDataList.map {
- DiscreteAccessClusterData(appPermissionTimelineUsages, listOf(it))
- }
- }
-
- clusterDiscreteAccessData(appPermissionTimelineUsages, accessDataList)
- }
- .flatten()
- .sortedWith(
- compareBy(
- { -it.discreteAccessDataList.first().accessTimeMs },
- { it.appPermissionTimelineUsage.permissionApp.label }
- )
- )
- .toList()
-
- /**
- * Clusters a list of [DiscreteAccessData] into a list of [DiscreteAccessClusterData] instances.
- *
- * [DiscreteAccessData] which have accesses sufficiently close together in time will be places
- * in the same cluster.
- */
- private fun clusterDiscreteAccessData(
- appPermissionTimelineUsage: AppPermissionTimelineUsage,
- discreteAccessDataList: List<DiscreteAccessData>
- ): List<DiscreteAccessClusterData> {
- val clusterDataList = mutableListOf<DiscreteAccessClusterData>()
- val currentDiscreteAccessDataList: MutableList<DiscreteAccessData> = mutableListOf()
- for (discreteAccessData in discreteAccessDataList) {
- if (currentDiscreteAccessDataList.isEmpty()) {
- currentDiscreteAccessDataList.add(discreteAccessData)
- } else if (
- !canAccessBeAddedToCluster(discreteAccessData, currentDiscreteAccessDataList)
- ) {
- clusterDataList.add(
- DiscreteAccessClusterData(
- appPermissionTimelineUsage,
- currentDiscreteAccessDataList.toMutableList()
- )
- )
- currentDiscreteAccessDataList.clear()
- currentDiscreteAccessDataList.add(discreteAccessData)
- } else {
- currentDiscreteAccessDataList.add(discreteAccessData)
- }
- }
- if (currentDiscreteAccessDataList.isNotEmpty()) {
- clusterDataList.add(
- DiscreteAccessClusterData(appPermissionTimelineUsage, currentDiscreteAccessDataList)
- )
- }
- return clusterDataList
- }
-
- /**
- * Extract recent [DiscreteAccessData] from a list of [TimelineUsage] instances, and return them
- * ordered descending by access time (recent here refers to accesses occurring after the
- * provided start time).
- */
- private fun extractRecentDiscreteAccessData(
- timelineUsages: TimelineUsage,
- showSystem: Boolean,
- startTime: Long
- ): List<DiscreteAccessData> {
- return if (
- timelineUsages.hasDiscreteData() && (showSystem || !timelineUsages.group.isSystem())
- ) {
- getRecentDiscreteAccessData(timelineUsages, startTime)
- .sortedWith(compareBy { -it.accessTimeMs })
- .toList()
- } else {
- listOf()
- }
- }
-
- /**
- * Extract recent [DiscreteAccessData] from a [TimelineUsage]. (recent here refers to accesses
- * occurring after the provided start time).
- */
- private fun getRecentDiscreteAccessData(
- timelineUsage: TimelineUsage,
- startTime: Long
- ): List<DiscreteAccessData> {
- return timelineUsage.allDiscreteAccessTime
- .filter { it.first >= startTime }
- .map {
- DiscreteAccessData(
- it.first,
- it.second,
- it.third,
- )
- }
- }
-
- /**
- * Returns whether the provided [DiscreteAccessData] occurred close enough to those in the
- * clustered list that it can be added to the cluster
- */
- private fun canAccessBeAddedToCluster(
- accessData: DiscreteAccessData,
- clusteredAccessDataList: List<DiscreteAccessData>
- ): Boolean =
- accessData.accessTimeMs / ONE_HOUR_MS ==
- clusteredAccessDataList.first().accessTimeMs / ONE_HOUR_MS &&
- clusteredAccessDataList.last().accessTimeMs / ONE_MINUTE_MS -
- accessData.accessTimeMs / ONE_MINUTE_MS > CLUSTER_SPACING_MINUTES
-
- /**
- * Returns whether the provided [AppPermissionGroup] is considered a system group.
- *
- * For the purpose of Permissions Hub UI, non user-sensitive [AppPermissionGroup]s are
- * considered "system" and should be hidden from the main page unless requested by the user
- * through the "show/hide system" toggle.
- */
- private fun AppPermissionGroup.isSystem() = !Utils.isGroupOrBgGroupUserSensitive(this)
-
- /** Returns whether app subattribution should be shown. */
- private fun shouldShowSubattributionForApp(appInfo: ApplicationInfo): Boolean {
- return shouldShowSubattributionInPermissionsDashboard() &&
- SubattributionUtils.isSubattributionSupported(application, appInfo)
- }
-
- /** Returns a summary of the duration the permission was accessed for. */
- private fun getDurationSummary(
- usage: DiscreteAccessClusterData,
- accessTimeList: List<Long>,
- context: Context
- ): String? {
- if (accessTimeList.isEmpty()) {
- return null
- }
-
- var durationMs: Long
-
- // Since Location accesses are atomic, we manually calculate the access duration
- // by comparing the first and last access within the cluster.
- if (permissionGroup == Manifest.permission_group.LOCATION) {
- durationMs = accessTimeList[0] - accessTimeList[accessTimeList.size - 1]
- } else {
- durationMs =
- usage.discreteAccessDataList.map { it.accessDurationMs }.filter { it > 0 }.sum()
- }
- // Only show the duration summary if it is at least (CLUSTER_SPACING_MINUTES + 1) minutes.
- // Displaying a time that is shorter than the cluster granularity
- // (CLUSTER_SPACING_MINUTES) will not convey useful information.
- if (durationMs >= TimeUnit.MINUTES.toMillis(CLUSTER_SPACING_MINUTES + 1)) {
- return getDurationUsedStr(context, durationMs)
- }
-
- return null
- }
-
- /** Returns the proxied package label if the permission access was proxied. */
- private fun getProxyPackageLabel(usage: DiscreteAccessClusterData): String? =
- usage.discreteAccessDataList
- .firstOrNull { it.proxy?.packageName != null }
- ?.let {
- getPackageLabel(
- PermissionControllerApplication.get(),
- it.proxy!!.packageName!!,
- UserHandle.getUserHandleForUid(it.proxy.uid)
- )
- }
-
- /** Returns the attribution label for the permission access, if any. */
- private fun getSubattributionLabel(usage: DiscreteAccessClusterData): String? =
- if (usage.appPermissionTimelineUsage.label == Resources.ID_NULL) null
- else
- usage.appPermissionTimelineUsage.permissionApp.attributionLabels?.let {
- it[usage.appPermissionTimelineUsage.label]
- }
-
- /** Builds a summary of the permission access. */
- private fun buildUsageSummary(
- subattributionLabel: String?,
- proxyPackageLabel: String?,
- durationSummary: String?,
- context: Context
- ): String? {
- val subTextStrings: MutableList<String?> = mutableListOf()
-
- subattributionLabel?.let { subTextStrings.add(subattributionLabel) }
- proxyPackageLabel?.let { subTextStrings.add(it) }
- durationSummary?.let { subTextStrings.add(it) }
- return when (subTextStrings.size) {
- 3 ->
- context.getString(
- R.string.history_preference_subtext_3,
- subTextStrings[0],
- subTextStrings[1],
- subTextStrings[2]
- )
- 2 ->
- context.getString(
- R.string.history_preference_subtext_2,
- subTextStrings[0],
- subTextStrings[1]
- )
- 1 -> subTextStrings[0]
- else -> null
- }
- }
-
- /**
- * Builds a list of [AppPermissionTimelineUsage] from the provided
- * [AppPermissionUsage.GroupUsage].
- */
- private fun getAppPermissionTimelineUsages(
- app: PermissionApp,
- groupUsage: AppPermissionUsage.GroupUsage?
- ): List<AppPermissionTimelineUsage> {
- if (groupUsage == null) {
- return listOf()
- }
-
- if (shouldShowSubattributionForApp(app.appInfo)) {
- return groupUsage.attributionLabelledGroupUsages.map {
- AppPermissionTimelineUsage(permissionGroup, app, it, it.label)
- }
- }
-
- return listOf(
- AppPermissionTimelineUsage(permissionGroup, app, groupUsage, Resources.ID_NULL)
- )
- }
-
- /** Extracts to a set all the permission groups declared by the platform. */
- private fun List<AppPermissionUsage>.extractAllPlatformAppPermissionGroups():
- Set<AppPermissionGroup> =
- this.flatMap { it.groupUsages }
- .map { it.group }
- .filter { PermissionMapping.isPlatformPermissionGroup(it.name) }
- .toSet()
-
- /** Initialize all relevant [TimeFilterItemMs] values. */
- private fun initializeTimeFilterItems(context: Context) {
- mTimeFilterItemMs.add(
- TimeFilterItemMs(Long.MAX_VALUE, context.getString(R.string.permission_usage_any_time))
- )
- mTimeFilterItemMs.add(
- TimeFilterItemMs(
- DAYS.toMillis(7),
- StringUtils.getIcuPluralsString(context, R.string.permission_usage_last_n_days, 7)
- )
- )
- mTimeFilterItemMs.add(
- TimeFilterItemMs(
- DAYS.toMillis(1),
- StringUtils.getIcuPluralsString(context, R.string.permission_usage_last_n_days, 1)
- )
- )
-
- // TODO: theianchen add code for filtering by time here.
- }
-
- /** Data used to create a preference for an app's permission usage. */
- data class HistoryPreferenceData(
- val userHandle: UserHandle,
- val pkgName: String,
- val appIcon: Drawable?,
- val preferenceTitle: String,
- val permissionGroup: String,
- val accessStartTime: Long,
- val accessEndTime: Long,
- val summaryText: CharSequence?,
- val showingAttribution: Boolean,
- val attributionTags: ArrayList<String>,
- val sessionId: Long
- )
-
- /**
- * A class representing a given time, e.g., "in the last hour".
- *
- * @param timeMs the time represented by this object in milliseconds.
- * @param label the label to describe the timeframe
- */
- data class TimeFilterItemMs(val timeMs: Long, val label: String)
-
- /**
- * Class containing all the information needed by the permission usage details fragments to
- * render UI.
- */
- inner class PermissionUsageDetailsUiData(
- /** List of [PermissionApp] instances */
- // Note that these are used only to cache app data for the permission usage details
- // fragment, and have no bearing on the UI on the main permission usage page.
- val permissionApps: List<PermissionApp>,
- /** Whether to show the "show/hide system" toggle. */
- val shouldDisplayShowSystemToggle: Boolean,
- /** [DiscreteAccessClusterData] instances ordered for display in UI */
- private val discreteAccessClusterDataList: List<DiscreteAccessClusterData>,
- ) {
- // Note that the HistoryPreferenceData are not initialized within the
- // PermissionUsageDetailsUiData instance as the need to be constructed only after the
- // calling fragment loads the necessary PermissionApp instances. We will attempt to remove
- // this dependency in b/240978905.
- /** Builds a list of [HistoryPreferenceData] to be displayed in the UI. */
- fun getHistoryPreferenceDataList(): List<HistoryPreferenceData> {
- return discreteAccessClusterDataList.map {
- this@PermissionUsageDetailsViewModelLegacy.getHistoryPreferenceData(it)
- }
- }
- }
-
- /**
- * Data class representing a cluster of accesses, to be represented as a single entry in the UI.
- */
- data class DiscreteAccessClusterData(
- val appPermissionTimelineUsage: AppPermissionTimelineUsage,
- val discreteAccessDataList: List<DiscreteAccessData>
- )
-
- /** Data class representing a discrete permission access. */
- data class DiscreteAccessData(
- val accessTimeMs: Long,
- val accessDurationMs: Long,
- val proxy: AppOpsManager.OpEventProxyInfo?
- )
-
- /** Data class representing an app's permissions usages for a particular permission group. */
- data class AppPermissionTimelineUsage(
- /** Permission group whose usage is being tracked. */
- val permissionGroup: String,
- // we need a PermissionApp because the loader takes the PermissionApp
- // object and loads the icon and label information asynchronously
- /** App whose permissions are being tracked. */
- val permissionApp: PermissionApp,
- /** Timeline usage for the given app and permission. */
- val timelineUsage: TimelineUsage,
- val label: Int
- ) {
- val attributionTags: java.util.ArrayList<String>
- get() = ArrayList(timelineUsage.attributionTags)
- }
-}
-
-/** Factory for an [PermissionUsageDetailsViewModelLegacy] */
-@RequiresApi(Build.VERSION_CODES.S)
-class PermissionUsageDetailsViewModelFactoryLegacy(
- private val application: Application,
- private val roleManager: RoleManager,
- private val filterGroup: String,
- private val sessionId: Long
-) : ViewModelProvider.Factory {
-
- override fun <T : ViewModel> create(modelClass: Class<T>): T {
- @Suppress("UNCHECKED_CAST")
- return PermissionUsageDetailsViewModelLegacy(
- application,
- roleManager,
- filterGroup,
- sessionId
- )
- as T
- }
-}
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/ManageCustomPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt
index bd80a88cd..429799157 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt
@@ -16,17 +16,23 @@
package com.android.permissioncontroller.permission.ui.model
+import android.Manifest
import android.app.Application
+import android.content.Intent
+import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
+import com.android.permission.flags.Flags
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.data.PermGroupsPackagesLiveData
import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
+import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.permission.utils.navigateSafe
/**
@@ -38,6 +44,12 @@ import com.android.permissioncontroller.permission.utils.navigateSafe
class ManageCustomPermissionsViewModel(private val app: Application) : AndroidViewModel(app) {
val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, UsedCustomPermGroupNamesLiveData())
+ val additionaPermGroupsUiInfo =
+ PermGroupsPackagesUiInfoLiveData(
+ app,
+ if (Flags.declutteredPermissionManagerEnabled()) AdditionalPermGroupNamesLiveData(app)
+ else MutableLiveData<List<String>>(),
+ )
/**
* Navigate to a Permission Apps fragment
@@ -46,6 +58,15 @@ class ManageCustomPermissionsViewModel(private val app: Application) : AndroidVi
* @param args The args to pass to the new fragment
*/
fun showPermissionApps(fragment: Fragment, args: Bundle) {
+ val groupName = args.getString(Intent.EXTRA_PERMISSION_GROUP_NAME)
+ if (groupName == Manifest.permission_group.NOTIFICATIONS) {
+ Utils.navigateToNotificationSettings(fragment.context!!)
+ return
+ }
+ if (Utils.isHealthPermissionUiEnabled() && groupName == HEALTH_PERMISSION_GROUP) {
+ Utils.navigateToHealthConnectSettings(fragment.context!!)
+ return
+ }
fragment.findNavController().navigateSafe(R.id.manage_to_perm_apps, args)
}
}
@@ -58,7 +79,8 @@ class ManageCustomPermissionsViewModel(private val app: Application) : AndroidVi
class ManageCustomPermissionsViewModelFactory(private val app: Application) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
- @Suppress("UNCHECKED_CAST") return ManageCustomPermissionsViewModel(app) as T
+ @Suppress("UNCHECKED_CAST")
+ return ManageCustomPermissionsViewModel(app) as T
}
}
@@ -77,3 +99,32 @@ class UsedCustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String
/* No op override */
}
}
+
+/**
+ * A LiveData that is the union of LiveData UsedCustomPermGroupNamesLiveData and
+ * UnusedStandardPermGroupNamesLiveData.
+ *
+ * @param app The current application of the fragment
+ */
+class AdditionalPermGroupNamesLiveData(private val app: Application) :
+ SmartUpdateMediatorLiveData<List<String>>() {
+
+ val usedCustomGroupNames = UsedCustomPermGroupNamesLiveData()
+ val unusedStandardGroupNames = UnusedStandardPermGroupNamesLiveData(app)
+
+ init {
+ addSource(usedCustomGroupNames) { update() }
+ addSource(unusedStandardGroupNames) { update() }
+ }
+
+ private fun combineGroupNames(
+ groupNames1: List<String>?,
+ groupNames2: List<String>?,
+ ): List<String> {
+ return (groupNames1 ?: emptyList()) + (groupNames2 ?: emptyList())
+ }
+
+ override fun onUpdate() {
+ value = combineGroupNames(usedCustomGroupNames.value, unusedStandardGroupNames.value)
+ }
+}
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 aeab0aa89..6dabe8ab7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
@@ -23,8 +23,11 @@ import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.map
import androidx.navigation.fragment.findNavController
+import com.android.permission.flags.Flags
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.data.PermGroupsPackagesLiveData
import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData
@@ -45,7 +48,21 @@ import com.android.permissioncontroller.permission.utils.navigateSafe
class ManageStandardPermissionsViewModel(private val app: Application) : AndroidViewModel(app) {
val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)
+ val usedStandardPermGroupsUiInfo =
+ PermGroupsPackagesUiInfoLiveData(
+ app,
+ if (Flags.declutteredPermissionManagerEnabled()) UsedStandardPermGroupNamesLiveData(app)
+ else MutableLiveData<List<String>>(),
+ )
val numCustomPermGroups = NumCustomPermGroupsWithPackagesLiveData()
+ val numUnusedStandardPermGroups =
+ MediatorLiveData<Int>().apply {
+ if (Flags.declutteredPermissionManagerEnabled()) {
+ addSource(UnusedStandardPermGroupNamesLiveData(app)) { groupNames ->
+ value = groupNames.size
+ }
+ }
+ }
val numAutoRevoked = unusedAutoRevokePackagesLiveData.map { it?.size ?: 0 }
/**
@@ -98,3 +115,47 @@ class NumCustomPermGroupsWithPackagesLiveData() : SmartUpdateMediatorLiveData<In
value = customPermGroupPackages.value?.size ?: 0
}
}
+
+/**
+ * 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.
+ *
+ * @param app The current application of the fragment
+ */
+class UsedStandardPermGroupNamesLiveData(private val app: Application) :
+ SmartUpdateMediatorLiveData<List<String>>() {
+ init {
+ addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) {
+ permGroups ->
+ if (permGroups.values.any { it != null }) {
+ value =
+ permGroups.filterValues { it != null && it.nonSystemTotal > 0 }.keys.toList()
+ }
+ }
+ }
+
+ override fun onUpdate() {
+ /* No op override */
+ }
+}
+
+/**
+ * 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.
+ *
+ * @param app The current application of the fragment
+ */
+class UnusedStandardPermGroupNamesLiveData(private val app: Application) :
+ SmartUpdateMediatorLiveData<List<String>>() {
+ init {
+ addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) {
+ permGroups ->
+ value = permGroups.filterValues { it != null && it.nonSystemTotal == 0 }.keys.toList()
+ }
+ }
+
+ override fun onUpdate() {
+ /* No op override */
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt
index 6af62e01f..510d19706 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt
@@ -27,8 +27,9 @@ import androidx.wear.compose.material.ChipDefaults
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.SwipeToDismissBox
import com.android.permissioncontroller.permission.ui.wear.elements.Chip
-import com.android.permissioncontroller.permission.ui.wear.elements.Scaffold
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionScaffold
import com.android.permissioncontroller.permission.ui.wear.model.LocationProviderInterceptDialogArgs
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion
@Composable
fun LocationProviderDialogScreen(args: LocationProviderInterceptDialogArgs?) {
@@ -41,7 +42,8 @@ fun LocationProviderDialogScreen(args: LocationProviderInterceptDialogArgs?) {
}
}
SwipeToDismissBox(state = state) { isBackground ->
- Scaffold(
+ WearPermissionScaffold(
+ materialUIVersion = WearPermissionMaterialUIVersion.MATERIAL2_5,
showTimeText = false,
image = iconId,
title = stringResource(titleId),
@@ -54,7 +56,7 @@ fun LocationProviderDialogScreen(args: LocationProviderInterceptDialogArgs?) {
onClick = onLocationSettingsClick,
modifier = Modifier.fillMaxWidth(),
textColor = MaterialTheme.colors.surface,
- colors = ChipDefaults.primaryChipColors()
+ colors = ChipDefaults.primaryChipColors(),
)
}
item {
@@ -64,7 +66,7 @@ fun LocationProviderDialogScreen(args: LocationProviderInterceptDialogArgs?) {
modifier = Modifier.fillMaxWidth(),
)
}
- }
+ },
)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt
index 078eefe3b..2933d6fda 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt
@@ -19,6 +19,7 @@ package com.android.permissioncontroller.permission.ui.wear
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PermissionInfo
+import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP
import android.os.Build
import android.os.UserHandle
import android.util.ArraySet
@@ -319,6 +320,10 @@ class WearAppPermissionGroupsHelper(
) {
// Redirect to location controller extra package settings.
LocationUtils.startLocationControllerExtraPackageSettings(context, user)
+ } else if (permGroupName.equals(HEALTH_PERMISSION_GROUP)
+ && android.permission.flags.Flags.replaceBodySensorPermissionEnabled()) {
+ // Redirect to Health&Fitness UI
+ Utils.navigateToAppHealthConnectSettings(fragment.requireContext(), packageName, user)
} else {
val args =
WearAppPermissionFragment.createArgs(
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 950353f52..1498b91b6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
@@ -37,17 +37,20 @@ import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.N
import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON
import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON
import com.android.permissioncontroller.permission.ui.wear.GrantPermissionsWearViewHandler.BUTTON_RES_ID_TO_NUM
-import com.android.permissioncontroller.permission.ui.wear.elements.Chip
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.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
@Composable
fun WearGrantPermissionsScreen(
viewModel: WearGrantPermissionsViewModel,
onButtonClicked: (Int) -> Unit,
- onLocationSwitchChanged: (Boolean) -> Unit
+ onLocationSwitchChanged: (Boolean) -> Unit,
) {
val groupMessage = viewModel.groupMessageLiveData.observeAsState("")
val icon = viewModel.iconLiveData.observeAsState(null)
@@ -55,8 +58,15 @@ fun WearGrantPermissionsScreen(
val locationVisibilities = viewModel.locationVisibilitiesLiveData.observeAsState(emptyList())
val preciseLocationChecked = viewModel.preciseLocationCheckedLiveData.observeAsState(false)
val buttonVisibilities = viewModel.buttonVisibilitiesLiveData.observeAsState(emptyList())
+ val materialUIVersion =
+ if (ResourceHelper.material3Enabled) {
+ MATERIAL3
+ } else {
+ MATERIAL2_5
+ }
ScrollableScreen(
+ materialUIVersion = materialUIVersion,
showTimeText = false,
image = icon.value,
title = groupMessage.value,
@@ -69,13 +79,14 @@ fun WearGrantPermissionsScreen(
locationVisibilities.value.getOrElse(DIALOG_WITH_BOTH_LOCATIONS) { false }
) {
item {
- ToggleChip(
+ WearPermissionToggleControl(
checked = preciseLocationChecked.value,
- onCheckedChanged = { onLocationSwitchChanged(it) },
+ onCheckedChanged = onLocationSwitchChanged,
label = stringResource(R.string.app_permission_location_accuracy),
toggleControl = ToggleChipToggleControl.Switch,
modifier = Modifier.fillMaxWidth(),
- labelMaxLine = Integer.MAX_VALUE
+ labelMaxLines = Integer.MAX_VALUE,
+ materialUIVersion = materialUIVersion,
)
}
}
@@ -87,16 +98,17 @@ fun WearGrantPermissionsScreen(
}
if (buttonVisibilities.value[pos]) {
item {
- Chip(
+ WearPermissionButton(
label =
getPrimaryText(
- pos,
- locationVisibilities.value,
- labelsByButton(BUTTON_RES_ID_TO_NUM.valueAt(i))
+ pos = pos,
+ locationVisibilities = locationVisibilities.value,
+ default = labelsByButton(BUTTON_RES_ID_TO_NUM.valueAt(i)),
),
onClick = { onButtonClicked(BUTTON_RES_ID_TO_NUM.keyAt(i)) },
modifier = Modifier.fillMaxWidth(),
- labelMaxLines = Integer.MAX_VALUE
+ labelMaxLines = Integer.MAX_VALUE,
+ materialUIVersion = materialUIVersion,
)
}
}
@@ -108,7 +120,7 @@ fun setContent(
composeView: ComposeView,
viewModel: WearGrantPermissionsViewModel,
onButtonClicked: (Int) -> Unit,
- onLocationSwitchChanged: (Boolean) -> Unit
+ onLocationSwitchChanged: (Boolean) -> Unit,
) {
composeView.setContent {
WearGrantPermissionsScreen(viewModel, onButtonClicked, onLocationSwitchChanged)
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/AnnotatedText.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt
index bcdf3b661..07bb88e80 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt
@@ -32,47 +32,70 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextDecoration
import androidx.wear.compose.material.MaterialTheme
+import com.android.permissioncontroller.permission.ui.wear.WearUtils.capitalize
const val CLICKABLE_SPAN_TAG = "CLICKABLE_SPAN_TAG"
@Composable
-fun AnnotatedText(text: CharSequence, style: TextStyle, modifier: Modifier = Modifier) {
+fun AnnotatedText(
+ text: CharSequence,
+ style: TextStyle,
+ modifier: Modifier = Modifier,
+ shouldCapitalize: Boolean,
+) {
val onClickCallbacks = mutableMapOf<String, (View) -> Unit>()
val context = LocalContext.current
val listener = LinkInteractionListener {
if (it is LinkAnnotation.Clickable) {
- onClickCallbacks.get(it.tag)?.invoke(View(context))
+ onClickCallbacks[it.tag]?.invoke(View(context))
}
}
val annotatedString =
- spannableStringToAnnotatedString(text, onClickCallbacks, listener = listener)
+ spannableStringToAnnotatedString(
+ text,
+ shouldCapitalize,
+ onClickCallbacks,
+ listener = listener,
+ )
BasicText(text = annotatedString, style = style, modifier = modifier)
}
@Composable
private fun spannableStringToAnnotatedString(
text: CharSequence,
+ shouldCapitalize: Boolean,
onClickCallbacks: MutableMap<String, (View) -> Unit>,
spanColor: Color = MaterialTheme.colors.primary,
- listener: LinkInteractionListener
-) =
- if (text is Spanned) {
- buildAnnotatedString {
- append((text.toString()))
- for (span in text.getSpans(0, text.length, Any::class.java)) {
- val start = text.getSpanStart(span)
- val end = text.getSpanEnd(span)
- when (span) {
- is ClickableSpan ->
- addClickableSpan(span, spanColor, start, end, onClickCallbacks, listener)
- else -> addStyle(SpanStyle(), start, end)
+ listener: LinkInteractionListener,
+): AnnotatedString {
+ val finalString = if (shouldCapitalize) text.toString().capitalize() else text.toString()
+ val annotatedString =
+ if (text is Spanned) {
+ buildAnnotatedString {
+ append(finalString)
+ for (span in text.getSpans(0, text.length, Any::class.java)) {
+ val start = text.getSpanStart(span)
+ val end = text.getSpanEnd(span)
+ when (span) {
+ is ClickableSpan ->
+ addClickableSpan(
+ span,
+ spanColor,
+ start,
+ end,
+ onClickCallbacks,
+ listener,
+ )
+ else -> addStyle(SpanStyle(), start, end)
+ }
}
}
+ } else {
+ AnnotatedString(finalString)
}
- } else {
- AnnotatedString(text.toString())
- }
+ return annotatedString
+}
private fun AnnotatedString.Builder.addClickableSpan(
span: ClickableSpan,
@@ -80,14 +103,10 @@ private fun AnnotatedString.Builder.addClickableSpan(
start: Int,
end: Int,
onClickCallbacks: MutableMap<String, (View) -> Unit>,
- listener: LinkInteractionListener
+ listener: LinkInteractionListener,
) {
val key = "${CLICKABLE_SPAN_TAG}:$start:$end"
onClickCallbacks[key] = span::onClick
addLink(LinkAnnotation.Clickable(key, linkInteractionListener = listener), start, end)
- addStyle(
- SpanStyle(color = spanColor, textDecoration = TextDecoration.Underline),
- start,
- end,
- )
+ addStyle(SpanStyle(color = spanColor, textDecoration = TextDecoration.Underline), start, end)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt
deleted file mode 100644
index 1394c56ea..000000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2023 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
-
-import androidx.annotation.DrawableRes
-import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.unit.Dp
-import androidx.wear.compose.material.Button
-import androidx.wear.compose.material.ButtonColors
-import androidx.wear.compose.material.ButtonDefaults
-import androidx.wear.compose.material.ButtonDefaults.DefaultButtonSize
-import androidx.wear.compose.material.ButtonDefaults.DefaultIconSize
-import androidx.wear.compose.material.ButtonDefaults.LargeButtonSize
-import androidx.wear.compose.material.ButtonDefaults.LargeIconSize
-import androidx.wear.compose.material.ButtonDefaults.SmallButtonSize
-import androidx.wear.compose.material.ButtonDefaults.SmallIconSize
-
-/**
- * This component is an alternative to [Button], providing the following:
- * - a convenient way of providing an icon and choosing its size from a range of sizes recommended
- * by the Wear guidelines;
- */
-@Composable
-public fun Button(
- imageVector: ImageVector,
- contentDescription: String,
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
- colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
- buttonSize: ButtonSize = ButtonSize.Default,
- iconRtlMode: IconRtlMode = IconRtlMode.Default,
- enabled: Boolean = true
-) {
- Button(
- icon = imageVector,
- contentDescription = contentDescription,
- onClick = onClick,
- modifier = modifier,
- colors = colors,
- buttonSize = buttonSize,
- iconRtlMode = iconRtlMode,
- enabled = enabled
- )
-}
-
-/**
- * This component is an alternative to [Button], providing the following:
- * - a convenient way of providing an icon and choosing its size from a range of sizes recommended
- * by the Wear guidelines;
- */
-@Composable
-public fun Button(
- @DrawableRes id: Int,
- contentDescription: String,
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
- colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
- buttonSize: ButtonSize = ButtonSize.Default,
- iconRtlMode: IconRtlMode = IconRtlMode.Default,
- enabled: Boolean = true
-) {
- Button(
- icon = id,
- contentDescription = contentDescription,
- onClick = onClick,
- modifier = modifier,
- colors = colors,
- buttonSize = buttonSize,
- iconRtlMode = iconRtlMode,
- enabled = enabled
- )
-}
-
-@Composable
-internal fun Button(
- icon: Any,
- contentDescription: String,
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
- colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
- buttonSize: ButtonSize = ButtonSize.Default,
- iconRtlMode: IconRtlMode = IconRtlMode.Default,
- enabled: Boolean = true
-) {
- Button(
- onClick = onClick,
- modifier = modifier.size(buttonSize.tapTargetSize),
- enabled = enabled,
- colors = colors
- ) {
- val iconModifier = Modifier.size(buttonSize.iconSize).align(Alignment.Center)
-
- Icon(
- icon = icon,
- contentDescription = contentDescription,
- modifier = iconModifier,
- rtlMode = iconRtlMode
- )
- }
-}
-
-public sealed class ButtonSize(public val iconSize: Dp, public val tapTargetSize: Dp) {
- public object Default :
- ButtonSize(iconSize = DefaultIconSize, tapTargetSize = DefaultButtonSize)
-
- public object Large : ButtonSize(iconSize = LargeIconSize, tapTargetSize = LargeButtonSize)
- public object Small : ButtonSize(iconSize = SmallIconSize, tapTargetSize = SmallButtonSize)
-
- /**
- * Custom sizes should follow the
- * [accessibility principles and guidance for touch targets](https://developer.android.com/training/wearables/accessibility#set-minimum).
- */
- public data class Custom(val customIconSize: Dp, val customTapTargetSize: Dp) :
- ButtonSize(iconSize = customIconSize, tapTargetSize = customTapTargetSize)
-}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt
index 53013def7..6ce7df125 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt
@@ -36,6 +36,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
@@ -63,7 +64,10 @@ import androidx.wear.compose.material.TimeText
import androidx.wear.compose.material.Vignette
import androidx.wear.compose.material.VignettePosition
import androidx.wear.compose.material.scrollAway
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionScaffold
import com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput.rotaryWithScroll
+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.WearPermissionTheme
/**
@@ -74,6 +78,7 @@ import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionT
*/
@Composable
fun ScrollableScreen(
+ materialUIVersion: WearPermissionMaterialUIVersion = MATERIAL2_5,
showTimeText: Boolean = true,
title: String? = null,
subtitle: CharSequence? = null,
@@ -103,7 +108,8 @@ fun ScrollableScreen(
if (getBackStackEntryCount(activity) > 0) {
SwipeToDismissBox(state = state) { isBackground ->
- Scaffold(
+ WearPermissionScaffold(
+ materialUIVersion,
showTimeText,
title,
subtitle,
@@ -111,11 +117,12 @@ fun ScrollableScreen(
isLoading = isLoading || isBackground || dismissed,
content,
titleTestTag,
- subtitleTestTag
+ subtitleTestTag,
)
}
} else {
- Scaffold(
+ WearPermissionScaffold(
+ materialUIVersion,
showTimeText,
title,
subtitle,
@@ -123,13 +130,13 @@ fun ScrollableScreen(
isLoading,
content,
titleTestTag,
- subtitleTestTag
+ subtitleTestTag,
)
}
}
@Composable
-internal fun Scaffold(
+internal fun Wear2Scaffold(
showTimeText: Boolean,
title: String?,
subtitle: CharSequence?,
@@ -165,14 +172,14 @@ internal fun Scaffold(
start = titleHorizontalPadding,
top = 4.dp,
bottom = titleBottomPadding,
- end = titleHorizontalPadding
+ end = titleHorizontalPadding,
)
val subTitlePaddingValues =
PaddingValues(
start = subtitleHorizontalPadding,
top = 4.dp,
bottom = subtitleBottomPadding,
- end = subtitleHorizontalPadding
+ end = subtitleHorizontalPadding,
)
val initialCenterIndex = 0
val centerHeightDp = Dp(LocalConfiguration.current.screenHeightDp / 2.0f)
@@ -191,14 +198,14 @@ internal fun Scaffold(
modifier =
Modifier.rotaryWithScroll(
scrollableState = listState,
- focusRequester = focusRequester
+ focusRequester = focusRequester,
),
timeText = {
if (showTimeText && !isLoading) {
TimeText(
modifier =
Modifier.scrollAway(listState, initialCenterIndex, scrollAwayOffset)
- .padding(top = timeTextTopPadding),
+ .padding(top = timeTextTopPadding)
)
}
},
@@ -208,12 +215,13 @@ internal fun Scaffold(
{ PositionIndicator(scalingLazyListState = listState) }
} else {
null
- }
+ },
) {
Box(modifier = Modifier.fillMaxSize()) {
if (isLoading) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
} else {
+ val iconColor = chipDefaultColors().iconColor(true).value
ScalingLazyColumn(
modifier = Modifier.fillMaxWidth(),
state = listState,
@@ -225,8 +233,8 @@ internal fun Scaffold(
start = scrollContentHorizontalPadding,
end = scrollContentHorizontalPadding,
top = scrollContentTopPadding,
- bottom = scrollContentBottomPadding
- )
+ bottom = scrollContentBottomPadding,
+ ),
) {
staticItem()
image?.let {
@@ -238,7 +246,8 @@ internal fun Scaffold(
painter = painterResource(id = image),
contentDescription = null,
contentScale = ContentScale.Crop,
- modifier = imageModifier
+ modifier = imageModifier,
+ colorFilter = ColorFilter.tint(iconColor),
)
}
is Drawable ->
@@ -247,7 +256,8 @@ internal fun Scaffold(
painter = rememberDrawablePainter(image),
contentDescription = null,
contentScale = ContentScale.Crop,
- modifier = imageModifier
+ modifier = imageModifier,
+ colorFilter = ColorFilter.tint(iconColor),
)
}
else -> {}
@@ -263,7 +273,7 @@ internal fun Scaffold(
Text(
text = title,
textAlign = TextAlign.Center,
- modifier = modifier
+ modifier = modifier,
)
}
}
@@ -282,6 +292,7 @@ internal fun Scaffold(
color = MaterialTheme.colors.onSurfaceVariant
),
modifier = modifier,
+ shouldCapitalize = true,
)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt
index a21a9d015..2e89586c9 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt
@@ -29,11 +29,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.Role
-import androidx.compose.ui.semantics.role
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.wear.compose.material.ChipDefaults
@@ -44,7 +39,6 @@ import androidx.wear.compose.material.ToggleChip
import androidx.wear.compose.material.ToggleChipColors
import androidx.wear.compose.material.ToggleChipDefaults
import androidx.wear.compose.material.contentColorFor
-import com.android.permissioncontroller.R
/**
* This component is an alternative to [ToggleChip], providing the following:
@@ -67,7 +61,7 @@ fun ToggleChip(
secondaryLabelMaxLine: Int? = null,
colors: ToggleChipColors = ToggleChipDefaults.toggleChipColors(),
enabled: Boolean = true,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
val hasSecondaryLabel = secondaryLabel != null
@@ -78,7 +72,7 @@ fun ToggleChip(
textAlign = TextAlign.Start,
overflow = TextOverflow.Ellipsis,
maxLines = labelMaxLine ?: if (hasSecondaryLabel) 1 else 2,
- style = MaterialTheme.typography.button
+ style = MaterialTheme.typography.button,
)
}
@@ -89,7 +83,7 @@ fun ToggleChip(
text = secondaryLabel,
overflow = TextOverflow.Ellipsis,
maxLines = secondaryLabelMaxLine ?: 1,
- style = MaterialTheme.typography.caption2
+ style = MaterialTheme.typography.caption2,
)
}
}
@@ -110,7 +104,7 @@ fun ToggleChip(
IconRtlMode.Mirrored
} else {
IconRtlMode.Default
- }
+ },
)
}
@@ -123,42 +117,28 @@ fun ToggleChip(
tint = iconColor,
contentDescription = null,
modifier = Modifier.size(ChipDefaults.IconSize).clip(CircleShape),
- rtlMode = iconRtlMode
+ rtlMode = iconRtlMode,
)
}
}
}
- val semanticsRole =
- when (toggleControl) {
- ToggleChipToggleControl.Switch -> Role.Switch
- ToggleChipToggleControl.Radio -> Role.RadioButton
- ToggleChipToggleControl.Checkbox -> Role.Checkbox
- }
-
- val stateDescriptionSemantics =
- stringResource(
- if (checked) {
- R.string.on
- } else {
- R.string.off
- }
- )
ToggleChip(
checked = checked,
- onCheckedChange = onCheckedChanged,
+ onCheckedChange = { newChecked ->
+ // Radio buttons cannot be toggled off by tapping on it again.
+ if (toggleControl != ToggleChipToggleControl.Radio || newChecked) {
+ onCheckedChanged.invoke(newChecked)
+ }
+ },
label = labelParam,
toggleControl = toggleControlParam,
- modifier =
- modifier.fillMaxWidth().semantics {
- role = semanticsRole
- stateDescription = stateDescriptionSemantics
- },
+ modifier = modifier.fillMaxWidth().toggleControlSemantics(toggleControl, checked),
appIcon = iconParam,
secondaryLabel = secondaryLabelParam,
colors = colors,
enabled = enabled,
- interactionSource = interactionSource
+ interactionSource = interactionSource,
)
}
@@ -198,7 +178,7 @@ fun toggleChipDisabledColors(): ToggleChipColors {
uncheckedSecondaryContentColor =
uncheckedSecondaryContentColor.copy(alpha = ContentAlpha.disabled),
uncheckedToggleControlColor =
- uncheckedToggleControlColor.copy(alpha = ContentAlpha.disabled)
+ uncheckedToggleControlColor.copy(alpha = ContentAlpha.disabled),
)
}
@@ -236,6 +216,6 @@ fun toggleChipBackgroundColors(): ToggleChipColors {
uncheckedEndBackgroundColor = uncheckedEndBackgroundColor,
uncheckedContentColor = uncheckedContentColor,
uncheckedSecondaryContentColor = uncheckedSecondaryContentColor,
- uncheckedToggleControlColor = uncheckedToggleControlColor
+ uncheckedToggleControlColor = uncheckedToggleControlColor,
)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt
index a4ce4e764..b6f6db4d3 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt
@@ -16,8 +16,43 @@
package com.android.permissioncontroller.permission.ui.wear.elements
-public enum class ToggleChipToggleControl {
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
+import com.android.permissioncontroller.R
+
+enum class ToggleChipToggleControl {
Switch,
Radio,
- Checkbox
+ Checkbox,
+}
+
+@Composable
+fun Modifier.toggleControlSemantics(
+ toggleControl: ToggleChipToggleControl,
+ checked: Boolean,
+): Modifier {
+ val semanticsRole =
+ when (toggleControl) {
+ ToggleChipToggleControl.Switch -> Role.Switch
+ ToggleChipToggleControl.Radio -> Role.RadioButton
+ ToggleChipToggleControl.Checkbox -> Role.Checkbox
+ }
+ val stateDescriptionSemantics =
+ stringResource(
+ if (checked) {
+ R.string.on
+ } else {
+ R.string.off
+ }
+ )
+
+ return semantics {
+ role = semanticsRole
+ stateDescription = stateDescriptionSemantics
+ }
}
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
new file mode 100644
index 000000000..79a8963d8
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright 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
+ *
+ * https://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.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.graphics.Shape
+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
+
+/**
+ * This component is wrapper on material Button component
+ * 1. It takes icon, primary, secondary label resources and construct them applying permission app
+ * defaults
+ */
+@Composable
+fun WearPermissionButton(
+ label: String,
+ modifier: Modifier = Modifier,
+ materialUIVersion: WearPermissionMaterialUIVersion =
+ WearPermissionMaterialUIVersion.MATERIAL2_5,
+ iconBuilder: WearPermissionIconBuilder? = null,
+ labelMaxLines: Int? = null,
+ secondaryLabel: String? = null,
+ secondaryLabelMaxLines: Int? = null,
+ onClick: () -> Unit,
+ enabled: Boolean = true,
+ style: WearPermissionButtonStyle = WearPermissionButtonStyle.Secondary,
+) {
+ if (materialUIVersion == WearPermissionMaterialUIVersion.MATERIAL2_5) {
+ Chip(
+ label = label,
+ labelMaxLines = labelMaxLines,
+ onClick = onClick,
+ modifier = modifier,
+ secondaryLabel = secondaryLabel,
+ secondaryLabelMaxLines = secondaryLabelMaxLines,
+ icon = { iconBuilder?.build() },
+ largeIcon = false,
+ colors = style.material2ChipColors(),
+ enabled = enabled,
+ )
+ } else {
+ WearPermissionButtonInternal(
+ iconBuilder = iconBuilder,
+ label = label,
+ labelMaxLines = labelMaxLines,
+ secondaryLabel = secondaryLabel,
+ secondaryLabelMaxLines = secondaryLabelMaxLines,
+ onClick = onClick,
+ modifier = modifier,
+ enabled = enabled,
+ colors = style.material3ButtonColors(),
+ )
+ }
+}
+
+@Composable
+internal fun WearPermissionButtonInternal(
+ modifier: Modifier = Modifier,
+ label: String? = null,
+ iconBuilder: WearPermissionIconBuilder? = null,
+ labelMaxLines: Int? = null,
+ secondaryLabel: String? = null,
+ secondaryLabelMaxLines: Int? = null,
+ onClick: () -> Unit,
+ enabled: Boolean = true,
+ colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
+ contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
+ requiresMinimumHeight: Boolean = true,
+ shape: Shape = ButtonDefaults.shape,
+) {
+ val minHeight: Dp =
+ if (requiresMinimumHeight) {
+ 0.dp
+ } else {
+ 1.dp
+ }
+ val iconParam: (@Composable BoxScope.() -> Unit)? = iconBuilder?.let { { it.build() } }
+ 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 {
+ {
+ Text(
+ text = secondaryLabel,
+ modifier = Modifier.fillMaxWidth(),
+ maxLines = secondaryLabelMaxLines ?: LocalTextConfiguration.current.maxLines,
+ )
+ }
+ }
+
+ Button(
+ icon = iconParam,
+ label = labelParam ?: {},
+ secondaryLabel = secondaryLabelParam,
+ enabled = enabled,
+ onClick = onClick,
+ modifier = modifier.requiredSizeIn(minHeight = minHeight).fillMaxWidth(),
+ contentPadding = contentPadding,
+ colors = colors,
+ shape = shape,
+ )
+}
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
new file mode 100644
index 000000000..504c69bb0
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.runtime.Composable
+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
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.Secondary
+import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.Transparent
+
+/**
+ * This component is wrapper on material control colors, It applies the right colors based material
+ * ui version.
+ */
+enum class WearPermissionButtonStyle {
+ Primary,
+ Secondary,
+ Transparent,
+ DisabledLike,
+}
+
+@Composable
+internal fun WearPermissionButtonStyle.material2ChipColors(): ChipColors {
+ return when (this) {
+ Primary -> chipDefaultColors()
+ Secondary -> ChipDefaults.secondaryChipColors()
+ Transparent -> ChipDefaults.childChipColors()
+ DisabledLike -> chipDisabledColors()
+ }
+}
+
+@Composable
+internal fun WearPermissionButtonStyle.material3ButtonColors(): ButtonColors {
+ return when (this) {
+ Primary -> ButtonDefaults.buttonColors()
+ Secondary -> ButtonDefaults.filledTonalButtonColors()
+ Transparent -> ButtonDefaults.childButtonColors()
+ DisabledLike -> ButtonDefaults.disabledLikeColors()
+ }
+}
+
+@Composable
+private fun ButtonDefaults.disabledLikeColors() =
+ filledTonalButtonColors().run {
+ ButtonColors(
+ containerPainter = disabledContainerPainter,
+ contentColor = disabledContentColor,
+ secondaryContentColor = disabledSecondaryContentColor,
+ iconColor = disabledIconColor,
+ disabledContainerPainter = disabledContainerPainter,
+ disabledContentColor = disabledContentColor,
+ disabledSecondaryContentColor = disabledSecondaryContentColor,
+ disabledIconColor = disabledIconColor,
+ )
+ }
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
new file mode 100644
index 000000000..b7521d073
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionIconBuilder.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 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
+ *
+ * https://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 android.graphics.drawable.Drawable
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+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.Icon
+import androidx.wear.compose.material3.IconButtonDefaults
+import com.android.permissioncontroller.permission.ui.wear.elements.rememberDrawablePainter
+
+/**
+ * This class simplifies the construction of icons with various attributes like resource type,
+ * content description, modifier, and tint. It supports different icon resource types, including:
+ * - ImageVector
+ * - Resource ID (Int)
+ * - Drawable
+ * - ImageBitmap
+ *
+ * Usage:
+ * ```
+ * val icon = WearPermissionIconBuilder.builder(IconResourceId)
+ * .contentDescription("Location Permission")
+ * .modifier(Modifier.size(24.dp))
+ * .tint(Color.Red)
+ * .build()
+ * ```
+ *
+ * Note: This builder uses a private constructor and is initialized through the `builder()`
+ * companion object method.
+ */
+class WearPermissionIconBuilder private constructor() {
+ var iconResource: Any? = null
+ private set
+
+ var contentDescription: String? = null
+ private set
+
+ var modifier: Modifier = Modifier.size(IconButtonDefaults.LargeIconSize)
+ private set
+
+ var tint: Color = Color.Unspecified
+ private set
+
+ fun contentDescription(description: String?): WearPermissionIconBuilder {
+ contentDescription = description
+ return this
+ }
+
+ fun modifier(modifier: Modifier): WearPermissionIconBuilder {
+ this.modifier then modifier
+ return this
+ }
+
+ fun tint(tint: Color): WearPermissionIconBuilder {
+ this.tint = tint
+ return this
+ }
+
+ @Composable
+ fun build() {
+ when (iconResource) {
+ is ImageVector -> Icon(iconResource as ImageVector, contentDescription, modifier, tint)
+ is Int ->
+ Icon(painterResource(id = iconResource as Int), contentDescription, modifier, tint)
+
+ is Drawable ->
+ Icon(
+ rememberDrawablePainter(iconResource as Drawable),
+ contentDescription,
+ modifier,
+ tint,
+ )
+
+ is ImageBitmap -> Icon(iconResource as ImageBitmap, contentDescription, modifier, tint)
+ else -> throw IllegalArgumentException("Type not supported.")
+ }
+ }
+
+ companion object {
+ fun builder(icon: Any) = WearPermissionIconBuilder().apply { iconResource = icon }
+ }
+}
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..cd18b5b09
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.graphics.RectangleShape
+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,
+ shape = RectangleShape,
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt
new file mode 100644
index 000000000..9a926f5a3
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt
@@ -0,0 +1,301 @@
+/*
+ * Copyright 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
+ *
+ * https://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 android.graphics.drawable.Drawable
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredHeightIn
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ScrollInfoProvider
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import androidx.wear.compose.material3.AppScaffold
+import androidx.wear.compose.material3.CircularProgressIndicator
+import androidx.wear.compose.material3.ListHeader
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.ScreenScaffold
+import androidx.wear.compose.material3.ScrollIndicator
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.TimeText
+import com.android.permissioncontroller.permission.ui.wear.elements.AnnotatedText
+import com.android.permissioncontroller.permission.ui.wear.elements.Wear2Scaffold
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumn
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.rememberResponsiveColumnState
+import com.android.permissioncontroller.permission.ui.wear.elements.rememberDrawablePainter
+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.WearPermissionTheme
+
+/**
+ * This component is wrapper on material scaffold component. It helps with time text, scroll
+ * indicator and standard list elements like title, icon and subtitle.
+ */
+@Composable
+internal fun WearPermissionScaffold(
+ materialUIVersion: WearPermissionMaterialUIVersion = MATERIAL2_5,
+ showTimeText: Boolean,
+ title: String?,
+ subtitle: CharSequence?,
+ image: Any?,
+ isLoading: Boolean,
+ content: ScalingLazyListScope.() -> Unit,
+ titleTestTag: String? = null,
+ subtitleTestTag: String? = null,
+) {
+
+ if (materialUIVersion == MATERIAL2_5) {
+ Wear2Scaffold(
+ showTimeText,
+ title,
+ subtitle,
+ image,
+ isLoading,
+ content,
+ titleTestTag,
+ subtitleTestTag,
+ )
+ } else {
+ WearPermissionScaffoldInternal(
+ showTimeText,
+ title,
+ subtitle,
+ image,
+ isLoading,
+ content,
+ titleTestTag,
+ subtitleTestTag,
+ )
+ }
+}
+
+@Composable
+private fun WearPermissionScaffoldInternal(
+ showTimeText: Boolean,
+ title: String?,
+ subtitle: CharSequence?,
+ image: Any?,
+ isLoading: Boolean,
+ content: ScalingLazyListScope.() -> Unit,
+ titleTestTag: String? = null,
+ subtitleTestTag: String? = null,
+) {
+ val screenWidth = LocalConfiguration.current.screenWidthDp
+ val screenHeight = LocalConfiguration.current.screenHeightDp
+ val paddingDefaults =
+ WearPermissionScaffoldPaddingDefaults(
+ screenWidth = screenWidth,
+ screenHeight = screenHeight,
+ titleNeedsLargePadding = subtitle == null,
+ )
+ val columnState =
+ rememberResponsiveColumnState(contentPadding = { paddingDefaults.scrollContentPadding })
+ WearPermissionTheme(version = WearPermissionMaterialUIVersion.MATERIAL3) {
+ AppScaffold(timeText = wearPermissionTimeText(showTimeText && !isLoading)) {
+ ScreenScaffold(
+ scrollInfoProvider = ScrollInfoProvider(columnState.state),
+ scrollIndicator = wearPermissionScrollIndicator(!isLoading, columnState),
+ ) {
+ Box(modifier = Modifier.fillMaxSize()) {
+ if (isLoading) {
+ CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
+ } else {
+ ScrollingView(
+ columnState = columnState,
+ icon = painterFromImage(image),
+ title = title,
+ titleTestTag = titleTestTag,
+ titlePaddingValues = paddingDefaults.titlePaddingValues,
+ subtitle = subtitle,
+ subtitleTestTag = subtitleTestTag,
+ subTitlePaddingValues = paddingDefaults.subTitlePaddingValues,
+ content = content,
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+private class WearPermissionScaffoldPaddingDefaults(
+ screenWidth: Int,
+ screenHeight: Int,
+ titleNeedsLargePadding: Boolean,
+) {
+ private val firstSpacerItemHeight = 0.dp
+ private val scrollContentHorizontalPadding = (screenWidth * 0.052).dp
+ private val titleHorizontalPadding = (screenWidth * 0.0884).dp
+ private val subtitleHorizontalPadding = (screenWidth * 0.0416).dp
+ private val scrollContentTopPadding = (screenHeight * 0.1456).dp - firstSpacerItemHeight
+ private val scrollContentBottomPadding = (screenHeight * 0.3636).dp
+ private val defaultItemPadding = 4.dp
+ private val largeItemPadding = 8.dp
+ val titlePaddingValues =
+ PaddingValues(
+ start = titleHorizontalPadding,
+ top = defaultItemPadding,
+ bottom = if (titleNeedsLargePadding) largeItemPadding else defaultItemPadding,
+ end = titleHorizontalPadding,
+ )
+ val subTitlePaddingValues =
+ PaddingValues(
+ start = subtitleHorizontalPadding,
+ top = defaultItemPadding,
+ bottom = largeItemPadding,
+ end = subtitleHorizontalPadding,
+ )
+ val scrollContentPadding =
+ PaddingValues(
+ start = scrollContentHorizontalPadding,
+ end = scrollContentHorizontalPadding,
+ top = scrollContentTopPadding,
+ bottom = scrollContentBottomPadding,
+ )
+}
+
+@Composable
+private fun BoxScope.ScrollingView(
+ columnState: ScalingLazyColumnState,
+ icon: Painter?,
+ title: String?,
+ titleTestTag: String?,
+ subtitle: CharSequence?,
+ subtitleTestTag: String?,
+ titlePaddingValues: PaddingValues,
+ subTitlePaddingValues: PaddingValues,
+ content: ScalingLazyListScope.() -> Unit,
+) {
+ ScalingLazyColumn(columnState = columnState) {
+ iconItem(icon, Modifier.size(24.dp))
+ titleItem(text = title, testTag = titleTestTag, contentPaddingValues = titlePaddingValues)
+ subtitleItem(
+ text = subtitle,
+ testTag = subtitleTestTag,
+ modifier = Modifier.align(Alignment.Center).padding(subTitlePaddingValues),
+ )
+ content()
+ }
+}
+
+private fun wearPermissionTimeText(showTime: Boolean): @Composable () -> Unit {
+ return if (showTime) {
+ { TimeText { time() } }
+ } else {
+ {}
+ }
+}
+
+private fun wearPermissionScrollIndicator(
+ showIndicator: Boolean,
+ columnState: ScalingLazyColumnState,
+): @Composable (BoxScope.() -> Unit)? {
+ return if (showIndicator) {
+ {
+ ScrollIndicator(
+ modifier = Modifier.align(Alignment.CenterEnd),
+ state = columnState.state,
+ )
+ }
+ } else {
+ null
+ }
+}
+
+@Composable
+private fun painterFromImage(image: Any?): Painter? {
+ return when (image) {
+ is Int -> painterResource(id = image)
+ is Drawable -> rememberDrawablePainter(image)
+ else -> null
+ }
+}
+
+private fun Modifier.optionalTestTag(tag: String?): Modifier {
+ if (tag == null) {
+ return this
+ }
+ return this then testTag(tag)
+}
+
+private fun ScalingLazyListScope.iconItem(painter: Painter?, modifier: Modifier = Modifier) =
+ painter?.let {
+ item {
+ val iconColor = WearPermissionButtonStyle.Secondary.material3ButtonColors().iconColor
+ Image(
+ painter = it,
+ contentDescription = null,
+ contentScale = ContentScale.Crop,
+ modifier = modifier,
+ colorFilter = ColorFilter.tint(iconColor),
+ )
+ }
+ }
+
+private fun ScalingLazyListScope.titleItem(
+ text: String?,
+ testTag: String?,
+ contentPaddingValues: PaddingValues,
+ modifier: Modifier = Modifier,
+) =
+ text?.let {
+ item {
+ ListHeader(
+ modifier = modifier.requiredHeightIn(1.dp), // We do not want default min height
+ contentPadding = contentPaddingValues,
+ ) {
+ Text(
+ text = it,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.optionalTestTag(testTag),
+ )
+ }
+ }
+ }
+
+private fun ScalingLazyListScope.subtitleItem(
+ text: CharSequence?,
+ testTag: String?,
+ modifier: Modifier = Modifier,
+) =
+ text?.let {
+ item {
+ AnnotatedText(
+ text = it,
+ style =
+ MaterialTheme.typography.bodyMedium.copy(
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ ),
+ modifier = modifier.optionalTestTag(testTag),
+ shouldCapitalize = true,
+ )
+ }
+ }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt
new file mode 100644
index 000000000..9841ca521
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright 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
+ *
+ * https://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.BoxScope
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.CheckboxButton
+import androidx.wear.compose.material3.LocalTextConfiguration
+import androidx.wear.compose.material3.RadioButton
+import androidx.wear.compose.material3.SwitchButton
+import androidx.wear.compose.material3.Text
+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.toggleControlSemantics
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion
+
+/**
+ * The custom component is a wrapper on different material3 toggle controls.
+ * 1. It provides an unified interface for RadioButton,CheckButton and SwitchButton.
+ * 2. It takes icon, primary, secondary label resources and construct them applying permission app
+ * defaults
+ * 3. Applies custom semantics for based on the toggle control type
+ */
+@Composable
+fun WearPermissionToggleControl(
+ toggleControl: ToggleChipToggleControl,
+ label: String,
+ checked: Boolean,
+ onCheckedChanged: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+ labelMaxLines: Int? = null,
+ materialUIVersion: WearPermissionMaterialUIVersion =
+ WearPermissionMaterialUIVersion.MATERIAL2_5,
+ iconBuilder: WearPermissionIconBuilder? = null,
+ secondaryLabel: String? = null,
+ secondaryLabelMaxLines: Int? = null,
+ enabled: Boolean = true,
+ style: WearPermissionToggleControlStyle = WearPermissionToggleControlStyle.Default,
+) {
+ if (materialUIVersion == WearPermissionMaterialUIVersion.MATERIAL2_5) {
+ ToggleChip(
+ toggleControl = toggleControl,
+ label = label,
+ labelMaxLine = labelMaxLines,
+ checked = checked,
+ onCheckedChanged = onCheckedChanged,
+ modifier = modifier,
+ icon = iconBuilder?.iconResource,
+ secondaryLabel = secondaryLabel,
+ secondaryLabelMaxLine = secondaryLabelMaxLines,
+ enabled = enabled,
+ colors = style.material2ToggleControlColors(),
+ )
+ } else {
+ WearPermissionToggleControlInternal(
+ label = label,
+ toggleControl = toggleControl,
+ checked = checked,
+ onCheckedChanged = onCheckedChanged,
+ modifier = modifier,
+ iconBuilder = iconBuilder,
+ labelMaxLines = labelMaxLines,
+ secondaryLabel = secondaryLabel,
+ secondaryLabelMaxLines = secondaryLabelMaxLines,
+ enabled = enabled,
+ style = style,
+ )
+ }
+}
+
+@Composable
+private fun WearPermissionToggleControlInternal(
+ label: String,
+ toggleControl: ToggleChipToggleControl,
+ checked: Boolean,
+ onCheckedChanged: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+ iconBuilder: WearPermissionIconBuilder? = null,
+ labelMaxLines: Int? = null,
+ secondaryLabel: String? = null,
+ secondaryLabelMaxLines: Int? = null,
+ enabled: Boolean = true,
+ style: WearPermissionToggleControlStyle = WearPermissionToggleControlStyle.Default,
+) {
+ val labelParam: (@Composable RowScope.() -> Unit) = {
+ Text(
+ text = label,
+ modifier = Modifier.fillMaxWidth(),
+ maxLines = labelMaxLines ?: LocalTextConfiguration.current.maxLines,
+ )
+ }
+
+ val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
+ secondaryLabel?.let {
+ {
+ Text(
+ text = it,
+ modifier = Modifier.fillMaxWidth(),
+ maxLines = secondaryLabelMaxLines ?: LocalTextConfiguration.current.maxLines,
+ )
+ }
+ }
+
+ val iconParam: (@Composable BoxScope.() -> Unit)? = iconBuilder?.let { { it.build() } }
+
+ val updatedModifier =
+ modifier
+ .fillMaxWidth()
+ // .heightIn(min = 58.dp) // TODO(b/370783358): This should be a overlaid value
+ .toggleControlSemantics(toggleControl, checked)
+
+ when (toggleControl) {
+ ToggleChipToggleControl.Radio ->
+ RadioButton(
+ selected = checked,
+ onSelect = {
+ // We do not want to call if it is already checked.
+ // Radio button can't be toggled off
+ if (!checked) {
+ onCheckedChanged(true)
+ }
+ },
+ modifier = updatedModifier,
+ enabled = enabled,
+ icon = iconParam,
+ secondaryLabel = secondaryLabelParam,
+ label = labelParam,
+ colors = style.radioButtonColorScheme(),
+ )
+
+ ToggleChipToggleControl.Checkbox ->
+ CheckboxButton(
+ checked = checked,
+ onCheckedChange = onCheckedChanged,
+ modifier = updatedModifier,
+ enabled = enabled,
+ icon = iconParam,
+ secondaryLabel = secondaryLabelParam,
+ label = labelParam,
+ colors = style.checkboxColorScheme(),
+ )
+
+ ToggleChipToggleControl.Switch ->
+ SwitchButton(
+ checked = checked,
+ onCheckedChange = onCheckedChanged,
+ modifier = updatedModifier,
+ enabled = enabled,
+ icon = iconParam,
+ secondaryLabel = secondaryLabelParam,
+ label = labelParam,
+ colors = style.switchButtonColorScheme(),
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt
new file mode 100644
index 000000000..b5746f019
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright 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
+ *
+ * https://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.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.wear.compose.material.ToggleChipColors
+import androidx.wear.compose.material.ToggleChipDefaults.toggleChipColors
+import androidx.wear.compose.material3.CheckboxButtonColors
+import androidx.wear.compose.material3.CheckboxButtonDefaults.checkboxButtonColors
+import androidx.wear.compose.material3.RadioButtonColors
+import androidx.wear.compose.material3.RadioButtonDefaults.radioButtonColors
+import androidx.wear.compose.material3.SwitchButtonColors
+import androidx.wear.compose.material3.SwitchButtonDefaults.switchButtonColors
+import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipBackgroundColors
+import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipDisabledColors
+
+/**
+ * Defines toggle control styles, It helps in setting the right colors scheme to a toggle control.
+ */
+enum class WearPermissionToggleControlStyle {
+ Default,
+ Transparent,
+ DisabledLike,
+}
+
+@Composable
+internal fun WearPermissionToggleControlStyle.radioButtonColorScheme(): RadioButtonColors {
+ return when (this) {
+ WearPermissionToggleControlStyle.Default -> radioButtonColors()
+ WearPermissionToggleControlStyle.Transparent -> radioButtonTransparentColors()
+ WearPermissionToggleControlStyle.DisabledLike -> radioButtonDisabledLikeColors()
+ }
+}
+
+@Composable
+internal fun WearPermissionToggleControlStyle.checkboxColorScheme(): CheckboxButtonColors {
+ return when (this) {
+ WearPermissionToggleControlStyle.Default -> checkboxButtonColors()
+ WearPermissionToggleControlStyle.Transparent -> checkButtonTransparentColors()
+ WearPermissionToggleControlStyle.DisabledLike -> checkboxDisabledLikeColors()
+ }
+}
+
+@Composable
+internal fun WearPermissionToggleControlStyle.switchButtonColorScheme(): SwitchButtonColors {
+ return when (this) {
+ WearPermissionToggleControlStyle.Default -> switchButtonColors()
+ WearPermissionToggleControlStyle.Transparent -> switchButtonTransparentColors()
+ WearPermissionToggleControlStyle.DisabledLike -> switchButtonDisabledLikeColors()
+ }
+}
+
+@Composable
+internal fun WearPermissionToggleControlStyle.material2ToggleControlColors(): ToggleChipColors {
+ return when (this) {
+ WearPermissionToggleControlStyle.Default -> toggleChipColors()
+ WearPermissionToggleControlStyle.Transparent -> toggleChipBackgroundColors()
+ WearPermissionToggleControlStyle.DisabledLike -> toggleChipDisabledColors()
+ }
+}
+
+@Composable
+private fun checkButtonTransparentColors() =
+ checkboxButtonColors(
+ checkedContainerColor = Color.Transparent,
+ uncheckedContainerColor = Color.Transparent,
+ disabledCheckedContainerColor = Color.Transparent,
+ disabledUncheckedContainerColor = Color.Transparent,
+ )
+
+@Composable
+private fun radioButtonTransparentColors() =
+ radioButtonColors(
+ selectedContainerColor = Color.Transparent,
+ unselectedContainerColor = Color.Transparent,
+ disabledSelectedContainerColor = Color.Transparent,
+ disabledUnselectedContainerColor = Color.Transparent,
+ )
+
+@Composable
+private fun switchButtonTransparentColors() =
+ switchButtonColors(
+ checkedContainerColor = Color.Transparent,
+ uncheckedContainerColor = Color.Transparent,
+ disabledCheckedContainerColor = Color.Transparent,
+ disabledUncheckedContainerColor = Color.Transparent,
+ )
+
+@Composable
+private fun checkboxDisabledLikeColors(): CheckboxButtonColors {
+ val defaultColors = checkboxButtonColors()
+ return checkboxButtonColors(
+ checkedContainerColor = defaultColors.disabledCheckedContainerColor,
+ checkedContentColor = defaultColors.disabledCheckedContentColor,
+ checkedSecondaryContentColor = defaultColors.disabledCheckedSecondaryContentColor,
+ checkedIconColor = defaultColors.disabledCheckedIconColor,
+ checkedBoxColor = defaultColors.disabledCheckedBoxColor,
+ checkedCheckmarkColor = defaultColors.disabledCheckedCheckmarkColor,
+ uncheckedContainerColor = defaultColors.disabledUncheckedContainerColor,
+ uncheckedContentColor = defaultColors.disabledUncheckedContentColor,
+ uncheckedSecondaryContentColor = defaultColors.disabledUncheckedSecondaryContentColor,
+ uncheckedIconColor = defaultColors.disabledUncheckedIconColor,
+ uncheckedBoxColor = defaultColors.disabledUncheckedBoxColor,
+ )
+}
+
+@Composable
+private fun radioButtonDisabledLikeColors(): RadioButtonColors {
+ val defaultColors = radioButtonColors()
+ return radioButtonColors(
+ selectedContainerColor = defaultColors.disabledSelectedContainerColor,
+ selectedContentColor = defaultColors.disabledSelectedContentColor,
+ selectedSecondaryContentColor = defaultColors.disabledSelectedSecondaryContentColor,
+ selectedIconColor = defaultColors.disabledSelectedIconColor,
+ selectedControlColor = defaultColors.disabledSelectedControlColor,
+ unselectedContentColor = defaultColors.disabledUnselectedContentColor,
+ unselectedContainerColor = defaultColors.disabledUnselectedContainerColor,
+ unselectedSecondaryContentColor = defaultColors.disabledUnselectedSecondaryContentColor,
+ unselectedIconColor = defaultColors.disabledUnselectedIconColor,
+ unselectedControlColor = defaultColors.disabledUnselectedControlColor,
+ )
+}
+
+@Composable
+private fun switchButtonDisabledLikeColors(): SwitchButtonColors {
+ val defaultColors = switchButtonColors()
+ return switchButtonColors(
+ checkedContainerColor = defaultColors.disabledCheckedContainerColor,
+ checkedContentColor = defaultColors.disabledCheckedContentColor,
+ checkedSecondaryContentColor = defaultColors.disabledCheckedSecondaryContentColor,
+ checkedIconColor = defaultColors.disabledCheckedIconColor,
+ checkedThumbColor = defaultColors.disabledCheckedThumbColor,
+ checkedThumbIconColor = defaultColors.disabledCheckedThumbIconColor,
+ checkedTrackColor = defaultColors.disabledCheckedTrackColor,
+ checkedTrackBorderColor = defaultColors.disabledCheckedTrackBorderColor,
+ uncheckedContainerColor = defaultColors.disabledUncheckedContainerColor,
+ uncheckedContentColor = defaultColors.disabledUncheckedContentColor,
+ uncheckedSecondaryContentColor = defaultColors.disabledUncheckedSecondaryContentColor,
+ uncheckedIconColor = defaultColors.disabledUncheckedIconColor,
+ uncheckedThumbColor = defaultColors.disabledUncheckedThumbColor,
+ uncheckedTrackColor = defaultColors.checkedTrackColor.run { copy(alpha = alpha * 0.12f) },
+ uncheckedTrackBorderColor = defaultColors.disabledUncheckedTrackBorderColor,
+ )
+}
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
new file mode 100644
index 000000000..c7ed0958c
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.theme
+
+import android.content.Context
+import android.os.SystemProperties
+import androidx.annotation.ColorRes
+import androidx.annotation.DimenRes
+import androidx.annotation.DoNotInline
+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 {
+ val colorInt = context.resources.getColor(id, context.theme)
+ Color(colorInt)
+ } catch (e: Exception) {
+ null
+ }
+ }
+
+ @DoNotInline
+ fun getString(context: Context, @StringRes id: Int): String? {
+ return try {
+ context.resources.getString(id)
+ } catch (e: Exception) {
+ null
+ }
+ }
+
+ @DoNotInline
+ fun getDimen(context: Context, @DimenRes id: Int): Float? {
+ return try {
+ context.resources.getDimension(id) / context.resources.displayMetrics.density
+ } catch (e: Exception) {
+ null
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3ColorScheme.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3ColorScheme.kt
new file mode 100644
index 000000000..7ac6c8114
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3ColorScheme.kt
@@ -0,0 +1,209 @@
+/*
+ * 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.theme
+
+import android.content.Context
+import android.os.Build
+import androidx.annotation.ColorRes
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.graphics.Color
+import androidx.wear.compose.material3.ColorScheme
+
+/**
+ * Creates a dynamic color maps that can be overlaid. In wear we only support dark theme for the
+ * time being. If the device supports dynamic color generation these resources are updated with the
+ * generated colors
+ */
+internal object WearComposeMaterial3ColorScheme {
+
+ @RequiresApi(Build.VERSION_CODES.S)
+ fun tonalColorScheme(context: Context): ColorScheme {
+ val tonalPalette = dynamicTonalPalette(context)
+ return ColorScheme(
+ background = tonalPalette.neutral0,
+ onBackground = tonalPalette.neutral100,
+ onPrimary = tonalPalette.primary10,
+ onPrimaryContainer = tonalPalette.primary90,
+ onSecondary = tonalPalette.secondary10,
+ onSecondaryContainer = tonalPalette.secondary90,
+ onSurface = tonalPalette.neutral95,
+ onSurfaceVariant = tonalPalette.neutralVariant80,
+ onTertiary = tonalPalette.tertiary10,
+ onTertiaryContainer = tonalPalette.tertiary90,
+ outline = tonalPalette.neutralVariant60,
+ outlineVariant = tonalPalette.neutralVariant40,
+ primary = tonalPalette.primary90,
+ primaryContainer = tonalPalette.primary30,
+ primaryDim = tonalPalette.primary80,
+ secondary = tonalPalette.secondary90,
+ secondaryContainer = tonalPalette.secondary30,
+ secondaryDim = tonalPalette.secondary80,
+ surfaceContainer = tonalPalette.neutral20,
+ surfaceContainerHigh = tonalPalette.neutral30,
+ tertiary = tonalPalette.tertiary90,
+ tertiaryContainer = tonalPalette.tertiary30,
+ tertiaryDim = tonalPalette.tertiary80,
+ )
+ }
+
+ private fun Color.updatedColor(context: Context, @ColorRes colorRes: Int): Color {
+ return ResourceHelper.getColor(context, colorRes) ?: this
+ }
+
+ @RequiresApi(36)
+ fun dynamicColorScheme(context: Context): ColorScheme {
+ val defaultColorScheme = ColorScheme()
+ return ColorScheme(
+ primary =
+ defaultColorScheme.primary.updatedColor(
+ context,
+ android.R.color.system_primary_fixed,
+ ),
+ primaryDim =
+ defaultColorScheme.primaryDim.updatedColor(
+ context,
+ android.R.color.system_primary_fixed_dim,
+ ),
+ primaryContainer =
+ defaultColorScheme.primaryContainer.updatedColor(
+ context,
+ android.R.color.system_primary_container_dark,
+ ),
+ onPrimary =
+ defaultColorScheme.onPrimary.updatedColor(
+ context,
+ android.R.color.system_on_primary_fixed,
+ ),
+ onPrimaryContainer =
+ defaultColorScheme.onPrimaryContainer.updatedColor(
+ context,
+ android.R.color.system_on_primary_container_dark,
+ ),
+ secondary =
+ defaultColorScheme.secondary.updatedColor(
+ context,
+ android.R.color.system_secondary_fixed,
+ ),
+ secondaryDim =
+ defaultColorScheme.secondaryDim.updatedColor(
+ context,
+ android.R.color.system_secondary_fixed_dim,
+ ),
+ secondaryContainer =
+ defaultColorScheme.secondaryContainer.updatedColor(
+ context,
+ android.R.color.system_secondary_container_dark,
+ ),
+ onSecondary =
+ defaultColorScheme.onSecondary.updatedColor(
+ context,
+ android.R.color.system_on_secondary_fixed,
+ ),
+ onSecondaryContainer =
+ defaultColorScheme.onSecondaryContainer.updatedColor(
+ context,
+ android.R.color.system_on_secondary_container_dark,
+ ),
+ tertiary =
+ defaultColorScheme.tertiary.updatedColor(
+ context,
+ android.R.color.system_tertiary_fixed,
+ ),
+ tertiaryDim =
+ defaultColorScheme.tertiaryDim.updatedColor(
+ context,
+ android.R.color.system_tertiary_fixed_dim,
+ ),
+ tertiaryContainer =
+ defaultColorScheme.tertiaryContainer.updatedColor(
+ context,
+ android.R.color.system_tertiary_container_dark,
+ ),
+ onTertiary =
+ defaultColorScheme.onTertiary.updatedColor(
+ context,
+ android.R.color.system_on_tertiary_fixed,
+ ),
+ onTertiaryContainer =
+ defaultColorScheme.onTertiaryContainer.updatedColor(
+ context,
+ android.R.color.system_on_tertiary_container_dark,
+ ),
+ surfaceContainerLow =
+ defaultColorScheme.surfaceContainerLow.updatedColor(
+ context,
+ android.R.color.system_surface_container_low_dark,
+ ),
+ surfaceContainer =
+ defaultColorScheme.surfaceContainer.updatedColor(
+ context,
+ android.R.color.system_surface_container_dark,
+ ),
+ surfaceContainerHigh =
+ defaultColorScheme.surfaceContainerHigh.updatedColor(
+ context,
+ android.R.color.system_surface_container_high_dark,
+ ),
+ onSurface =
+ defaultColorScheme.onSurface.updatedColor(
+ context,
+ android.R.color.system_on_surface_dark,
+ ),
+ onSurfaceVariant =
+ defaultColorScheme.onSurfaceVariant.updatedColor(
+ context,
+ android.R.color.system_on_surface_variant_dark,
+ ),
+ outline =
+ defaultColorScheme.outline.updatedColor(
+ context,
+ android.R.color.system_outline_dark,
+ ),
+ outlineVariant =
+ defaultColorScheme.outlineVariant.updatedColor(
+ context,
+ android.R.color.system_outline_variant_dark,
+ ),
+ background =
+ defaultColorScheme.background.updatedColor(
+ context,
+ android.R.color.system_background_dark,
+ ),
+ onBackground =
+ defaultColorScheme.onBackground.updatedColor(
+ context,
+ android.R.color.system_on_background_dark,
+ ),
+ error =
+ defaultColorScheme.error.updatedColor(context, android.R.color.system_error_dark),
+ onError =
+ defaultColorScheme.onError.updatedColor(
+ context,
+ android.R.color.system_on_error_dark,
+ ),
+ errorContainer =
+ defaultColorScheme.errorContainer.updatedColor(
+ context,
+ android.R.color.system_error_container_dark,
+ ),
+ onErrorContainer =
+ defaultColorScheme.onErrorContainer.updatedColor(
+ context,
+ android.R.color.system_on_error_container_dark,
+ ),
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Shapes.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Shapes.kt
new file mode 100644
index 000000000..f81022842
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Shapes.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.theme
+
+import android.content.Context
+import androidx.annotation.DimenRes
+import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material3.Shapes
+import com.android.permissioncontroller.R
+
+// TODO(b/324928718): Use system defined symbols.
+internal object WearComposeMaterial3Shapes {
+ private fun CornerBasedShape.updatedShape(
+ context: Context,
+ @DimenRes cornerSizeRes: Int,
+ ): CornerBasedShape {
+ val size = ResourceHelper.getDimen(context, cornerSizeRes)?.dp ?: return this
+ return copy(CornerSize(size))
+ }
+
+ fun dynamicShapes(context: Context): Shapes {
+ val defaultShapes = Shapes()
+ return Shapes(
+ extraLarge =
+ defaultShapes.extraLarge.updatedShape(
+ context,
+ R.dimen.wear_compose_material3_shape_corner_extra_large_size,
+ ),
+ large =
+ defaultShapes.large.updatedShape(
+ context,
+ R.dimen.wear_compose_material3_shape_corner_large_size,
+ ),
+ medium =
+ defaultShapes.medium.updatedShape(
+ context,
+ R.dimen.wear_compose_material3_shape_corner_medium_size,
+ ),
+ small =
+ defaultShapes.small.updatedShape(
+ context,
+ R.dimen.wear_compose_material3_shape_corner_small_size,
+ ),
+ extraSmall =
+ defaultShapes.extraSmall.updatedShape(
+ context,
+ R.dimen.wear_compose_material3_shape_corner_extra_small_size,
+ ),
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3TypeScaleTokens.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3TypeScaleTokens.kt
new file mode 100644
index 000000000..a4ec9ee1d
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3TypeScaleTokens.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.theme
+
+/*
+ * These values are retrieved from https://carbon.googleplex.com/wear-m3/pages and
+ * modified by UX.
+ * These values are internal to material3 library. We copied them to support flex font families
+ * Do not edit directly. Copy paste from compose material library.
+ */
+
+internal object WearComposeMaterial3TypeScaleTokens {
+ val ArcLargeRoundness = 100.0f
+ val ArcLargeWeight = 600.0f
+ val ArcLargeWidth = 100.0f
+
+ val ArcMediumRoundness = 100.0f
+ val ArcMediumWeight = 600.0f
+ val ArcMediumWidth = 90.0f
+
+ val ArcSmallRoundness = 100.0f
+ val ArcSmallWeight = 550.0f
+ val ArcSmallWidth = 90.0f
+
+ val BodyExtraSmallRoundness = 100.0f
+ val BodyExtraSmallWeight = 500.0f
+ val BodyExtraSmallWidth = 84.0f
+
+ val BodyLargeRoundness = 100.0f
+ val BodyLargeWeight = 450.0f
+ val BodyLargeWidth = 90.0f
+
+ val BodyMediumRoundness = 100.0f
+ val BodyMediumWeight = 450.0f
+ val BodyMediumWidth = 90.0f
+
+ val BodySmallRoundness = 100.0f
+ val BodySmallWeight = 500.0f
+ val BodySmallWidth = 86.0f
+
+ val DisplayLargeRoundness = 100.0f
+ val DisplayLargeWeight = 450.0f
+ val DisplayLargeWidth = 100.0f
+
+ val DisplayMediumRoundness = 100.0f
+ val DisplayMediumWeight = 500.0f
+ val DisplayMediumWidth = 100.0f
+
+ val DisplaySmallRoundness = 100.0f
+ val DisplaySmallWeight = 500.0f
+ val DisplaySmallWidth = 100.0f
+
+ val LabelLargeRoundness = 100.0f
+ val LabelLargeWeight = 500.0f
+ val LabelLargeWidth = 100.0f
+
+ val LabelMediumRoundness = 100.0f
+ val LabelMediumWeight = 500.0f
+ val LabelMediumWidth = 90.0f
+
+ val LabelSmallRoundness = 100.0f
+ val LabelSmallWeight = 500.0f
+ val LabelSmallWidth = 84.0f
+
+ val NumeralExtraLargeRoundness = 100.0f
+ val NumeralExtraLargeWeight = 550.0f
+ val NumeralExtraLargeWidth = 100.0f
+
+ val NumeralExtraSmallRoundness = 100.0f
+ val NumeralExtraSmallWeight = 550.0f
+ val NumeralExtraSmallWidth = 100.0f
+
+ val NumeralLargeRoundness = 100.0f
+ val NumeralLargeWeight = 600.0f
+ val NumeralLargeWidth = 100.0f
+
+ val NumeralMediumRoundness = 100.0f
+ val NumeralMediumWidth = 100.0f
+ val NumeralMediumWeight = 600.0f
+
+ val NumeralSmallRoundness = 100.0f
+ val NumeralSmallWeight = 600.0f
+ val NumeralSmallWidth = 100.0f
+
+ val TitleLargeRoundness = 100.0f
+ val TitleLargeWeight = 500.0f
+ val TitleLargeWidth = 100.0f
+
+ val TitleMediumRoundness = 100.0f
+ val TitleMediumWeight = 550.0f
+ val TitleMediumWidth = 100.0f
+
+ val TitleSmallRoundness = 100.0f
+ val TitleSmallWeight = 550.0f
+ val TitleSmallWidth = 100.0f
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Typography.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Typography.kt
new file mode 100644
index 000000000..ceae526a7
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Typography.kt
@@ -0,0 +1,240 @@
+/*
+ * 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.theme
+
+import android.content.Context
+import androidx.annotation.DimenRes
+import androidx.annotation.StringRes
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.DeviceFontFamilyName
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontVariation
+import androidx.compose.ui.unit.sp
+import androidx.wear.compose.material3.Typography
+import com.android.permissioncontroller.R
+
+internal object WearComposeMaterial3Typography {
+
+ private const val DEVICE_DEFAULT_FLEX_FONT_TYPE = "font-family-flex-device-default"
+
+ fun fontFamily(
+ context: Context,
+ @StringRes id: Int,
+ variationSettings: FontVariation.Settings? = null,
+ ): FontFamily {
+ val typefaceName = ResourceHelper.getString(context, id) ?: DEVICE_DEFAULT_FLEX_FONT_TYPE
+
+ val font =
+ if (variationSettings != null) {
+ Font(
+ familyName = DeviceFontFamilyName(typefaceName),
+ variationSettings = variationSettings,
+ )
+ } else {
+ Font(familyName = DeviceFontFamilyName(typefaceName))
+ }
+ return FontFamily(font)
+ }
+
+ private fun TextStyle.updatedTextStyle(
+ context: Context,
+ @StringRes fontRes: Int,
+ variationSettings: FontVariation.Settings? = null,
+ @DimenRes fontSizeRes: Int,
+ ): TextStyle {
+
+ val fontFamily =
+ fontFamily(context = context, id = fontRes, variationSettings = variationSettings)
+ val fontSize = ResourceHelper.getDimen(context = context, id = fontSizeRes)?.sp ?: fontSize
+
+ return copy(fontFamily = fontFamily, fontSize = fontSize)
+ }
+
+ fun dynamicTypography(context: Context): Typography {
+ val defaultTypography = Typography()
+ return Typography(
+ arcLarge =
+ defaultTypography.arcLarge.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_arc_large_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_arc_large_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.ArcLargeVariationSettings,
+ ),
+ arcMedium =
+ defaultTypography.arcMedium.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_arc_medium_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_arc_medium_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.ArcMediumVariationSettings,
+ ),
+ arcSmall =
+ defaultTypography.arcSmall.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_arc_small_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_arc_small_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.ArcSmallVariationSettings,
+ ),
+ bodyLarge =
+ defaultTypography.bodyLarge.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_body_large_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_body_large_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.BodyLargeVariationSettings,
+ ),
+ bodyMedium =
+ defaultTypography.bodyMedium.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_body_medium_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_body_medium_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.BodyMediumVariationSettings,
+ ),
+ bodySmall =
+ defaultTypography.bodySmall.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_body_small_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_body_small_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.BodySmallVariationSettings,
+ ),
+ bodyExtraSmall =
+ defaultTypography.bodyExtraSmall.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_body_extra_small_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_body_extra_small_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.BodyExtraSmallVariationSettings,
+ ),
+ displayLarge =
+ defaultTypography.displayLarge.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_display_large_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_display_large_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.DisplayLargeVariationSettings,
+ ),
+ displayMedium =
+ defaultTypography.displayMedium.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_display_medium_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_display_medium_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.DisplayMediumVariationSettings,
+ ),
+ displaySmall =
+ defaultTypography.displaySmall.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_display_small_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_display_small_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.DisplaySmallVariationSettings,
+ ),
+ labelLarge =
+ defaultTypography.labelLarge.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_label_large_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_label_large_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.LabelLargeVariationSettings,
+ ),
+ labelMedium =
+ defaultTypography.labelMedium.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_label_medium_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_label_medium_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.LabelMediumVariationSettings,
+ ),
+ labelSmall =
+ defaultTypography.labelSmall.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_label_small_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_label_small_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.LabelSmallVariationSettings,
+ ),
+ numeralExtraLarge =
+ defaultTypography.numeralExtraLarge.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_numeral_extra_large_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_numeral_extra_large_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.NumeralExtraLargeVariationSettings,
+ ),
+ numeralLarge =
+ defaultTypography.numeralLarge.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_numeral_large_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_numeral_large_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.NumeralLargeVariationSettings,
+ ),
+ numeralMedium =
+ defaultTypography.numeralMedium.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_numeral_medium_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_numeral_medium_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.NumeralMediumVariationSettings,
+ ),
+ numeralSmall =
+ defaultTypography.numeralSmall.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_numeral_small_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_numeral_small_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.NumeralSmallVariationSettings,
+ ),
+ numeralExtraSmall =
+ defaultTypography.numeralExtraSmall.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_numeral_extra_small_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_numeral_extra_small_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.NumeralExtraSmallVariationSettings,
+ ),
+ titleLarge =
+ defaultTypography.titleLarge.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_title_large_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_title_large_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.TitleLargeVariationSettings,
+ ),
+ titleMedium =
+ defaultTypography.titleMedium.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_title_medium_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_title_medium_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.TitleMediumVariationSettings,
+ ),
+ titleSmall =
+ defaultTypography.titleSmall.updatedTextStyle(
+ context = context,
+ fontRes = R.string.wear_compose_material3_title_small_font_family,
+ fontSizeRes = R.dimen.wear_compose_material3_title_small_font_size,
+ variationSettings =
+ WearComposeMaterial3VariableFontTokens.TitleSmallVariationSettings,
+ ),
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3VariableFontTokens.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3VariableFontTokens.kt
new file mode 100644
index 000000000..1b42a3b05
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3VariableFontTokens.kt
@@ -0,0 +1,186 @@
+/*
+ * 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.theme
+
+import androidx.compose.ui.text.font.FontVariation
+
+internal object WearComposeMaterial3VariableFontTokens {
+ val ArcLargeVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.ArcLargeRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.ArcLargeWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.ArcLargeWeight),
+ )
+ val ArcMediumVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.ArcMediumRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.ArcMediumWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.ArcMediumWeight),
+ )
+ val ArcSmallVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.ArcSmallRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.ArcSmallWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.ArcSmallWeight),
+ )
+ val BodyExtraSmallVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting(
+ "ROND",
+ WearComposeMaterial3TypeScaleTokens.BodyExtraSmallRoundness,
+ ),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.BodyExtraSmallWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.BodyExtraSmallWeight),
+ )
+ val BodyLargeVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.BodyLargeRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.BodyLargeWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.BodyLargeWeight),
+ )
+ val BodyMediumVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.BodyMediumRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.BodyMediumWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.BodyMediumWeight),
+ )
+ val BodySmallVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.BodySmallRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.BodySmallWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.BodySmallWeight),
+ )
+ val DisplayLargeVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting(
+ "ROND",
+ WearComposeMaterial3TypeScaleTokens.DisplayLargeRoundness,
+ ),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.DisplayLargeWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.DisplayLargeWeight),
+ )
+ val DisplayMediumVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting(
+ "ROND",
+ WearComposeMaterial3TypeScaleTokens.DisplayMediumRoundness,
+ ),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.DisplayMediumWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.DisplayMediumWeight),
+ )
+ val DisplaySmallVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting(
+ "ROND",
+ WearComposeMaterial3TypeScaleTokens.DisplaySmallRoundness,
+ ),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.DisplaySmallWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.DisplaySmallWeight),
+ )
+ val LabelLargeVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.LabelLargeRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.LabelLargeWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.LabelLargeWeight),
+ )
+ val LabelMediumVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.LabelMediumRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.LabelMediumWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.LabelMediumWeight),
+ )
+ val LabelSmallVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.LabelSmallRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.LabelSmallWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.LabelSmallWeight),
+ )
+ val NumeralExtraLargeVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting(
+ "ROND",
+ WearComposeMaterial3TypeScaleTokens.NumeralExtraLargeRoundness,
+ ),
+ FontVariation.Setting(
+ "wdth",
+ WearComposeMaterial3TypeScaleTokens.NumeralExtraLargeWidth,
+ ),
+ FontVariation.Setting(
+ "wght",
+ WearComposeMaterial3TypeScaleTokens.NumeralExtraLargeWeight,
+ ),
+ )
+ val NumeralExtraSmallVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting(
+ "ROND",
+ WearComposeMaterial3TypeScaleTokens.NumeralExtraSmallRoundness,
+ ),
+ FontVariation.Setting(
+ "wdth",
+ WearComposeMaterial3TypeScaleTokens.NumeralExtraSmallWidth,
+ ),
+ FontVariation.Setting(
+ "wght",
+ WearComposeMaterial3TypeScaleTokens.NumeralExtraSmallWeight,
+ ),
+ )
+ val NumeralLargeVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting(
+ "ROND",
+ WearComposeMaterial3TypeScaleTokens.NumeralLargeRoundness,
+ ),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.NumeralLargeWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.NumeralLargeWeight),
+ )
+ val NumeralMediumVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting(
+ "ROND",
+ WearComposeMaterial3TypeScaleTokens.NumeralMediumRoundness,
+ ),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.NumeralMediumWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.NumeralMediumWeight),
+ )
+ val NumeralSmallVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting(
+ "ROND",
+ WearComposeMaterial3TypeScaleTokens.NumeralSmallRoundness,
+ ),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.NumeralSmallWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.NumeralSmallWeight),
+ )
+ val TitleLargeVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.TitleLargeRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.TitleLargeWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.TitleLargeWeight),
+ )
+ val TitleMediumVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.TitleMediumRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.TitleMediumWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.TitleMediumWeight),
+ )
+ val TitleSmallVariationSettings =
+ FontVariation.Settings(
+ FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.TitleSmallRoundness),
+ FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.TitleSmallWidth),
+ FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.TitleSmallWeight),
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearMaterialBridgedLegacyTheme.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearMaterialBridgedLegacyTheme.kt
new file mode 100644
index 000000000..160dc2e93
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearMaterialBridgedLegacyTheme.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.theme
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.unit.sp
+import androidx.wear.compose.material.Colors
+import androidx.wear.compose.material.Shapes
+import androidx.wear.compose.material.Typography
+
+/**
+ * This exists to support Permission Controller screens that may still use Material 2.5 components
+ * to maintain consistency with the settings screens.
+ *
+ * However to avoid maintaining two sets of resources for overlays, this class construct 2.5 theme
+ * from 3.0
+ */
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+internal class WearMaterialBridgedLegacyTheme
+private constructor(newTheme: WearOverlayableMaterial3Theme) {
+
+ val colors =
+ newTheme.colorScheme.run {
+ Colors(
+ background = background,
+ onBackground = onBackground,
+ primary = onPrimaryContainer, // primary90
+ primaryVariant = primaryDim, // primary80
+ onPrimary = onPrimary, // primary10
+ secondary = tertiary, // Tertiary90
+ secondaryVariant = tertiaryDim, // Tertiary60 - Tertiary80 BestFit.
+ onSecondary = onTertiary, // Tertiary10
+ surface = surfaceContainer, // neutral20
+ onSurface = onSurface, // neutral95
+ onSurfaceVariant = onSurfaceVariant, // neutralVariant80
+ )
+ }
+
+ // Based on:
+ // Material 2:
+ // wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Typography.kt
+ // Material 3:
+ // wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypeScaleTokens.kt
+ val typography =
+ newTheme.typography.run {
+ Typography(
+ display1 = displayLarge, // 40.sp
+ display2 = displayMedium.copy(fontSize = 34.sp, lineHeight = 40.sp),
+ display3 = displayMedium, // 30.sp
+ title1 = displaySmall, // 24.sp
+ title2 = titleLarge, // 20.sp
+ title3 = titleMedium, // 16.sp
+ body1 = bodyLarge, // 16.sp
+ body2 = bodyMedium, // 14.sp
+ caption1 = bodyMedium, // 14.sp
+ caption2 = bodySmall, // 12.sp
+ caption3 = bodyExtraSmall, // 10.sp
+ button = labelMedium, // 15.sp
+ )
+ }
+
+ val shapes = newTheme.shapes.run { Shapes(large = large, medium = medium, small = small) }
+
+ companion object {
+ fun createFrom(newTheme: WearOverlayableMaterial3Theme) =
+ WearMaterialBridgedLegacyTheme(newTheme)
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearOverlayableMaterial3Theme.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearOverlayableMaterial3Theme.kt
new file mode 100644
index 000000000..8aeb5f74d
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearOverlayableMaterial3Theme.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.theme
+
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresApi
+
+/**
+ * Theme wrapper providing Material 3 styling while maintaining compatibility with Runtime Resource
+ * Overlay (RRO).
+ *
+ * Uses the tonal palette from the previous Material Design version until dynamic color tokens are
+ * available in SDK 36.
+ */
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+internal class WearOverlayableMaterial3Theme(context: Context) {
+ val colorScheme =
+ if (Build.VERSION.SDK_INT >= 36) {
+ WearComposeMaterial3ColorScheme.dynamicColorScheme(context)
+ } else {
+ WearComposeMaterial3ColorScheme.tonalColorScheme(context)
+ }
+
+ val typography = WearComposeMaterial3Typography.dynamicTypography(context)
+
+ val shapes = WearComposeMaterial3Shapes.dynamicShapes(context)
+}
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 933cf19f9..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
@@ -1,3 +1,18 @@
+/*
+ * 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.theme
import android.content.Context
@@ -14,11 +29,71 @@ import androidx.compose.ui.text.font.FontFamily
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.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_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(
+ version: WearPermissionMaterialUIVersion = MATERIAL2_5,
+ content: @Composable () -> Unit,
+) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ WearPermissionLegacyTheme(content)
+ } else {
+ // Whether we are ready to use material3 for any screen.
+ val useBridgedTheme = ResourceHelper.material3Enabled
-/** The Material 3 Theme Wrapper for Supporting RRO. */
+ // Material3 UI controls are still being used in the screen that the theme is applied
+ if (version == MATERIAL3) {
+ val material3Theme = WearOverlayableMaterial3Theme(LocalContext.current)
+ Material3Theme(
+ colorScheme = material3Theme.colorScheme,
+ typography = material3Theme.typography,
+ shapes = material3Theme.shapes,
+ content = content,
+ )
+ }
+ // 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(
+ colors = bridgedLegacyTheme.colors,
+ typography = bridgedLegacyTheme.typography,
+ shapes = bridgedLegacyTheme.shapes,
+ content = content,
+ )
+ }
+ // We are not ready for material3 yet in any screens.
+ else {
+ WearPermissionLegacyTheme(content)
+ }
+ }
+}
+
+/**
+ * The Material 2.5 Theme Wrapper for Supporting RRO with legacy resources. This theme is kept here
+ * for backward compatibility. When grant screen is updated to material3 will clean up legacy
+ * resources.
+ */
@Composable
-fun WearPermissionTheme(content: @Composable () -> Unit) {
+fun WearPermissionLegacyTheme(content: @Composable () -> Unit) {
val context = LocalContext.current
val colors =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
index fb33aaffc..0701045f5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
@@ -165,7 +165,7 @@ object KotlinUtils {
val first: A,
val second: B,
val third: C,
- val fourth: D
+ val fourth: D,
)
/**
@@ -179,7 +179,7 @@ object KotlinUtils {
DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_CAMERA_MIC_ICONS_ENABLED,
- true
+ true,
)
}
@@ -190,7 +190,7 @@ object KotlinUtils {
DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_LOCATION_INDICATORS_ENABLED,
- false
+ false,
)
}
@@ -211,7 +211,7 @@ object KotlinUtils {
DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_PHOTO_PICKER_PROMPT_ENABLED,
- true
+ true,
)
}
@@ -236,7 +236,7 @@ object KotlinUtils {
DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
PERMISSION_RATIONALE_ENABLED,
- true
+ true,
)
}
@@ -249,7 +249,7 @@ object KotlinUtils {
DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED,
- true
+ true,
) &&
!DeviceUtils.isAuto(context) &&
!DeviceUtils.isTelevision(context) &&
@@ -262,7 +262,7 @@ object KotlinUtils {
return DeviceConfig.getLong(
DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_SAFETY_LABEL_CHANGES_JOB_INTERVAL_MILLIS,
- Duration.ofDays(30).toMillis()
+ Duration.ofDays(30).toMillis(),
)
}
@@ -278,7 +278,7 @@ object KotlinUtils {
*/
fun <K> getMapAndListDifferences(
newValues: Collection<K>,
- oldValues: Map<K, *>
+ oldValues: Map<K, *>,
): Pair<Set<K>, Set<K>> {
val mapHas = oldValues.keys.toMutableSet()
val listHas = newValues.toMutableSet()
@@ -301,7 +301,7 @@ object KotlinUtils {
fun sortPreferenceGroup(
group: PreferenceGroup,
compare: (lhs: Preference, rhs: Preference) -> Int,
- hasHeader: Boolean
+ hasHeader: Boolean,
) {
val preferences = mutableListOf<Preference>()
for (i in 0 until group.preferenceCount) {
@@ -369,7 +369,7 @@ object KotlinUtils {
return groupInfo.loadSafeLabel(
context.packageManager,
0f,
- TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM,
)
}
@@ -407,7 +407,7 @@ object KotlinUtils {
.loadSafeLabel(
context.packageManager,
20000.toFloat(),
- TextUtils.SAFE_STRING_FLAG_TRIM
+ TextUtils.SAFE_STRING_FLAG_TRIM,
)
} catch (e: PackageManager.NameNotFoundException) {
permName
@@ -431,7 +431,7 @@ object KotlinUtils {
Utils.applyTint(
context,
permInfo.loadUnbadgedIcon(context.packageManager),
- android.R.attr.colorControlNormal
+ android.R.attr.colorControlNormal,
)
}
@@ -445,7 +445,7 @@ object KotlinUtils {
Utils.applyTint(
context,
context.getDrawable(R.drawable.ic_perm_device_info),
- android.R.attr.colorControlNormal
+ android.R.attr.colorControlNormal,
)
}
}
@@ -461,7 +461,8 @@ object KotlinUtils {
fun getPermInfoDescription(context: Context, permName: String): CharSequence {
return try {
val permInfo = context.packageManager.getPermissionInfo(permName, 0)
- permInfo.loadDescription(context.packageManager) ?: ""
+ permInfo.loadDescription(context.packageManager)
+ ?: permInfo.loadLabel(context.packageManager)
} catch (e: PackageManager.NameNotFoundException) {
""
}
@@ -511,7 +512,7 @@ object KotlinUtils {
fun getBadgedPackageIconBitmap(
application: Application,
user: UserHandle,
- packageName: String
+ packageName: String,
): Bitmap? {
val drawable = getBadgedPackageIcon(application, packageName, user)
@@ -547,7 +548,7 @@ object KotlinUtils {
Bitmap.createBitmap(
pkgIcon.intrinsicWidth,
pkgIcon.intrinsicHeight,
- Bitmap.Config.ARGB_8888
+ Bitmap.Config.ARGB_8888,
)
// Draw the icon so it can be displayed.
val canvas = Canvas(pkgIconBmp)
@@ -597,7 +598,7 @@ object KotlinUtils {
activity: Activity,
uid: Int,
requestedPermissions: List<String>,
- requestCode: Int
+ requestCode: Int,
) {
// A clone profile doesn't have a MediaProvider. If the app's user is a clone profile, open
// the photo picker in the parent profile
@@ -653,7 +654,7 @@ object KotlinUtils {
return opsManager.unsafeCheckOpNoThrow(
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
uid,
- packageName
+ packageName,
) == MODE_ALLOWED
}
return true
@@ -692,7 +693,7 @@ object KotlinUtils {
app: Application,
group: LightAppPermGroup,
vararg flags: Pair<Int, Boolean>,
- filterPermissions: List<String> = group.permissions.keys.toList()
+ filterPermissions: List<String> = group.permissions.keys.toList(),
): LightAppPermGroup {
var flagMask = 0
var flagsToSet = 0
@@ -718,7 +719,7 @@ object KotlinUtils {
permName,
group.packageName,
group.userHandle,
- *flags
+ *flags,
)
}
newPerms[permName] =
@@ -727,7 +728,7 @@ object KotlinUtils {
perm.permInfo,
perm.isGranted,
perm.flags or flagsToSet,
- perm.foregroundPerms
+ perm.foregroundPerms,
)
}
return LightAppPermGroup(
@@ -735,7 +736,7 @@ object KotlinUtils {
group.permGroupInfo,
newPerms,
group.hasInstallToRuntimeSplit,
- group.specialLocationGrant
+ group.specialLocationGrant,
)
}
@@ -766,7 +767,7 @@ object KotlinUtils {
isOneTime,
userFixed,
withoutAppOps,
- filterPermissions
+ filterPermissions,
)
}
@@ -785,7 +786,7 @@ object KotlinUtils {
fun grantBackgroundRuntimePermissions(
app: Application,
group: LightAppPermGroup,
- filterPermissions: Collection<String> = group.permissions.keys
+ filterPermissions: Collection<String> = group.permissions.keys,
): LightAppPermGroup {
return grantRuntimePermissions(
app,
@@ -794,7 +795,7 @@ object KotlinUtils {
isOneTime = false,
userFixed = false,
withoutAppOps = false,
- filterPermissions = filterPermissions
+ filterPermissions = filterPermissions,
)
}
@@ -806,7 +807,7 @@ object KotlinUtils {
isOneTime: Boolean = false,
userFixed: Boolean = false,
withoutAppOps: Boolean = false,
- filterPermissions: Collection<String> = group.permissions.keys
+ filterPermissions: Collection<String> = group.permissions.keys,
): LightAppPermGroup {
val deviceId = group.deviceId
val newPerms = group.permissions.toMutableMap()
@@ -837,7 +838,7 @@ object KotlinUtils {
group.packageInfo.packageName,
PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
permFlags,
- user
+ user,
)
}
}
@@ -846,7 +847,7 @@ object KotlinUtils {
if (shouldKillForAnyPermission) {
(app.getSystemService(ActivityManager::class.java) as ActivityManager).killUid(
group.packageInfo.uid,
- KILL_REASON_APP_OP_CHANGE
+ KILL_REASON_APP_OP_CHANGE,
)
}
val newGroup =
@@ -855,7 +856,7 @@ object KotlinUtils {
group.permGroupInfo,
newPerms,
group.hasInstallToRuntimeSplit,
- group.specialLocationGrant
+ group.specialLocationGrant,
)
// If any permission in the group is one time granted, start one time permission session.
if (newGroup.permissions.any { it.value.isOneTime && it.value.isGranted }) {
@@ -867,7 +868,7 @@ object KotlinUtils {
Utils.getOneTimePermissionsTimeout(),
Utils.getOneTimePermissionsKilledDelay(false),
ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
- ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE
+ ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE,
)
} else {
context
@@ -876,7 +877,7 @@ object KotlinUtils {
group.packageName,
Utils.getOneTimePermissionsTimeout(),
ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
- ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE
+ ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE,
)
}
}
@@ -904,7 +905,7 @@ object KotlinUtils {
group: LightAppPermGroup,
isOneTime: Boolean,
userFixed: Boolean = false,
- withoutAppOps: Boolean = false
+ withoutAppOps: Boolean = false,
): Pair<LightPermission, Boolean> {
val pkgInfo = group.packageInfo
val user = UserHandle.getUserHandleForUid(pkgInfo.uid)
@@ -942,7 +943,7 @@ object KotlinUtils {
group.packageName,
PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
oldFlags,
- user
+ user,
)
// TODO: Update this method once AppOp is device aware
disallowAppOp(app, perm, group)
@@ -1018,7 +1019,7 @@ object KotlinUtils {
group.packageInfo.packageName,
PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
newFlags,
- user
+ user,
)
}
@@ -1047,7 +1048,7 @@ object KotlinUtils {
userFixed: Boolean = false,
oneTime: Boolean = false,
forceRemoveRevokedCompat: Boolean = false,
- filterPermissions: Collection<String> = group.permissions.keys
+ filterPermissions: Collection<String> = group.permissions.keys,
): LightAppPermGroup {
return revokeRuntimePermissions(
app,
@@ -1056,7 +1057,7 @@ object KotlinUtils {
userFixed,
oneTime,
forceRemoveRevokedCompat,
- filterPermissions
+ filterPermissions,
)
}
@@ -1079,7 +1080,7 @@ object KotlinUtils {
userFixed: Boolean = false,
oneTime: Boolean = false,
forceRemoveRevokedCompat: Boolean = false,
- filterPermissions: Collection<String> = group.permissions.keys
+ filterPermissions: Collection<String> = group.permissions.keys,
): LightAppPermGroup {
return revokeRuntimePermissions(
app,
@@ -1088,7 +1089,7 @@ object KotlinUtils {
userFixed,
oneTime,
forceRemoveRevokedCompat,
- filterPermissions
+ filterPermissions,
)
}
@@ -1100,7 +1101,7 @@ object KotlinUtils {
userFixed: Boolean,
oneTime: Boolean,
forceRemoveRevokedCompat: Boolean = false,
- filterPermissions: Collection<String>
+ filterPermissions: Collection<String>,
): LightAppPermGroup {
val deviceId = group.deviceId
val wasOneTime = group.isOneTime
@@ -1117,7 +1118,7 @@ object KotlinUtils {
userFixed,
oneTime,
forceRemoveRevokedCompat,
- group
+ group,
)
newPerms[newPerm.name] = newPerm
shouldKillForAnyPermission = shouldKillForAnyPermission || shouldKill
@@ -1127,7 +1128,7 @@ object KotlinUtils {
if (shouldKillForAnyPermission && !shouldSkipKillForGroup(app, group)) {
(app.getSystemService(ActivityManager::class.java) as ActivityManager).killUid(
group.packageInfo.uid,
- KILL_REASON_APP_OP_CHANGE
+ KILL_REASON_APP_OP_CHANGE,
)
}
@@ -1137,7 +1138,7 @@ object KotlinUtils {
group.permGroupInfo,
newPerms,
group.hasInstallToRuntimeSplit,
- group.specialLocationGrant
+ group.specialLocationGrant,
)
if (wasOneTime && !anyPermsOfPackageOneTimeGranted(app, newGroup.packageInfo, newGroup)) {
@@ -1165,7 +1166,7 @@ object KotlinUtils {
packageName: String,
permissionGroupName: String,
user: UserHandle,
- postRevokeHandler: Runnable?
+ postRevokeHandler: Runnable?,
) {
GlobalScope.launch(Dispatchers.Main) {
val group =
@@ -1192,7 +1193,7 @@ object KotlinUtils {
private fun anyPermsOfPackageOneTimeGranted(
app: Application,
packageInfo: LightPackageInfo,
- group: LightAppPermGroup? = null
+ group: LightAppPermGroup? = null,
): Boolean {
val user = group?.userHandle ?: UserHandle.getUserHandleForUid(packageInfo.uid)
if (group?.isOneTime == true) {
@@ -1233,7 +1234,7 @@ object KotlinUtils {
userFixed: Boolean,
oneTime: Boolean,
forceRemoveRevokedCompat: Boolean,
- group: LightAppPermGroup
+ group: LightAppPermGroup,
): Pair<LightPermission, Boolean> {
// Do not touch permissions fixed by the system.
if (perm.isSystemFixed) {
@@ -1259,14 +1260,14 @@ object KotlinUtils {
!isPermissionSplitFromNonRuntime(
app,
perm.name,
- group.packageInfo.targetSdkVersion
+ group.packageInfo.targetSdkVersion,
)
) {
// Revoke the permission if needed.
context.packageManager.revokeRuntimePermission(
group.packageInfo.packageName,
perm.name,
- user
+ user,
)
isGranted = false
if (forceRemoveRevokedCompat) {
@@ -1311,7 +1312,7 @@ object KotlinUtils {
group.packageInfo.packageName,
PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
newFlags,
- user
+ user,
)
}
@@ -1332,7 +1333,7 @@ object KotlinUtils {
.cancelBackgroundAccessWarningNotification(
group.packageInfo.packageName,
user,
- true
+ true,
)
}
}
@@ -1372,7 +1373,7 @@ object KotlinUtils {
private fun allowAppOp(
app: Application,
perm: LightPermission,
- group: LightAppPermGroup
+ group: LightAppPermGroup,
): Boolean {
val packageName = group.packageInfo.packageName
val uid = group.packageInfo.uid
@@ -1434,7 +1435,7 @@ object KotlinUtils {
private fun disallowAppOp(
app: Application,
perm: LightPermission,
- group: LightAppPermGroup
+ group: LightAppPermGroup,
): Boolean {
val packageName = group.packageInfo.packageName
val uid = group.packageInfo.uid
@@ -1473,7 +1474,7 @@ object KotlinUtils {
uid: Int,
packageName: String,
mode: Int,
- manager: AppOpsManager
+ manager: AppOpsManager,
): Boolean {
val currentMode = manager.unsafeCheckOpRaw(op, uid, packageName)
if (currentMode == mode) {
@@ -1492,7 +1493,7 @@ object KotlinUtils {
app,
POST_NOTIFICATIONS,
group.packageName,
- group.userHandle
+ group.userHandle,
)
}
@@ -1512,7 +1513,7 @@ object KotlinUtils {
app: Application,
permission: String,
packageName: String,
- user: UserHandle
+ user: UserHandle,
): Boolean {
val userContext: Context = Utils.getUserContext(app, user)
if (
@@ -1556,7 +1557,7 @@ object KotlinUtils {
var resolveInfos =
context.packageManager.queryIntentActivities(
intentToResolve,
- MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE
+ MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE,
)
if (resolveInfos.size <= 0) {
@@ -1566,7 +1567,7 @@ object KotlinUtils {
resolveInfos =
context.packageManager.queryIntentActivities(
intentToResolve,
- MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE
+ MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE,
)
}
return resolveInfos.size > 0
@@ -1582,14 +1583,14 @@ object KotlinUtils {
fun setFlagsWhenLocationAccuracyChanged(
app: Application,
group: LightAppPermGroup,
- isFineSelected: Boolean
+ isFineSelected: Boolean,
) {
if (isFineSelected) {
setGroupFlags(
app,
group,
PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY to true,
- filterPermissions = listOf(ACCESS_FINE_LOCATION)
+ filterPermissions = listOf(ACCESS_FINE_LOCATION),
)
val fineIsOneTime =
group.permissions[Manifest.permission.ACCESS_FINE_LOCATION]?.isOneTime ?: false
@@ -1598,20 +1599,20 @@ object KotlinUtils {
group,
PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY to false,
PackageManager.FLAG_PERMISSION_ONE_TIME to fineIsOneTime,
- filterPermissions = listOf(Manifest.permission.ACCESS_COARSE_LOCATION)
+ filterPermissions = listOf(Manifest.permission.ACCESS_COARSE_LOCATION),
)
} else {
setGroupFlags(
app,
group,
PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY to false,
- filterPermissions = listOf(ACCESS_FINE_LOCATION)
+ filterPermissions = listOf(ACCESS_FINE_LOCATION),
)
setGroupFlags(
app,
group,
PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY to true,
- filterPermissions = listOf(Manifest.permission.ACCESS_COARSE_LOCATION)
+ filterPermissions = listOf(Manifest.permission.ACCESS_COARSE_LOCATION),
)
}
}
@@ -1629,7 +1630,7 @@ object KotlinUtils {
DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
SAFETY_PROTECTION_RESOURCES_ENABLED,
- false
+ false,
) &&
context
.getResources()
@@ -1658,7 +1659,7 @@ object KotlinUtils {
fun getAppStoreIntent(
context: Context,
installerPackageName: String?,
- packageName: String?
+ packageName: String?,
): Intent? {
val intent: Intent = Intent(Intent.ACTION_SHOW_APP_INFO).setPackage(installerPackageName)
val result: Intent? = resolveActivityForIntent(context, intent)
@@ -1720,7 +1721,7 @@ object KotlinUtils {
/** Get the [value][LiveData.getValue], suspending until [isInitialized] if not yet so */
suspend fun <T, LD : LiveData<T>> LD.getInitializedValue(
observe: LD.(Observer<T?>) -> Unit = { observeForever(it) },
- isValueInitialized: LD.() -> Boolean = { value != null }
+ isValueInitialized: LD.() -> Boolean = { value != null },
): T? {
return if (isValueInitialized()) {
value
@@ -1754,7 +1755,7 @@ suspend fun <T, LD : LiveData<T>> LD.getInitializedValue(
suspend inline fun <T, R> Iterable<T>.mapInParallel(
context: CoroutineContext,
scope: CoroutineScope = GlobalScope,
- crossinline transform: suspend CoroutineScope.(T) -> R
+ crossinline transform: suspend CoroutineScope.(T) -> R,
): List<R> = map { scope.async(context) { transform(it) } }.map { it.await() }
/**
@@ -1765,7 +1766,7 @@ suspend inline fun <T, R> Iterable<T>.mapInParallel(
suspend inline fun <T> Iterable<T>.forEachInParallel(
context: CoroutineContext,
scope: CoroutineScope = GlobalScope,
- crossinline action: suspend CoroutineScope.(T) -> Unit
+ crossinline action: suspend CoroutineScope.(T) -> Unit,
) {
mapInParallel(context, scope) { action(it) }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
index 0d1d960ab..a3446f802 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
@@ -139,7 +139,6 @@ object PermissionMapping {
PLATFORM_PERMISSIONS[Manifest.permission.NEARBY_WIFI_DEVICES] =
Manifest.permission_group.NEARBY_DEVICES
}
-
// Ranging permission will be supported from Android B+, update this when isAtLeastB()
// is available.
if (SdkLevel.isAtLeastV() && Flags.rangingPermissionEnabled()) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java
index e5de63f32..3d3b47272 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java
@@ -126,6 +126,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Set;
+import java.util.function.Supplier;
public final class Utils {
@@ -1566,18 +1567,40 @@ public final class Utils {
public static String getEnterpriseString(@NonNull Context context,
@NonNull String updatableStringId, int defaultStringId, @NonNull Object... formatArgs) {
return SdkLevel.isAtLeastT()
- ? getUpdatableEnterpriseString(
- context, updatableStringId, defaultStringId, formatArgs)
+ ? getUpdatableEnterpriseString(context, updatableStringId,
+ () -> context.getString(defaultStringId, formatArgs), formatArgs)
: context.getString(defaultStringId, formatArgs);
}
+ /**
+ * Selects the appropriate enterprise string for the provided resource ID and a fallback string
+ */
+ @NonNull
+ public static String getEnterpriseString(@NonNull Context context,
+ @NonNull String updatableStringId, @NonNull String defaultString) {
+ return SdkLevel.isAtLeastT()
+ ? getUpdatableEnterpriseString(context, updatableStringId, () -> defaultString)
+ : defaultString;
+ }
+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@NonNull
private static String getUpdatableEnterpriseString(@NonNull Context context,
- @NonNull String updatableStringId, int defaultStringId, @NonNull Object... formatArgs) {
+ @NonNull String updatableStringId, @NonNull Supplier<String> defaultStringLoader,
+ @NonNull Object... formatArgs) {
DevicePolicyManager dpm = getSystemServiceSafe(context, DevicePolicyManager.class);
- return dpm.getResources().getString(updatableStringId, () -> context.getString(
- defaultStringId, formatArgs), formatArgs);
+ return dpm.getResources().getString(updatableStringId, defaultStringLoader, formatArgs);
+ }
+
+ /**
+ * Returns the profile label from the {@link UserManager} for the provided profile
+ */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @NonNull
+ public static String getProfileLabel(@NonNull UserHandle profile, @NonNull Context context) {
+ Context profileContext = context.createContextAsUser(profile, 0);
+ UserManager profileUserManager = profileContext.getSystemService(UserManager.class);
+ return profileUserManager.getProfileLabel();
}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt
index c633c013a..1610901bc 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt
@@ -48,7 +48,6 @@ import androidx.annotation.GuardedBy
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
-import androidx.core.util.Preconditions
import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.PermissionControllerStatsLog
@@ -712,7 +711,7 @@ class AccessibilityPackageResetHandler : BroadcastReceiver() {
return
}
- val data = Preconditions.checkNotNull(intent.data)
+ val data = requireNotNull(intent.data)
val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
coroutineScope.launch(Dispatchers.Default) {
if (DEBUG) {
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt
index 43b3edc04..58a6f1bc4 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt
@@ -57,7 +57,6 @@ import androidx.annotation.GuardedBy
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
-import androidx.core.util.Preconditions
import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.Constants.KEY_LAST_NOTIFICATION_LISTENER_NOTIFICATION_SHOWN
@@ -1146,7 +1145,7 @@ class NotificationListenerPackageResetHandler : BroadcastReceiver() {
return
}
- val data = Preconditions.checkNotNull(intent.data)
+ val data = requireNotNull(intent.data)
val pkg: String = data.schemeSpecificPart
if (DEBUG) Log.i(TAG, "Reset $pkg")
diff --git a/PermissionController/src/com/android/permissioncontroller/role/Role.md b/PermissionController/src/com/android/permissioncontroller/role/Role.md
index d4a514784..255214495 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/Role.md
+++ b/PermissionController/src/com/android/permissioncontroller/role/Role.md
@@ -62,6 +62,13 @@ is optional and defaults to `false`.
the Java method on the `Flags` class which will be invoked via reflection. Note that any new
aconfig library dependency will need corresponding jarjar rules for PermissionController and the
system service JAR.
+- `ignoreDisabledSystemPackageWhenGranting`: Whether the role should ignore the requested
+permissions of the disabled system package (if any) when granting permissions. If `false`, the
+permission will need to be requested by the disabled system package as well, if there is one. This
+attribute is optional and defaults to the opposite of `systemOnly` on Android S+, or `true` below
+Android S. **Note:** Extra care should be taken when adding a runtime permission to a role with
+this attribute explicitly set to `true`, because that may allow apps to update and silently obtain
+a new runtime permission.
- `label`: The string resource for the label of the role, e.g. `@string/role_sms_label`, which says
"Default SMS app". For default apps, this string will appear in the default app detail page as the
title. This attribute is required if the role is `visible`.
diff --git a/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING
index 46b148e68..93ad3d31b 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING
@@ -46,6 +46,9 @@
"postsubmit": [
{
"name": "CtsRoleTestCases"
+ },
+ {
+ "name": "CtsRoleMultiUserTestCases"
}
],
"mainline-postsubmit": [
@@ -60,6 +63,9 @@
"exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
}
]
+ },
+ {
+ "name": "CtsRoleMultiUserTestCases[com.google.android.permission.apex]"
}
]
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/UserPackage.java b/PermissionController/src/com/android/permissioncontroller/role/UserPackage.java
new file mode 100644
index 000000000..29f6904ea
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/UserPackage.java
@@ -0,0 +1,104 @@
+/*
+ * 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.role;
+
+import android.content.pm.ApplicationInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.core.os.ParcelCompat;
+
+import java.util.Objects;
+
+/**
+ * Data class storing a {@link UserHandle} and a package name.
+ */
+public class UserPackage implements Parcelable {
+ @NonNull
+ public final UserHandle user;
+ @NonNull
+ public final String packageName;
+
+ private UserPackage(@NonNull UserHandle user, @NonNull String packageName) {
+ this.user = user;
+ this.packageName = packageName;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof UserPackage) {
+ UserPackage other = (UserPackage) obj;
+ return Objects.equals(user, other.user)
+ && Objects.equals(packageName, other.packageName);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(user, packageName);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeParcelable(user, 0);
+ out.writeString(packageName);
+ }
+
+ public static final Parcelable.Creator<UserPackage> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public UserPackage createFromParcel(Parcel in) {
+ UserHandle user =
+ ParcelCompat.readParcelable(
+ in, UserHandle.class.getClassLoader(), UserHandle.class);
+ String packageName = in.readString();
+ if (user == null || packageName == null) {
+ return null;
+ }
+ return UserPackage.of(user, packageName);
+ }
+
+ @Override
+ public UserPackage[] newArray(int size) {
+ return new UserPackage[size];
+ }
+ };
+
+ /** Returns a {@link UserPackage} represented by the specified user and package name */
+ @NonNull
+ public static UserPackage of(@NonNull UserHandle user, @NonNull String packageName) {
+ return new UserPackage(user, packageName);
+ }
+
+ /** Returns a {@link UserPackage} using user and package name from an {@link ApplicationInfo} */
+ @NonNull
+ public static UserPackage from(@NonNull ApplicationInfo applicationInfo) {
+ return new UserPackage(
+ UserHandle.getUserHandleForUid(applicationInfo.uid), applicationInfo.packageName);
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
index 2f515f02c..61e33d10b 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
@@ -69,6 +69,10 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
+ ".preference.DESCRIPTION";
private static final String PREFERENCE_KEY_OTHER_NFC_SERVICES =
DefaultAppChildFragment.class.getName() + ".preference.OTHER_NFC_SERVICES";
+ private static final String PREFERENCE_EXTRA_PACKAGE_NAME =
+ DefaultAppChildFragment.class.getName() + ".extra.PACKAGE_NAME";
+ private static final String PREFERENCE_EXTRA_USER = DefaultAppChildFragment.class.getName()
+ + ".extra.USER";
@NonNull
private String mRoleName;
@@ -133,7 +137,6 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
Context context = preferenceManager.getContext();
PreferenceScreen preferenceScreen = preferenceFragment.getPreferenceScreen();
- Preference oldDescriptionPreference = null;
ArrayMap<String, Preference> oldPreferences = new ArrayMap<>();
if (preferenceScreen == null) {
preferenceScreen = preferenceManager.createPreferenceScreen(context);
@@ -162,7 +165,9 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
ApplicationInfo qualifyingApplicationInfo = qualifyingApplication.first;
boolean isHolderApplication = qualifyingApplication.second;
- String key = qualifyingApplicationInfo.packageName;
+ int userId =
+ UserHandle.getUserHandleForUid(qualifyingApplicationInfo.uid).getIdentifier();
+ String key = qualifyingApplicationInfo.packageName + "@" + userId;
Drawable icon = Utils.getBadgedIcon(context, qualifyingApplicationInfo);
String title = Utils.getFullAppLabel(qualifyingApplicationInfo, context);
addPreference(key, icon, title, isHolderApplication, qualifyingApplicationInfo,
@@ -205,16 +210,27 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
preference.setPersistent(false);
preference.setOnPreferenceChangeListener((preference2, newValue) -> false);
preference.setOnPreferenceClickListener(this);
+ // In the cases we need this (see #onPreferenceClick()), this should never be null.
+ // This method (addPreference) is used for both legitimate apps and the `NONE` item,
+ // the `NONE` item passes a null applicationinfo object. NFC uses a different preference
+ // method for adding, and a different onclick method
+ if (applicationInfo != null) {
+ Bundle extras = preference.getExtras();
+ extras.putString(PREFERENCE_EXTRA_PACKAGE_NAME, applicationInfo.packageName);
+ extras.putParcelable(PREFERENCE_EXTRA_USER,
+ UserHandle.getUserHandleForUid(applicationInfo.uid));
+ }
} else {
preference = roleApplicationPreference.asTwoStatePreference();
}
preference.setChecked(checked);
if (applicationInfo != null) {
+ UserHandle user = UserHandle.getUserHandleForUid(applicationInfo.uid);
roleApplicationPreference.setRestrictionIntent(
- mRole.getApplicationRestrictionIntentAsUser(applicationInfo, mUser, context));
+ mRole.getApplicationRestrictionIntentAsUser(applicationInfo, user, context));
RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, roleApplicationPreference,
- applicationInfo, mUser, context);
+ applicationInfo, user, context);
}
preferenceScreen.addPreference(preference);
@@ -243,22 +259,26 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
if (Objects.equals(key, PREFERENCE_KEY_NONE)) {
mViewModel.setNoneDefaultApp();
} else {
- String packageName = key;
+ String packageName =
+ preference.getExtras().getString(PREFERENCE_EXTRA_PACKAGE_NAME);
+ UserHandle user =
+ preference.getExtras().getParcelable(PREFERENCE_EXTRA_USER);
CharSequence confirmationMessage =
RoleUiBehaviorUtils.getConfirmationMessage(mRole, packageName,
requireContext());
if (confirmationMessage != null) {
- DefaultAppConfirmationDialogFragment.show(packageName, confirmationMessage, this);
+ DefaultAppConfirmationDialogFragment.show(packageName, user, confirmationMessage,
+ this);
} else {
- setDefaultApp(packageName);
+ setDefaultApp(packageName, user);
}
}
return true;
}
@Override
- public void setDefaultApp(@NonNull String packageName) {
- mViewModel.setDefaultApp(packageName);
+ public void setDefaultApp(@NonNull String packageName, @NonNull UserHandle user) {
+ mViewModel.setDefaultApp(packageName, user);
}
private void addNonPaymentNfcServicesPreference(@NonNull PreferenceScreen preferenceScreen,
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppConfirmationDialogFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppConfirmationDialogFragment.java
index 843854bf4..5f399a0b8 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppConfirmationDialogFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppConfirmationDialogFragment.java
@@ -20,9 +20,11 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.os.BundleCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
@@ -32,24 +34,27 @@ import androidx.fragment.app.Fragment;
public class DefaultAppConfirmationDialogFragment extends DialogFragment {
private String mPackageName;
+ private UserHandle mUser;
private CharSequence mMessage;
/**
* Create a new instance of this fragment.
*
* @param packageName the package name of the application
+ * @param user the user the specified package is running in
* @param message the confirmation message
*
* @return a new instance of this fragment
*
- * @see #show(String, CharSequence, Fragment)
+ * @see #show(String, UserHandle, CharSequence, Fragment)
*/
@NonNull
public static DefaultAppConfirmationDialogFragment newInstance(@NonNull String packageName,
- @NonNull CharSequence message) {
+ @NonNull UserHandle user, @NonNull CharSequence message) {
DefaultAppConfirmationDialogFragment fragment = new DefaultAppConfirmationDialogFragment();
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ arguments.putParcelable(Intent.EXTRA_USER, user);
arguments.putCharSequence(Intent.EXTRA_TEXT, message);
fragment.setArguments(arguments);
return fragment;
@@ -59,14 +64,15 @@ public class DefaultAppConfirmationDialogFragment extends DialogFragment {
* Show a new instance of this fragment.
*
* @param packageName the package name of the application
+ * @param user the user the specified package is running in
* @param message the confirmation message
* @param fragment the parent fragment
*
- * @see #newInstance(String, CharSequence)
+ * @see #newInstance(String, UserHandle, CharSequence)
*/
- public static void show(@NonNull String packageName, @NonNull CharSequence message,
- @NonNull Fragment fragment) {
- newInstance(packageName, message).show(fragment.getChildFragmentManager(), null);
+ public static void show(@NonNull String packageName, @NonNull UserHandle user,
+ @NonNull CharSequence message, @NonNull Fragment fragment) {
+ newInstance(packageName, user, message).show(fragment.getChildFragmentManager(), null);
}
@Override
@@ -75,6 +81,7 @@ public class DefaultAppConfirmationDialogFragment extends DialogFragment {
Bundle arguments = getArguments();
mPackageName = arguments.getString(Intent.EXTRA_PACKAGE_NAME);
+ mUser = BundleCompat.getParcelable(arguments, Intent.EXTRA_USER, UserHandle.class);
mMessage = arguments.getCharSequence(Intent.EXTRA_TEXT);
}
@@ -90,7 +97,7 @@ public class DefaultAppConfirmationDialogFragment extends DialogFragment {
private void onOk() {
Listener listener = (Listener) getParentFragment();
- listener.setDefaultApp(mPackageName);
+ listener.setDefaultApp(mPackageName, mUser);
}
/**
@@ -103,6 +110,6 @@ public class DefaultAppConfirmationDialogFragment extends DialogFragment {
*
* @param packageName the package name of the application
*/
- void setDefaultApp(@NonNull String packageName);
+ void setDefaultApp(@NonNull String packageName, @NonNull UserHandle user);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
index 0b96eb8ba..48472bc5e 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.UserHandle;
+import android.permission.flags.Flags;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -36,6 +37,7 @@ import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
+import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.utils.Utils;
import com.android.permissioncontroller.role.utils.PackageUtils;
@@ -145,15 +147,25 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
addMoreDefaultAppsPreference(preferenceScreen, oldPreferences, context);
addManageDomainUrlsPreference(preferenceScreen, oldPreferences, context);
if (hasWorkProfile && !workRoleItems.isEmpty()) {
+ String defaultWorkTitle;
+ if (SdkLevel.isAtLeastV() && Flags.useProfileLabelsForDefaultAppSectionTitles()) {
+ defaultWorkTitle = Utils.getProfileLabel(mViewModel.getWorkProfile(), context);
+ } else {
+ defaultWorkTitle = context.getString(R.string.default_apps_for_work);
+ }
String workTitle = Utils.getEnterpriseString(context,
- DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE,
- R.string.default_apps_for_work);
+ DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE, defaultWorkTitle);
addPreferenceCategory(oldWorkPreferenceCategory, PREFERENCE_KEY_WORK_CATEGORY,
workTitle, preferenceScreen, workRoleItems, oldWorkPreferences, this,
mViewModel.getWorkProfile(), context);
}
if (hasPrivateProfile && !privateRoleItems.isEmpty()) {
- String privateTitle = context.getString(R.string.default_apps_for_private_profile);
+ String privateTitle;
+ if (SdkLevel.isAtLeastV() && Flags.useProfileLabelsForDefaultAppSectionTitles()) {
+ privateTitle = Utils.getProfileLabel(mViewModel.getPrivateProfile(), context);
+ } else {
+ privateTitle = context.getString(R.string.default_apps_for_private_profile);
+ }
addPreferenceCategory(oldPrivatePreferenceCategory, PREFERENCE_KEY_PRIVATE_CATEGORY,
privateTitle, preferenceScreen, privateRoleItems, oldPrivatePreferences, this,
mViewModel.getPrivateProfile(), context);
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java
index 5bc25df54..82253ed00 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java
@@ -30,8 +30,11 @@ import androidx.lifecycle.ViewModel;
import com.android.permissioncontroller.permission.utils.Utils;
import com.android.permissioncontroller.role.utils.UserUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.util.RoleFlags;
import java.util.List;
+import java.util.function.Predicate;
/**
* {@link ViewModel} for the list of default apps.
@@ -55,12 +58,34 @@ public class DefaultAppListViewModel extends AndroidViewModel {
super(application);
mUser = Process.myUserHandle();
+ RoleListLiveData liveData = new RoleListLiveData(true, mUser, application);
RoleListSortFunction sortFunction = new RoleListSortFunction(application);
- mLiveData = Transformations.map(new RoleListLiveData(true, mUser, application),
- sortFunction);
mWorkProfile = UserUtils.getWorkProfile(application);
- mWorkLiveData = mWorkProfile != null ? Transformations.map(new RoleListLiveData(true,
- mWorkProfile, application), sortFunction) : null;
+ if (RoleFlags.isProfileGroupExclusivityAvailable()) {
+ if (mWorkProfile != null) {
+ // Show profile group exclusive roles from work profile in primary group.
+ RoleListLiveData workLiveData =
+ new RoleListLiveData(true, mWorkProfile, application);
+ Predicate<RoleItem> exclusivityPredicate = roleItem ->
+ roleItem.getRole().getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP;
+ mLiveData = Transformations.map(
+ new MergeRoleListLiveData(liveData,
+ Transformations.map(workLiveData,
+ new RoleListFilterFunction(exclusivityPredicate))),
+ sortFunction);
+ mWorkLiveData = Transformations.map(
+ Transformations.map(workLiveData,
+ new RoleListFilterFunction(exclusivityPredicate.negate())),
+ sortFunction);
+ } else {
+ mLiveData = Transformations.map(liveData, sortFunction);
+ mWorkLiveData = null;
+ }
+ } else {
+ mLiveData = Transformations.map(liveData, sortFunction);
+ mWorkLiveData = mWorkProfile != null ? Transformations.map(
+ new RoleListLiveData(true, mWorkProfile, application), sortFunction) : null;
+ }
UserHandle privateProfile = UserUtils.getPrivateProfile(application);
if (privateProfile != null && Utils.shouldShowInSettings(
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java
index c89e1f71e..cdee94b13 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java
@@ -30,6 +30,7 @@ import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
+import com.android.permissioncontroller.role.utils.UserUtils;
import com.android.role.controller.model.Role;
import java.util.List;
@@ -58,10 +59,26 @@ public class DefaultAppViewModel extends AndroidViewModel {
super(application);
mRole = role;
- mUser = user;
-
- mRoleLiveData = Transformations.map(new RoleLiveData(mRole, mUser, application),
- new RoleSortFunction(application));
+ // If EXCLUSIVITY_PROFILE_GROUP this user should be profile parent
+ mUser = mRole.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP
+ ? UserUtils.getProfileParentOrSelf(user, application)
+ : user;
+ RoleLiveData liveData = new RoleLiveData(mRole, mUser, application);
+ RoleSortFunction sortFunction = new RoleSortFunction(application);
+ if (mRole.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
+ // Context user might be work profile, ensure we get a non-null UserHandle if work
+ // profile exists. getWorkProfile returns null if context user is work profile.
+ UserHandle workProfile = UserUtils.getWorkProfileOrSelf(application);
+ if (workProfile != null) {
+ RoleLiveData workLiveData = new RoleLiveData(role, workProfile, application);
+ mRoleLiveData = Transformations.map(new MergeRoleLiveData(liveData, workLiveData),
+ sortFunction);
+ } else {
+ mRoleLiveData = Transformations.map(liveData, sortFunction);
+ }
+ } else {
+ mRoleLiveData = Transformations.map(liveData, sortFunction);
+ }
}
@NonNull
@@ -79,13 +96,13 @@ public class DefaultAppViewModel extends AndroidViewModel {
*
* @param packageName the package name of the application
*/
- public void setDefaultApp(@NonNull String packageName) {
+ public void setDefaultApp(@NonNull String packageName, @NonNull UserHandle user) {
if (mManageRoleHolderStateLiveData.getValue() != ManageRoleHolderStateLiveData.STATE_IDLE) {
Log.i(LOG_TAG, "Trying to set default app while another request is on-going");
return;
}
mManageRoleHolderStateLiveData.setRoleHolderAsUser(mRole.getName(), packageName, true, 0,
- mUser, getApplication());
+ user, getApplication());
}
/**
@@ -93,12 +110,15 @@ public class DefaultAppViewModel extends AndroidViewModel {
*/
public void setNoneDefaultApp() {
Context context = getApplication();
- mRole.onNoneHolderSelectedAsUser(mUser, context);
+ UserHandle user = mRole.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP
+ ? UserUtils.getProfileParentOrSelf(mUser, context)
+ : mUser;
+ mRole.onNoneHolderSelectedAsUser(user, context);
if (mManageRoleHolderStateLiveData.getValue() != ManageRoleHolderStateLiveData.STATE_IDLE) {
Log.i(LOG_TAG, "Trying to set default app while another request is on-going");
return;
}
- mManageRoleHolderStateLiveData.clearRoleHoldersAsUser(mRole.getName(), 0, mUser, context);
+ mManageRoleHolderStateLiveData.clearRoleHoldersAsUser(mRole.getName(), 0, user, context);
}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/MergeRoleListLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleListLiveData.java
index 0318800f4..0c2d96b2c 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/MergeRoleListLiveData.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleListLiveData.java
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.role.ui.specialappaccess;
+package com.android.permissioncontroller.role.ui;
import android.util.ArrayMap;
import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
-import com.android.permissioncontroller.role.ui.RoleItem;
-import com.android.permissioncontroller.role.ui.RoleListLiveData;
-
import java.util.ArrayList;
import java.util.List;
@@ -33,14 +31,14 @@ import java.util.List;
public class MergeRoleListLiveData extends MediatorLiveData<List<RoleItem>> {
@NonNull
- private final RoleListLiveData[] mLiveDatas;
+ private final LiveData<List<RoleItem>>[] mLiveDatas;
- public MergeRoleListLiveData(@NonNull RoleListLiveData... liveDatas) {
+ public MergeRoleListLiveData(@NonNull LiveData<List<RoleItem>>... liveDatas) {
mLiveDatas = liveDatas;
int liveDatasLength = mLiveDatas.length;
for (int i = 0; i < liveDatasLength; i++) {
- RoleListLiveData liveData = mLiveDatas[i];
+ LiveData<List<RoleItem>> liveData = mLiveDatas[i];
addSource(liveData, roleItems -> onRoleListChanged());
}
@@ -50,7 +48,7 @@ public class MergeRoleListLiveData extends MediatorLiveData<List<RoleItem>> {
ArrayMap<String, RoleItem> mergedRoleItemMap = new ArrayMap<>();
int liveDatasLength = mLiveDatas.length;
for (int liveDatasIndex = 0; liveDatasIndex < liveDatasLength; liveDatasIndex++) {
- RoleListLiveData liveData = mLiveDatas[liveDatasIndex];
+ LiveData<List<RoleItem>> liveData = mLiveDatas[liveDatasIndex];
List<RoleItem> roleItems = liveData.getValue();
if (roleItems == null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/MergeRoleLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleLiveData.java
index dab59a1ab..31a02729a 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/MergeRoleLiveData.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleLiveData.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.role.ui.specialappaccess;
+package com.android.permissioncontroller.role.ui;
import android.content.pm.ApplicationInfo;
import android.util.Pair;
@@ -22,8 +22,6 @@ import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.lifecycle.MediatorLiveData;
-import com.android.permissioncontroller.role.ui.RoleLiveData;
-
import java.util.ArrayList;
import java.util.List;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
index 97ed74c01..234554193 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
@@ -44,6 +44,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
+import androidx.core.os.BundleCompat;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.ViewModelProvider;
@@ -51,10 +52,12 @@ import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor;
import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.permissioncontroller.role.UserPackage;
import com.android.permissioncontroller.role.model.UserDeniedManager;
import com.android.permissioncontroller.role.utils.PackageUtils;
import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.permissioncontroller.role.utils.UiUtils;
+import com.android.permissioncontroller.role.utils.UserUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -293,29 +296,31 @@ public class RequestRoleFragment extends DialogFragment {
}
private void setRoleHolder() {
- String packageName = mAdapter.getCheckedPackageName();
+ UserPackage userPackage = mAdapter.getCheckedUserPackage();
Context context = requireContext();
- UserHandle user = Process.myUserHandle();
- if (packageName == null) {
+ if (userPackage == null) {
reportRequestResult(PermissionControllerStatsLog
.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_GRANTED_ANOTHER,
null);
+ UserHandle user = mRole.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP
+ ? UserUtils.getProfileParentOrSelf(Process.myUserHandle(), context)
+ : Process.myUserHandle();
mRole.onNoneHolderSelectedAsUser(user, context);
mViewModel.getManageRoleHolderStateLiveData().clearRoleHoldersAsUser(mRoleName, 0, user,
context);
} else {
- boolean isRequestingApplication = Objects.equals(packageName, mPackageName);
+ boolean isRequestingApplication = isRequestingApplication(userPackage);
if (isRequestingApplication) {
reportRequestResult(PermissionControllerStatsLog
.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED, null);
} else {
reportRequestResult(PermissionControllerStatsLog
.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_GRANTED_ANOTHER,
- packageName);
+ userPackage);
}
int flags = isRequestingApplication ? RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP : 0;
mViewModel.getManageRoleHolderStateLiveData().setRoleHolderAsUser(mRoleName,
- packageName, true, flags, user, context);
+ userPackage.packageName, true, flags, userPackage.user, context);
}
}
@@ -329,11 +334,13 @@ public class RequestRoleFragment extends DialogFragment {
ManageRoleHolderStateLiveData liveData =
mViewModel.getManageRoleHolderStateLiveData();
String packageName = liveData.getLastPackageName();
- if (packageName != null) {
- mRole.onHolderSelectedAsUser(packageName, liveData.getLastUser(),
- requireContext());
+ UserHandle user = liveData.getLastUser();
+ UserPackage userPackage = packageName != null && user != null
+ ? UserPackage.of(user, packageName) : null;
+ if (userPackage != null) {
+ mRole.onHolderSelectedAsUser(packageName, user, requireContext());
}
- if (Objects.equals(packageName, mPackageName)) {
+ if (isRequestingApplication(userPackage)) {
Log.i(LOG_TAG, "Application added as a role holder, role: " + mRoleName
+ ", package: " + mPackageName);
clearDeniedSetResultOkAndFinish();
@@ -383,20 +390,23 @@ public class RequestRoleFragment extends DialogFragment {
requireActivity().finish();
}
- private void reportRequestResult(int result, @Nullable String grantedAnotherPackageName) {
- String holderPackageName = getHolderPackageName();
- reportRequestResult(getApplicationUid(mPackageName), mPackageName, mRoleName,
- getQualifyingApplicationCount(), getQualifyingApplicationUid(holderPackageName),
- holderPackageName, getQualifyingApplicationUid(grantedAnotherPackageName),
- grantedAnotherPackageName, result);
+ private void reportRequestResult(int result, @Nullable UserPackage grantedUserPackage) {
+ UserPackage holderUserPackage = getHolderUserPackage();
+ reportRequestResult(getRequestingApplicationUid(), mPackageName, mRoleName,
+ getQualifyingApplicationCount(),
+ getQualifyingApplicationUid(holderUserPackage),
+ holderUserPackage != null ? holderUserPackage.packageName : null,
+ getQualifyingApplicationUid(grantedUserPackage),
+ grantedUserPackage != null ? grantedUserPackage.packageName : null,
+ result);
}
- private int getApplicationUid(@NonNull String packageName) {
- int uid = getQualifyingApplicationUid(packageName);
+ private int getRequestingApplicationUid() {
+ int uid = getQualifyingApplicationUid(UserPackage.of(Process.myUserHandle(), mPackageName));
if (uid != -1) {
return uid;
}
- ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName,
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(mPackageName,
requireActivity());
if (applicationInfo == null) {
return -1;
@@ -404,8 +414,8 @@ public class RequestRoleFragment extends DialogFragment {
return applicationInfo.uid;
}
- private int getQualifyingApplicationUid(@Nullable String packageName) {
- if (packageName == null || mAdapter == null) {
+ private int getQualifyingApplicationUid(@Nullable UserPackage userPackage) {
+ if (userPackage == null || mAdapter == null) {
return -1;
}
int count = mAdapter.getCount();
@@ -416,7 +426,7 @@ public class RequestRoleFragment extends DialogFragment {
continue;
}
ApplicationInfo applicationInfo = qualifyingApplication.first;
- if (Objects.equals(applicationInfo.packageName, packageName)) {
+ if (Objects.equals(UserPackage.from(applicationInfo), userPackage)) {
return applicationInfo.uid;
}
}
@@ -436,11 +446,15 @@ public class RequestRoleFragment extends DialogFragment {
}
@Nullable
- private String getHolderPackageName() {
+ private UserPackage getHolderUserPackage() {
if (mAdapter == null) {
return null;
}
- return mAdapter.mHolderPackageName;
+ return mAdapter.mHolderUserPackage;
+ }
+
+ private boolean isRequestingApplication(@Nullable UserPackage userPackage) {
+ return Objects.equals(userPackage, UserPackage.of(Process.myUserHandle(), mPackageName));
}
static void reportRequestResult(int requestingUid, String requestingPackageName,
@@ -466,8 +480,8 @@ public class RequestRoleFragment extends DialogFragment {
private static final String STATE_USER_CHECKED = Adapter.class.getName()
+ ".state.USER_CHECKED";
- private static final String STATE_CHECKED_PACKAGE_NAME = Adapter.class.getName()
- + ".state.CHECKED_PACKAGE_NAME";
+ private static final String STATE_CHECKED_USER_PACKAGE = Adapter.class.getName()
+ + ".state.CHECKED_USER_PACKAGE";
private static final int LAYOUT_TRANSITION_DURATION_MILLIS = 150;
@@ -483,7 +497,7 @@ public class RequestRoleFragment extends DialogFragment {
new ArrayList<>();
@Nullable
- private String mHolderPackageName;
+ private UserPackage mHolderUserPackage;
private boolean mDontAskAgain;
@@ -492,7 +506,7 @@ public class RequestRoleFragment extends DialogFragment {
private boolean mUserChecked;
@Nullable
- private String mCheckedPackageName;
+ private UserPackage mCheckedUserPackage;
Adapter(@NonNull ListView listView, @NonNull Role role) {
mListView = listView;
@@ -502,14 +516,20 @@ public class RequestRoleFragment extends DialogFragment {
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(STATE_USER_CHECKED, mUserChecked);
if (mUserChecked) {
- outState.putString(STATE_CHECKED_PACKAGE_NAME, mCheckedPackageName);
+ if (mCheckedUserPackage != null) {
+ outState.putParcelable(STATE_CHECKED_USER_PACKAGE, mCheckedUserPackage);
+ } else {
+ outState.remove(STATE_CHECKED_USER_PACKAGE);
+ }
}
}
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
mUserChecked = savedInstanceState.getBoolean(STATE_USER_CHECKED);
if (mUserChecked) {
- mCheckedPackageName = savedInstanceState.getString(STATE_CHECKED_PACKAGE_NAME);
+ mCheckedUserPackage =
+ BundleCompat.getParcelable(savedInstanceState, STATE_CHECKED_USER_PACKAGE,
+ UserPackage.class);
}
}
@@ -520,7 +540,7 @@ public class RequestRoleFragment extends DialogFragment {
mDontAskAgain = dontAskAgain;
if (mDontAskAgain) {
mUserChecked = false;
- mCheckedPackageName = mHolderPackageName;
+ mCheckedUserPackage = mHolderUserPackage;
}
notifyDataSetChanged();
}
@@ -529,17 +549,18 @@ public class RequestRoleFragment extends DialogFragment {
Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position);
if (qualifyingApplication == null) {
mUserChecked = true;
- mCheckedPackageName = null;
+ mCheckedUserPackage = null;
} else {
ApplicationInfo applicationInfo = qualifyingApplication.first;
+ UserHandle user = UserHandle.getUserHandleForUid(applicationInfo.uid);
Intent restrictionIntent = mRole.getApplicationRestrictionIntentAsUser(
- applicationInfo, Process.myUserHandle(), mListView.getContext());
+ applicationInfo, user, mListView.getContext());
if (restrictionIntent != null) {
mListView.getContext().startActivity(restrictionIntent);
return;
} else {
mUserChecked = true;
- mCheckedPackageName = applicationInfo.packageName;
+ mCheckedUserPackage = UserPackage.from(applicationInfo);
}
}
notifyDataSetChanged();
@@ -551,9 +572,9 @@ public class RequestRoleFragment extends DialogFragment {
mQualifyingApplications.add(0, null);
}
mQualifyingApplications.addAll(qualifyingApplications);
- mHolderPackageName = getHolderPackageName(qualifyingApplications);
+ mHolderUserPackage = getHolderUserPackage(qualifyingApplications);
- if (mUserChecked && mCheckedPackageName != null) {
+ if (mUserChecked && mCheckedUserPackage != null) {
boolean isCheckedPackageNameFound = false;
int count = getCount();
for (int i = 0; i < count; i++) {
@@ -561,9 +582,10 @@ public class RequestRoleFragment extends DialogFragment {
if (qualifyingApplication == null) {
continue;
}
- String packageName = qualifyingApplication.first.packageName;
+ ApplicationInfo applicationInfo = qualifyingApplication.first;
+ UserPackage userPackage = UserPackage.from(applicationInfo);
- if (Objects.equals(packageName, mCheckedPackageName)) {
+ if (Objects.equals(userPackage, mCheckedUserPackage)) {
mUserChecked = true;
isCheckedPackageNameFound = true;
break;
@@ -571,19 +593,19 @@ public class RequestRoleFragment extends DialogFragment {
}
if (!isCheckedPackageNameFound) {
mUserChecked = false;
- mCheckedPackageName = null;
+ mCheckedUserPackage = null;
}
}
if (!mUserChecked) {
- mCheckedPackageName = mHolderPackageName;
+ mCheckedUserPackage = mHolderUserPackage;
}
notifyDataSetChanged();
}
@Nullable
- private static String getHolderPackageName(
+ private static UserPackage getHolderUserPackage(
@NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) {
int qualifyingApplicationSize = qualifyingApplications.size();
for (int i = 0; i < qualifyingApplicationSize; i++) {
@@ -596,19 +618,19 @@ public class RequestRoleFragment extends DialogFragment {
boolean isHolderApplication = qualifyingApplication.second;
if (isHolderApplication) {
- return applicationInfo.packageName;
+ return UserPackage.from(applicationInfo);
}
}
return null;
}
@Nullable
- public String getCheckedPackageName() {
- return mCheckedPackageName;
+ public UserPackage getCheckedUserPackage() {
+ return mCheckedUserPackage;
}
public boolean isHolderApplicationChecked() {
- return Objects.equals(mCheckedPackageName, mHolderPackageName);
+ return Objects.equals(mCheckedUserPackage, mHolderUserPackage);
}
@Override
@@ -650,7 +672,7 @@ public class RequestRoleFragment extends DialogFragment {
}
Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position);
if (qualifyingApplication == null) {
- return mHolderPackageName == null;
+ return mHolderUserPackage == null;
} else {
boolean isHolderApplication = qualifyingApplication.second;
return isHolderApplication;
@@ -685,16 +707,17 @@ public class RequestRoleFragment extends DialogFragment {
if (qualifyingApplication == null) {
applicationInfo = null;
restricted = false;
- checked = mCheckedPackageName == null;
+ checked = mCheckedUserPackage == null;
icon = AppCompatResources.getDrawable(context, R.drawable.ic_remove_circle);
title = context.getString(R.string.default_app_none);
- subtitle = mHolderPackageName != null ? context.getString(
+ subtitle = mHolderUserPackage != null ? context.getString(
R.string.request_role_current_default) : null;
} else {
applicationInfo = qualifyingApplication.first;
+ UserPackage userPackage = UserPackage.from(applicationInfo);
restricted = mRole.getApplicationRestrictionIntentAsUser(applicationInfo,
- Process.myUserHandle(), context) != null;
- checked = Objects.equals(applicationInfo.packageName, mCheckedPackageName);
+ userPackage.user, context) != null;
+ checked = Objects.equals(userPackage, mCheckedUserPackage);
icon = Utils.getBadgedIcon(context, applicationInfo);
title = Utils.getAppLabel(applicationInfo, context);
boolean isHolderApplication = qualifyingApplication.second;
@@ -712,8 +735,10 @@ public class RequestRoleFragment extends DialogFragment {
holder.subtitleText.setVisibility(!TextUtils.isEmpty(subtitle) ? View.VISIBLE
: View.GONE);
holder.subtitleText.setText(subtitle);
- RoleUiBehaviorUtils.prepareRequestRoleItemViewAsUser(mRole, holder, applicationInfo,
- Process.myUserHandle(), context);
+ if (applicationInfo != null) {
+ RoleUiBehaviorUtils.prepareRequestRoleItemViewAsUser(mRole, holder, applicationInfo,
+ UserHandle.getUserHandleForUid(applicationInfo.uid), context);
+ }
return view;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListFilterFunction.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListFilterFunction.java
new file mode 100644
index 000000000..b84fa80b9
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListFilterFunction.java
@@ -0,0 +1,53 @@
+/*
+ * 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.role.ui;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import kotlin.jvm.functions.Function1;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * A function for {@link androidx.lifecycle#map(androidx.lifecycle.LiveData, Function1)}
+ * that filters a live data for role list.
+ */
+public class RoleListFilterFunction implements Function1<List<RoleItem>, List<RoleItem>> {
+ private final Predicate<RoleItem> mPredicate;
+
+ public RoleListFilterFunction(@NonNull Predicate<RoleItem> predicate) {
+ mPredicate = predicate;
+ }
+
+ @NonNull
+ @Override
+ public List<RoleItem> invoke(@Nullable List<RoleItem> roleItems) {
+ List<RoleItem> filteredRoleItems = new ArrayList<>();
+ if (roleItems != null) {
+ int roleItemsSize = roleItems.size();
+ for (int i = 0; i < roleItemsSize; i++) {
+ RoleItem roleItem = roleItems.get(i);
+ if (mPredicate.test(roleItem)) {
+ filteredRoleItems.add(roleItem);
+ }
+ }
+ }
+ return filteredRoleItems;
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/ReservedForTestingProfileGroupExclusivityRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/ReservedForTestingProfileGroupExclusivityRoleUiBehavior.java
new file mode 100644
index 000000000..be51a1e17
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/ReservedForTestingProfileGroupExclusivityRoleUiBehavior.java
@@ -0,0 +1,79 @@
+/*
+ * 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.role.ui.behavior;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+
+import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.permissioncontroller.role.ui.RequestRoleItemView;
+import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.util.UserUtils;
+
+import java.util.List;
+
+public class ReservedForTestingProfileGroupExclusivityRoleUiBehavior implements RoleUiBehavior {
+ @Override
+ public void preparePreferenceAsUser(@NonNull Role role, @NonNull TwoTargetPreference preference,
+ @NonNull List<ApplicationInfo> applicationInfos, @NonNull UserHandle user,
+ @NonNull Context context) {
+ if (!applicationInfos.isEmpty()) {
+ preparePreferenceInternal(preference.asPreference(), applicationInfos.get(0), false,
+ user, context);
+ }
+ }
+
+ @Override
+ public void prepareApplicationPreferenceAsUser(@NonNull Role role,
+ @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
+ @NonNull UserHandle user, @NonNull Context context) {
+ preparePreferenceInternal(preference, applicationInfo, true, user, context);
+ }
+
+ @Override
+ public void prepareRequestRoleItemViewAsUser(@NonNull Role role,
+ @NonNull RequestRoleItemView itemView, @NonNull ApplicationInfo applicationInfo,
+ @NonNull UserHandle user, @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ String title = getTitle(applicationInfo, userContext);
+ itemView.getTitleTextView().setText(title);
+ }
+
+ private void preparePreferenceInternal(@NonNull Preference preference,
+ @NonNull ApplicationInfo applicationInfo, boolean setTitle, @NonNull UserHandle user,
+ @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ String title = getTitle(applicationInfo, userContext);
+ if (setTitle) {
+ preference.setTitle(title);
+ } else {
+ preference.setSummary(title);
+ }
+ }
+
+ @NonNull
+ private static String getTitle(@NonNull ApplicationInfo applicationInfo,
+ @NonNull Context context) {
+ return Utils.getFullAppLabel(applicationInfo, context) + "@"
+ + UserHandle.getUserHandleForUid(applicationInfo.uid).getIdentifier();
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListViewModel.java
index ee5a0dbbd..e96fb0943 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListViewModel.java
@@ -26,6 +26,7 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;
+import com.android.permissioncontroller.role.ui.MergeRoleListLiveData;
import com.android.permissioncontroller.role.ui.RoleItem;
import com.android.permissioncontroller.role.ui.RoleListLiveData;
import com.android.permissioncontroller.role.ui.RoleListSortFunction;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java
index 0cc00abc1..c12265d43 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java
@@ -33,6 +33,7 @@ import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData;
+import com.android.permissioncontroller.role.ui.MergeRoleLiveData;
import com.android.permissioncontroller.role.ui.RoleLiveData;
import com.android.permissioncontroller.role.ui.RoleSortFunction;
import com.android.permissioncontroller.role.utils.UserUtils;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt
index a47719cf7..9e93f33c7 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt
@@ -34,7 +34,7 @@ class WearDefaultAppHelper(
val user: UserHandle,
val role: Role,
val viewModel: DefaultAppViewModel,
- val confirmDialogViewModel: DefaultAppConfirmDialogViewModel
+ val confirmDialogViewModel: DefaultAppConfirmDialogViewModel,
) {
fun getTitle() = context.getString(role.labelResource)
@@ -46,7 +46,7 @@ class WearDefaultAppHelper(
context = context,
defaultLabel = context.getString(R.string.default_app_none),
checked = !hasHolderApplication(qualifyingApplications),
- onDefaultCheckChanged = { _ -> viewModel.setNoneDefaultApp() }
+ onDefaultCheckChanged = { _ -> viewModel.setNoneDefaultApp() },
)
.apply { icon = context.getDrawable(R.drawable.ic_remove_circle) }
} else {
@@ -60,6 +60,7 @@ class WearDefaultAppHelper(
.map { pair ->
val appInfo = pair.first
val selected = pair.second
+ val user = UserHandle.getUserHandleForUid(appInfo.uid)
WearRoleApplicationPreference(
context = context,
defaultLabel = Utils.getFullAppLabel(appInfo, context),
@@ -71,15 +72,19 @@ class WearDefaultAppHelper(
RoleUiBehaviorUtils.getConfirmationMessage(
role,
packageName,
- context
+ context,
)
if (confirmationMessage != null) {
- showConfirmDialog(packageName, confirmationMessage.toString())
+ showConfirmDialog(
+ packageName,
+ user,
+ confirmationMessage.toString(),
+ )
} else {
- setDefaultApp(packageName)
+ setDefaultApp(packageName, user)
}
}
- }
+ },
)
.apply {
icon = appInfo.loadIcon(context.packageManager)
@@ -91,22 +96,22 @@ class WearDefaultAppHelper(
this,
appInfo,
user,
- context
+ context,
)
}
}
.toList()
}
- private fun showConfirmDialog(packageName: String, message: String) {
+ private fun showConfirmDialog(packageName: String, userHandle: UserHandle, message: String) {
confirmDialogViewModel.confirmDialogArgs =
ConfirmDialogArgs(
message = message,
onOkButtonClick = {
- setDefaultApp(packageName)
+ setDefaultApp(packageName, userHandle)
dismissConfirmDialog()
},
- onCancelButtonClick = { dismissConfirmDialog() }
+ onCancelButtonClick = { dismissConfirmDialog() },
)
confirmDialogViewModel.showConfirmDialogLiveData.value = true
}
@@ -116,8 +121,8 @@ class WearDefaultAppHelper(
confirmDialogViewModel.showConfirmDialogLiveData.value = false
}
- private fun setDefaultApp(packageName: String) {
- viewModel.setDefaultApp(packageName)
+ private fun setDefaultApp(packageName: String, user: UserHandle) {
+ viewModel.setDefaultApp(packageName, user)
}
fun getDescription() = context.getString(role.descriptionResource)
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt
index 728ea8e99..af8dc5e92 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt
@@ -32,12 +32,14 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.android.permissioncontroller.PermissionControllerStatsLog
import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor
+import com.android.permissioncontroller.role.UserPackage
import com.android.permissioncontroller.role.model.UserDeniedManager
import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData
import com.android.permissioncontroller.role.ui.RequestRoleViewModel
import com.android.permissioncontroller.role.ui.wear.model.WearRequestRoleViewModel
import com.android.permissioncontroller.role.ui.wear.model.WearRequestRoleViewModelFactory
import com.android.permissioncontroller.role.utils.PackageUtils
+import com.android.permissioncontroller.role.utils.UserUtils
import com.android.role.controller.model.Role
import com.android.role.controller.model.Roles
import java.util.Objects
@@ -56,7 +58,7 @@ class WearRequestRoleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
+ savedInstanceState: Bundle?,
): View? {
roleName = arguments?.getString(Intent.EXTRA_ROLE_NAME) ?: ""
packageName = arguments?.getString(Intent.EXTRA_PACKAGE_NAME) ?: ""
@@ -74,12 +76,12 @@ class WearRequestRoleFragment : Fragment() {
if (currentPackageNames.contains(packageName)) {
Log.i(
TAG,
- "Application is already a role holder, role: $roleName, package: $packageName"
+ "Application is already a role holder, role: $roleName, package: $packageName",
)
reportRequestResult(
PermissionControllerStatsLog
.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_ALREADY_GRANTED,
- null
+ null,
)
clearDeniedSetResultOkAndFinish()
return null
@@ -89,7 +91,7 @@ class WearRequestRoleFragment : Fragment() {
Log.w(TAG, "Unknown application: $packageName")
reportRequestResult(
PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED,
- null
+ null,
)
finish()
return null
@@ -98,7 +100,7 @@ class WearRequestRoleFragment : Fragment() {
viewModel =
ViewModelProvider(
this,
- RequestRoleViewModel.Factory(role, requireActivity().application)
+ RequestRoleViewModel.Factory(role, requireActivity().application),
)
.get(RequestRoleViewModel::class.java)
viewModel.manageRoleHolderStateLiveData.observe(this, this::onManageRoleHolderStateChanged)
@@ -117,25 +119,25 @@ class WearRequestRoleFragment : Fragment() {
roleName,
packageName,
viewModel,
- wearViewModel
+ wearViewModel,
)
- val onSetAsDefault: (Boolean, String?) -> Unit = { dontAskAgain, selectedPackageName ->
+ val onSetAsDefault: (Boolean, UserPackage?) -> Unit = { dontAskAgain, selectedUserPackage ->
run {
if (dontAskAgain) {
Log.i(
TAG,
"Request denied with don't ask again, role: $roleName" +
- ", package: $packageName"
+ ", package: $packageName",
)
reportRequestResult(
PermissionControllerStatsLog
.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_ALWAYS,
- null
+ null,
)
setDeniedAlwaysAndFinish()
} else {
- setRoleHolder(selectedPackageName)
+ setRoleHolder(selectedUserPackage)
}
}
}
@@ -143,7 +145,7 @@ class WearRequestRoleFragment : Fragment() {
Log.i(TAG, "Dialog cancelled, role: $roleName , package: $packageName")
reportRequestResult(
PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED,
- null
+ null,
)
setDeniedOnceAndFinish()
}
@@ -158,25 +160,28 @@ class WearRequestRoleFragment : Fragment() {
when (state) {
ManageRoleHolderStateLiveData.STATE_SUCCESS -> {
val lastPackageName = liveData.lastPackageName
+ val lastUser = liveData.lastUser
+ val lastUserPackage: UserPackage? =
+ if (lastPackageName == null || lastUser == null) {
+ null
+ } else {
+ UserPackage.of(lastUser, lastPackageName)
+ }
if (lastPackageName != null) {
- role.onHolderSelectedAsUser(
- lastPackageName,
- liveData.lastUser,
- requireContext()
- )
+ role.onHolderSelectedAsUser(lastPackageName, lastUser, requireContext())
}
- if (lastPackageName == packageName) {
+ if (isRequestingApplication(lastUserPackage)) {
Log.i(
TAG,
"Application added as a role holder, role: $roleName, package: " +
- packageName
+ packageName,
)
clearDeniedSetResultOkAndFinish()
} else {
Log.i(
TAG,
"Request denied with another application added as a role holder, " +
- "role: $roleName, package: $packageName"
+ "role: $roleName, package: $packageName",
)
setDeniedOnceAndFinish()
}
@@ -196,23 +201,24 @@ class WearRequestRoleFragment : Fragment() {
finish()
}
- private fun reportRequestResult(result: Int, grantedAnotherPackageName: String?) {
- val holderPackageName: String? = getHolderPackageName()
+ private fun reportRequestResult(result: Int, grantedUserPackage: UserPackage?) {
+ val holderUserPackage: UserPackage? = getHolderUserPackage()
reportRequestResult(
- getApplicationUid(packageName),
+ getRequestingApplicationUid(),
packageName,
roleName,
getQualifyingApplicationCount(),
- getQualifyingApplicationUid(holderPackageName),
- holderPackageName,
- getQualifyingApplicationUid(grantedAnotherPackageName),
- grantedAnotherPackageName,
- result
+ getQualifyingApplicationUid(holderUserPackage),
+ holderUserPackage?.packageName,
+ getQualifyingApplicationUid(grantedUserPackage),
+ grantedUserPackage?.packageName,
+ result,
)
}
- private fun getApplicationUid(packageName: String): Int {
- val uid: Int = getQualifyingApplicationUid(packageName)
+ private fun getRequestingApplicationUid(): Int {
+ val uid: Int =
+ getQualifyingApplicationUid(UserPackage.of(Process.myUserHandle(), packageName))
if (uid != -1) {
return uid
}
@@ -221,14 +227,15 @@ class WearRequestRoleFragment : Fragment() {
return applicationInfo.uid
}
- private fun getQualifyingApplicationUid(packageName: String?): Int {
- if (packageName == null) {
+ private fun getQualifyingApplicationUid(userPackage: UserPackage?): Int {
+ if (userPackage == null) {
return -1
}
viewModel.roleLiveData.value?.let { qualifyingApplications ->
for (qualifyingApplication in qualifyingApplications) {
val qualifyingApplicationInfo = qualifyingApplication.first
- if (Objects.equals(qualifyingApplicationInfo.packageName, packageName)) {
+ val qualifyingAppUserPackage = UserPackage.from(qualifyingApplicationInfo)
+ if (Objects.equals(qualifyingAppUserPackage, userPackage)) {
return qualifyingApplicationInfo.uid
}
}
@@ -236,12 +243,13 @@ class WearRequestRoleFragment : Fragment() {
return -1
}
- private fun getHolderPackageName(): String? {
+ private fun getHolderUserPackage(): UserPackage? {
viewModel.roleLiveData.value?.let { qualifyingApplications ->
for (qualifyingApplication in qualifyingApplications) {
val isHolderApplication = qualifyingApplication.second
if (isHolderApplication) {
- return qualifyingApplication.first.packageName
+ val applicationInfo = qualifyingApplication.first
+ return UserPackage.from(applicationInfo)
}
}
}
@@ -261,49 +269,58 @@ class WearRequestRoleFragment : Fragment() {
requireActivity().finish()
}
- private fun setRoleHolder(selectedPackageName: String?) {
+ private fun setRoleHolder(selectedUserPackage: UserPackage?) {
val context: Context = requireContext()
- val user: UserHandle = Process.myUserHandle()
- if (selectedPackageName == null) {
+ if (selectedUserPackage == null) {
reportRequestResult(
PermissionControllerStatsLog
.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_GRANTED_ANOTHER,
- null
+ null,
)
+ val user: UserHandle =
+ if (role.exclusivity == Role.EXCLUSIVITY_PROFILE_GROUP) {
+ UserUtils.getProfileParentOrSelf(Process.myUserHandle(), context)
+ } else {
+ Process.myUserHandle()
+ }
role.onNoneHolderSelectedAsUser(user, context)
viewModel.manageRoleHolderStateLiveData.clearRoleHoldersAsUser(
roleName,
0,
user,
- context
+ context,
)
} else {
- val isRequestingApplication = selectedPackageName == packageName
+ val isRequestingApplication = isRequestingApplication(selectedUserPackage)
if (isRequestingApplication) {
reportRequestResult(
PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED,
- null
+ null,
)
} else {
reportRequestResult(
PermissionControllerStatsLog
.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_GRANTED_ANOTHER,
- selectedPackageName
+ selectedUserPackage,
)
}
val flags =
if (isRequestingApplication) RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP else 0
viewModel.manageRoleHolderStateLiveData.setRoleHolderAsUser(
roleName,
- selectedPackageName,
+ selectedUserPackage.packageName,
true,
flags,
- user,
- context
+ selectedUserPackage.user,
+ context,
)
}
}
+ private fun isRequestingApplication(userPackage: UserPackage?): Boolean {
+ return Objects.equals(userPackage, UserPackage.of(Process.myUserHandle(), packageName))
+ }
+
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
wearViewModel.onSaveInstanceState(outState)
@@ -318,12 +335,12 @@ class WearRequestRoleFragment : Fragment() {
TAG,
"Application is uninstalled, role: $roleName" +
", package: " +
- packageName
+ packageName,
)
reportRequestResult(
PermissionControllerStatsLog
.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED,
- null
+ null,
)
finish()
}
@@ -364,7 +381,7 @@ class WearRequestRoleFragment : Fragment() {
currentPackageName: String?,
grantedAnotherUid: Int,
grantedAnotherPackageName: String?,
- result: Int
+ result: Int,
) {
Log.i(
TAG,
@@ -376,7 +393,7 @@ class WearRequestRoleFragment : Fragment() {
" currentPackageName=$currentPackageName" +
" grantedAnotherUid=$grantedAnotherUid" +
" grantedAnotherPackageName=$grantedAnotherPackageName" +
- " result=$result"
+ " result=$result",
)
PermissionControllerStatsLog.write(
PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED,
@@ -388,7 +405,7 @@ class WearRequestRoleFragment : Fragment() {
currentPackageName,
grantedAnotherUid,
grantedAnotherPackageName,
- result
+ result,
)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt
index c7f14b862..f8a1b1ee8 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt
@@ -19,13 +19,16 @@ package com.android.permissioncontroller.role.ui.wear
import android.content.Context
import android.content.pm.ApplicationInfo
import android.graphics.drawable.Drawable
+import android.os.Process
import android.util.Pair
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.role.UserPackage
import com.android.permissioncontroller.role.model.UserDeniedManager
import com.android.permissioncontroller.role.ui.RequestRoleViewModel
import com.android.permissioncontroller.role.ui.wear.model.WearRequestRoleViewModel
import com.android.role.controller.model.Role
+import java.util.Objects
/** A helper class for [WearRequestRoleScreen]. */
class WearRequestRoleHelper(
@@ -35,7 +38,7 @@ class WearRequestRoleHelper(
val roleName: String,
val packageName: String,
val viewModel: RequestRoleViewModel,
- val wearViewModel: WearRequestRoleViewModel
+ val wearViewModel: WearRequestRoleViewModel,
) {
fun getIcon() = Utils.getBadgedIcon(context, applicationInfo)
@@ -48,12 +51,12 @@ class WearRequestRoleHelper(
fun getNonePreference(
qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
- selectedPackage: String?
+ selectedPackage: UserPackage?,
): RequestRolePreference? =
if (role.shouldShowNone()) {
val hasHolderApplication = hasHolderApplication(qualifyingApplications)
RequestRolePreference(
- packageName = null,
+ userPackage = null,
label = context.getString(R.string.default_app_none),
subTitle =
if (!hasHolderApplication) {
@@ -62,14 +65,14 @@ class WearRequestRoleHelper(
null
},
icon = context.getDrawable(R.drawable.ic_remove_circle),
- checked = selectedPackage.isNullOrEmpty(),
+ checked = selectedPackage == null,
enabled =
if (!wearViewModel.dontAskAgain()) {
true
} else {
!hasHolderApplication
},
- isHolder = !hasHolderApplication
+ isHolder = !hasHolderApplication,
)
} else {
null
@@ -77,12 +80,13 @@ class WearRequestRoleHelper(
fun getPreferences(
qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
- selectedPackage: String?
+ selectedPackage: UserPackage?,
): List<RequestRolePreference> {
return qualifyingApplications
.map { qualifyingApplication ->
+ val userPackage = UserPackage.from(qualifyingApplication.first)
RequestRolePreference(
- packageName = qualifyingApplication.first.packageName,
+ userPackage = userPackage,
label = Utils.getAppLabel(qualifyingApplication.first, context),
subTitle =
if (qualifyingApplication.second) {
@@ -91,14 +95,14 @@ class WearRequestRoleHelper(
context.getString(role.requestDescriptionResource)
},
icon = Utils.getBadgedIcon(context, qualifyingApplication.first),
- checked = qualifyingApplication.first.packageName.equals(selectedPackage),
+ checked = Objects.equals(userPackage, selectedPackage),
enabled =
if (!wearViewModel.dontAskAgain()) {
true
} else {
qualifyingApplication.second
},
- isHolder = qualifyingApplication.second
+ isHolder = qualifyingApplication.second,
)
}
.toList()
@@ -112,16 +116,20 @@ class WearRequestRoleHelper(
return enabled && (wearViewModel.dontAskAgain() || !wearViewModel.isHolderChecked)
}
- fun initializeHolderPackageName(qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>) {
- wearViewModel.holderPackageName =
- qualifyingApplications.find { it.second }?.first?.packageName
+ fun initializeHolderPackage(qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>) {
+ wearViewModel.holderPackage =
+ qualifyingApplications
+ .find { it.second }
+ ?.first
+ ?.let { appInfo -> UserPackage.from(appInfo) }
}
- fun initializeSelectedPackageName() {
- if (wearViewModel.holderPackageName == null) {
- wearViewModel.selectedPackageName.value = null
+ fun initializeSelectedPackage() {
+ if (wearViewModel.holderPackage == null) {
+ wearViewModel.selectedPackage.value = null
} else {
- wearViewModel.selectedPackageName.value = packageName
+ wearViewModel.selectedPackage.value =
+ UserPackage.of(Process.myUserHandle(), packageName)
}
}
@@ -131,7 +139,7 @@ class WearRequestRoleHelper(
val icon: Drawable?,
val checked: Boolean,
val enabled: Boolean,
- val packageName: String?,
- val isHolder: Boolean
+ val userPackage: UserPackage?,
+ val isHolder: Boolean,
)
}
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..fcc0d56f9 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,27 @@ 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.UserPackage
import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData
@Composable
fun WearRequestRoleScreen(
helper: WearRequestRoleHelper,
- onSetAsDefault: (Boolean, String?) -> Unit,
- onCanceled: () -> Unit
+ onSetAsDefault: (Boolean, UserPackage?) -> Unit,
+ onCanceled: () -> Unit,
) {
val roleLiveData = helper.viewModel.roleLiveData.observeAsState(emptyList())
val manageRoleHolderState =
@@ -53,39 +58,46 @@ fun WearRequestRoleScreen(
ManageRoleHolderStateLiveData.STATE_WORKING
)
val dontAskAgain = helper.wearViewModel.dontAskAgain.observeAsState(false)
- val selectedPackageName = helper.wearViewModel.selectedPackageName.observeAsState(null)
+ val selectedPackage = helper.wearViewModel.selectedPackage.observeAsState(null)
var isLoading by remember { mutableStateOf(true) }
if (isLoading && roleLiveData.value.isNotEmpty()) {
- helper.initializeHolderPackageName(roleLiveData.value)
- helper.initializeSelectedPackageName()
+ helper.initializeHolderPackage(roleLiveData.value)
+ helper.initializeSelectedPackage()
}
- val onCheckedChanged: (Boolean, String?, Boolean) -> Unit = { checked, packageName, isHolder ->
- if (checked) {
- helper.wearViewModel.selectedPackageName.value = packageName
- helper.wearViewModel.isHolderChecked = isHolder
+ val onCheckedChanged: (Boolean, UserPackage?, Boolean) -> Unit =
+ { checked, userPackage, isHolder ->
+ if (checked) {
+ helper.wearViewModel.selectedPackage.value = userPackage
+ helper.wearViewModel.isHolderChecked = isHolder
+ }
}
- }
val onDontAskAgainCheckedChanged: (Boolean) -> Unit = { checked ->
helper.wearViewModel.dontAskAgain.value = checked
if (checked) {
- helper.initializeSelectedPackageName()
+ helper.initializeSelectedPackage()
}
}
-
+ val materialUIVersion =
+ if (ResourceHelper.material3Enabled) {
+ MATERIAL3
+ } else {
+ MATERIAL2_5
+ }
WearRequestRoleContent(
+ materialUIVersion,
isLoading,
helper,
roleLiveData.value,
manageRoleHolderState.value == ManageRoleHolderStateLiveData.STATE_IDLE,
dontAskAgain.value,
- selectedPackageName.value,
+ selectedPackage.value,
onCheckedChanged,
onDontAskAgainCheckedChanged,
onSetAsDefault,
- onCanceled
+ onCanceled,
)
if (isLoading && roleLiveData.value.isNotEmpty()) {
@@ -95,65 +107,84 @@ fun WearRequestRoleScreen(
@Composable
internal fun WearRequestRoleContent(
+ materialUIVersion: WearPermissionMaterialUIVersion,
isLoading: Boolean,
helper: WearRequestRoleHelper,
qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
enabled: Boolean,
dontAskAgain: Boolean,
- selectedPackageName: String?,
- onCheckedChanged: (Boolean, String?, Boolean) -> Unit,
+ selectedPackage: UserPackage?,
+ onCheckedChanged: (Boolean, UserPackage?, Boolean) -> Unit,
onDontAskAgainCheckedChanged: (Boolean) -> Unit,
- onSetAsDefault: (Boolean, String?) -> Unit,
- onCanceled: () -> Unit
+ onSetAsDefault: (Boolean, UserPackage?) -> 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, selectedPackage)?.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.userPackage, 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)) {
+ for (pref in helper.getPreferences(qualifyingApplications, selectedPackage)) {
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.userPackage, pref.isHolder)
},
toggleControl = ToggleChipToggleControl.Radio,
)
}
- pref.subTitle?.let { subTitle -> item { ListFooter(description = subTitle) } }
+ pref.subTitle?.let { subTitle ->
+ item {
+ WearPermissionListFooter(
+ materialUIVersion = materialUIVersion,
+ label = subTitle,
+ )
+ }
+ }
}
if (helper.showDontAskButton()) {
item {
- ToggleChip(
+ WearPermissionToggleControl(
+ materialUIVersion = materialUIVersion,
checked = dontAskAgain,
enabled = enabled,
onCheckedChanged = { checked -> run { onDontAskAgainCheckedChanged(checked) } },
label = stringResource(R.string.request_role_dont_ask_again),
toggleControl = ToggleChipToggleControl.Checkbox,
- colors = toggleChipBackgroundColors(),
+ style = WearPermissionToggleControlStyle.Transparent,
modifier =
Modifier.testTag("com.android.permissioncontroller:id/dont_ask_again"),
)
@@ -163,17 +194,18 @@ internal fun WearRequestRoleContent(
item { Spacer(modifier = Modifier.height(14.dp)) }
item {
- Chip(
+ WearPermissionButton(
+ materialUIVersion = materialUIVersion,
label = stringResource(R.string.request_role_set_as_default),
- textColor = MaterialTheme.colors.background,
- colors = ChipDefaults.primaryChipColors(),
+ style = WearPermissionButtonStyle.Primary,
enabled = helper.shouldSetAsDefaultEnabled(enabled),
- onClick = { onSetAsDefault(dontAskAgain, selectedPackageName) },
+ onClick = { onSetAsDefault(dontAskAgain, selectedPackage) },
modifier = Modifier.testTag("android:id/button1"),
)
}
item {
- Chip(
+ WearPermissionButton(
+ materialUIVersion = materialUIVersion,
label = stringResource(R.string.cancel),
enabled = enabled,
onClick = { onCanceled() },
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/WearRequestRoleViewModel.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/WearRequestRoleViewModel.kt
index 692533916..7cc0b2b52 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/WearRequestRoleViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/WearRequestRoleViewModel.kt
@@ -17,16 +17,18 @@
package com.android.permissioncontroller.role.ui.wear.model
import android.os.Bundle
+import androidx.core.os.BundleCompat
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.role.UserPackage
/** ViewModel for WearRequestRoleScreen. */
class WearRequestRoleViewModel : ViewModel() {
val dontAskAgain = MutableLiveData<Boolean>(false)
- val selectedPackageName = MutableLiveData<String?>(null)
+ val selectedPackage = MutableLiveData<UserPackage?>(null)
var isHolderChecked: Boolean = false
- var holderPackageName: String? = null
+ var holderPackage: UserPackage? = null
fun dontAskAgain(): Boolean {
return dontAskAgain.value ?: false
@@ -34,20 +36,25 @@ class WearRequestRoleViewModel : ViewModel() {
fun onSaveInstanceState(outState: Bundle) {
outState.putBoolean(STATE_DONT_ASK_AGAIN, dontAskAgain())
- outState.putString(STATE_SELECTED_PACKAGE_NAME, selectedPackageName.value)
+ outState.putParcelable(STATE_SELECTED_USER_PACKAGE, selectedPackage.value)
outState.putBoolean(STATE_IS_HOLDER_CHECKED, isHolderChecked)
}
fun onRestoreInstanceState(savedInstanceState: Bundle) {
dontAskAgain.value = savedInstanceState.getBoolean(STATE_DONT_ASK_AGAIN)
- selectedPackageName.value = savedInstanceState.getString(STATE_SELECTED_PACKAGE_NAME)
isHolderChecked = savedInstanceState.getBoolean(STATE_IS_HOLDER_CHECKED)
+ selectedPackage.value =
+ BundleCompat.getParcelable(
+ savedInstanceState,
+ STATE_SELECTED_USER_PACKAGE,
+ UserPackage::class.java,
+ )
}
companion object {
const val STATE_DONT_ASK_AGAIN = "WearRequestRoleViewModel.state.DONT_ASK_AGAIN"
- const val STATE_SELECTED_PACKAGE_NAME =
- "WearRequestRoleViewModel.state.SELECTED_PACKAGE_NAME"
+ const val STATE_SELECTED_USER_PACKAGE =
+ "WearRequestRoleViewModel.state.SELECTED_USER_PACKAGE"
const val STATE_IS_HOLDER_CHECKED = "WearRequestRoleViewModel.state.IS_HOLDER_CHECKED"
}
}
@@ -55,6 +62,7 @@ class WearRequestRoleViewModel : ViewModel() {
/** Factory for a WearRequestRoleViewModel */
class WearRequestRoleViewModelFactory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
- @Suppress("UNCHECKED_CAST") return WearRequestRoleViewModel() as T
+ @Suppress("UNCHECKED_CAST")
+ return WearRequestRoleViewModel() as T
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java
index 339b2a12a..df0aa99f2 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java
@@ -37,11 +37,24 @@ public class UserUtils {
private UserUtils() {}
/**
+ * Returns the parent of a given user, or user if it has no parent (e.g. it is the primary
+ * user)
+ */
+ @NonNull
+ public static UserHandle getProfileParentOrSelf(@NonNull UserHandle user,
+ @NonNull Context context) {
+ return com.android.role.controller.util.UserUtils.getProfileParentOrSelf(user, context);
+ }
+
+ /**
* Get the work profile of current user, if any.
+ * <p>
+ * If {@link Process#myUserHandle()} is the work profile, then {@code null} is returned
*
* @param context the {@code Context} to retrieve system services
*
- * @return the work profile of current user, or {@code null} if none
+ * @return the work profile of current user, or {@code null} if none or if work profile is the
+ * current process user
*/
@Nullable
public static UserHandle getWorkProfile(@NonNull Context context) {
@@ -65,6 +78,30 @@ public class UserUtils {
}
/**
+ * Get the work profile of current profile-group, if any.
+ * <p>
+ * {@link Process#myUserHandle()} may be the work profile, and is a valid returned value
+ *
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return the work profile in the current profile-group, or {@code null} if none
+ */
+ @Nullable
+ public static UserHandle getWorkProfileOrSelf(@NonNull Context context) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ List<UserHandle> profiles = userManager.getUserProfiles();
+ int profilesSize = profiles.size();
+ for (int i = 0; i < profilesSize; i++) {
+ UserHandle profile = profiles.get(i);
+ if (!userManager.isManagedProfile(profile.getIdentifier())) {
+ continue;
+ }
+ return profile;
+ }
+ return null;
+ }
+
+ /**
* Get the private profile of current user, if any.
*
* @param context the {@code Context} to retrieve system services
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java
index 3ab4faa66..54cb86ff3 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java
@@ -26,7 +26,7 @@ import android.view.ViewGroup;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceViewHolder;
-import androidx.preference.SwitchPreference;
+import androidx.preference.SwitchPreferenceCompat;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel;
@@ -38,7 +38,7 @@ import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsVie
* method will not register any changes while it appears disabled.
*/
@RequiresApi(TIRAMISU)
-public class ClickableDisabledSwitchPreference extends SwitchPreference {
+public class ClickableDisabledSwitchPreference extends SwitchPreferenceCompat {
private boolean mAppearDisabled;
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/HighlightablePreferenceGroupAdapter.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/HighlightablePreferenceGroupAdapter.java
index c6cfa3a9a..a079405e6 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/HighlightablePreferenceGroupAdapter.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/HighlightablePreferenceGroupAdapter.java
@@ -21,17 +21,23 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
+import androidx.annotation.DrawableRes;
+import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.PreferenceViewHolder;
import androidx.recyclerview.widget.RecyclerView;
import com.android.permissioncontroller.R;
+import com.android.settingslib.widget.SettingsPreferenceGroupAdapter;
+import com.android.settingslib.widget.SettingsThemeHelper;
import com.google.android.material.appbar.AppBarLayout;
@@ -41,7 +47,7 @@ import com.google.android.material.appbar.AppBarLayout;
*
* @see com.android.settings.widget.HighlightablePreferenceGroupAdapter
*/
-public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
+public class HighlightablePreferenceGroupAdapter extends SettingsPreferenceGroupAdapter {
private static final String TAG = "HighlightableAdapter";
private static final long DELAY_COLLAPSE_DURATION_MILLIS = 300L;
@@ -50,10 +56,12 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter
private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L;
private static final long HIGHLIGHT_FADE_IN_DURATION = 200L;
+ private final int mInitialBackgroundColor;
private final int mHighlightColor;
- private boolean mFadeInAnimated;
+ boolean mFadeInAnimated;
- private final int mNormalBackgroundRes;
+ private final Context mContext;
+ private final @DrawableRes int mNormalBackgroundRes;
private final String mHighlightKey;
private boolean mHighlightRequested;
private int mHighlightPosition = RecyclerView.NO_POSITION;
@@ -63,13 +71,27 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter
super(preferenceGroup);
mHighlightKey = key;
mHighlightRequested = highlightRequested;
- final Context context = preferenceGroup.getContext();
- final TypedValue outValue = new TypedValue();
- context.getTheme()
+ mContext = preferenceGroup.getContext();
+
+ final TypedValue backgroundResOutValue = new TypedValue();
+ mContext.getTheme()
.resolveAttribute(
- android.R.attr.selectableItemBackground, outValue, true /* resolveRefs */);
- mNormalBackgroundRes = outValue.resourceId;
- mHighlightColor = context.getColor(R.color.preference_highlight_color);
+ android.R.attr.selectableItemBackground,
+ backgroundResOutValue,
+ true /* resolveRefs */);
+ mNormalBackgroundRes = backgroundResOutValue.resourceId;
+
+ if (SettingsThemeHelper.isExpressiveTheme(mContext)) {
+ final TypedValue backgroundColorOutValue = new TypedValue();
+ mContext.getTheme()
+ .resolveAttribute(
+ R.attr.colorSurface, backgroundColorOutValue, true /* resolveRefs */);
+ mInitialBackgroundColor = backgroundColorOutValue.resourceId;
+ } else {
+ mInitialBackgroundColor = mContext.getColor(android.R.color.transparent);
+ }
+
+ mHighlightColor = mContext.getColor(R.color.preference_highlight_color);
}
@Override
@@ -78,11 +100,13 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter
updateBackground(holder, position);
}
- private void updateBackground(PreferenceViewHolder holder, int position) {
+ void updateBackground(PreferenceViewHolder holder, int position) {
View v = holder.itemView;
- if (position == mHighlightPosition
- && (mHighlightKey != null
- && TextUtils.equals(mHighlightKey, getItem(position).getKey()))) {
+ Preference preference = getItem(position);
+ if (preference != null
+ && position == mHighlightPosition
+ && (mHighlightKey != null && TextUtils.equals(mHighlightKey, preference.getKey()))
+ && v.isShown()) {
// This position should be highlighted. If it's highlighted before - skip animation.
addHighlightBackground(holder, !mFadeInAnimated);
} else if (Boolean.TRUE.equals(v.getTag(R.id.preference_highlighted))) {
@@ -109,31 +133,46 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter
// Collapse app bar after 300 milliseconds.
if (appBarLayout != null) {
root.postDelayed(
- () -> {
- appBarLayout.setExpanded(false, true);
- },
- DELAY_COLLAPSE_DURATION_MILLIS);
+ () -> appBarLayout.setExpanded(false, true), DELAY_COLLAPSE_DURATION_MILLIS);
}
// Remove the animator as early as possible to avoid a RecyclerView crash.
recyclerView.setItemAnimator(null);
- // Scroll to correct position after 600 milliseconds.
+ // Scroll to correct position after a short delay.
root.postDelayed(
() -> {
if (ensureHighlightPosition()) {
recyclerView.smoothScrollToPosition(mHighlightPosition);
+ highlightAndFocusTargetItem(recyclerView, mHighlightPosition);
}
},
DELAY_HIGHLIGHT_DURATION_MILLIS);
+ }
- // Highlight preference after 900 milliseconds.
- root.postDelayed(
- () -> {
- if (ensureHighlightPosition()) {
- notifyItemChanged(mHighlightPosition);
- }
- },
- DELAY_COLLAPSE_DURATION_MILLIS + DELAY_HIGHLIGHT_DURATION_MILLIS);
+ private void highlightAndFocusTargetItem(RecyclerView recyclerView, int highlightPosition) {
+ RecyclerView.ViewHolder target =
+ recyclerView.findViewHolderForAdapterPosition(highlightPosition);
+ if (target != null) { // view already visible
+ notifyItemChanged(mHighlightPosition);
+ target.itemView.requestFocus();
+ } else { // otherwise we're about to scroll to that view (but we might not be scrolling yet)
+ recyclerView.addOnScrollListener(
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+ notifyItemChanged(mHighlightPosition);
+ RecyclerView.ViewHolder target =
+ recyclerView.findViewHolderForAdapterPosition(
+ highlightPosition);
+ if (target != null) {
+ target.itemView.requestFocus();
+ }
+ recyclerView.removeOnScrollListener(this);
+ }
+ }
+ });
+ }
}
/**
@@ -159,7 +198,7 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter
}
/** Remove the highlighted background with a delay */
- public void requestRemoveHighlightDelayed(PreferenceViewHolder holder) {
+ private void requestRemoveHighlightDelayed(PreferenceViewHolder holder) {
final View v = holder.itemView;
v.postDelayed(
() -> {
@@ -172,33 +211,36 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter
private void addHighlightBackground(PreferenceViewHolder holder, boolean animate) {
final View v = holder.itemView;
v.setTag(R.id.preference_highlighted, true);
+
if (!animate) {
- v.setBackgroundColor(mHighlightColor);
+ setBackgroundColor(v, mHighlightColor);
Log.d(TAG, "AddHighlight: Not animation requested - setting highlight background");
requestRemoveHighlightDelayed(holder);
return;
}
mFadeInAnimated = true;
- final int colorFrom = mNormalBackgroundRes;
- final int colorTo = mHighlightColor;
+
final ValueAnimator fadeInLoop =
- ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
+ ValueAnimator.ofObject(
+ new ArgbEvaluator(), mInitialBackgroundColor, mHighlightColor);
fadeInLoop.setDuration(HIGHLIGHT_FADE_IN_DURATION);
fadeInLoop.addUpdateListener(
- animator -> v.setBackgroundColor((int) animator.getAnimatedValue()));
+ animator -> setBackgroundColor(v, (int) animator.getAnimatedValue()));
fadeInLoop.setRepeatMode(ValueAnimator.REVERSE);
fadeInLoop.setRepeatCount(4);
fadeInLoop.start();
Log.d(TAG, "AddHighlight: starting fade in animation");
+
holder.setIsRecyclable(false);
requestRemoveHighlightDelayed(holder);
}
private void removeHighlightBackground(PreferenceViewHolder holder, boolean animate) {
final View v = holder.itemView;
+
if (!animate) {
v.setTag(R.id.preference_highlighted, false);
- v.setBackgroundResource(mNormalBackgroundRes);
+ clearBackgroundColor(v);
Log.d(TAG, "RemoveHighlight: No animation requested - setting normal background");
return;
}
@@ -208,27 +250,44 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter
Log.d(TAG, "RemoveHighlight: Not highlighted - skipping");
return;
}
- int colorFrom = mHighlightColor;
- int colorTo = mNormalBackgroundRes;
v.setTag(R.id.preference_highlighted, false);
final ValueAnimator colorAnimation =
- ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
+ ValueAnimator.ofObject(
+ new ArgbEvaluator(), mHighlightColor, mInitialBackgroundColor);
colorAnimation.setDuration(HIGHLIGHT_FADE_OUT_DURATION);
colorAnimation.addUpdateListener(
- animator -> v.setBackgroundColor((int) animator.getAnimatedValue()));
+ animator -> setBackgroundColor(v, (int) animator.getAnimatedValue()));
colorAnimation.addListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- // Animation complete - the background is now white. Change to
- // mNormalBackgroundRes
- // so it is white and has ripple on touch.
- v.setBackgroundResource(mNormalBackgroundRes);
+ // Animation complete - the background needs to be the target background.
+ clearBackgroundColor(v);
holder.setIsRecyclable(true);
}
});
colorAnimation.start();
Log.d(TAG, "Starting fade out animation");
}
+
+ private void setBackgroundColor(View v, int color) {
+ if (SettingsThemeHelper.isExpressiveTheme(mContext)) {
+ v.getBackground()
+ .setColorFilter(
+ new PorterDuffColorFilter(
+ color,
+ PorterDuff.Mode.SRC_ATOP));
+ } else {
+ v.setBackgroundColor(color);
+ }
+ }
+
+ private void clearBackgroundColor(View v) {
+ if (SettingsThemeHelper.isExpressiveTheme(mContext)) {
+ v.getBackground().clearColorFilter();
+ } else {
+ v.setBackgroundResource(mNormalBackgroundRes);
+ }
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
index 7622270b9..88759797e 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
@@ -52,6 +52,7 @@ import androidx.preference.PreferenceViewHolder;
import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
+import com.android.settingslib.widget.GroupSectionDividerMixin;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.shape.AbsoluteCornerSize;
@@ -62,7 +63,8 @@ import java.util.Objects;
/** A preference that displays a card representing a {@link SafetyCenterIssue}. */
@RequiresApi(TIRAMISU)
-public class IssueCardPreference extends Preference implements ComparablePreference {
+public class IssueCardPreference extends Preference
+ implements ComparablePreference, GroupSectionDividerMixin {
public static final String TAG = IssueCardPreference.class.getSimpleName();
@@ -101,12 +103,13 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- holder.itemView.setBackgroundResource(mPositionInCardList.getBackgroundDrawableResId());
+ View issueCardView = holder.itemView.requireViewById(R.id.issue_card);
+ issueCardView.setBackgroundResource(mPositionInCardList.getBackgroundDrawableResId());
int topMargin = getTopMargin(mPositionInCardList, getContext());
- MarginLayoutParams layoutParams = (MarginLayoutParams) holder.itemView.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) issueCardView.getLayoutParams();
if (layoutParams.topMargin != topMargin) {
layoutParams.topMargin = topMargin;
- holder.itemView.setLayoutParams(layoutParams);
+ issueCardView.setLayoutParams(layoutParams);
}
// Set default group visibility in case view is being reused
@@ -202,19 +205,20 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
private void configureSafetyProtectionView(PreferenceViewHolder holder) {
View safetyProtectionSectionView =
holder.findViewById(R.id.issue_card_protected_by_android);
+ View issueCard = holder.findViewById(R.id.issue_card);
if (safetyProtectionSectionView.getVisibility() == View.GONE) {
- holder.itemView.setPaddingRelative(
- holder.itemView.getPaddingStart(),
- holder.itemView.getPaddingTop(),
- holder.itemView.getPaddingEnd(),
+ issueCard.setPaddingRelative(
+ issueCard.getPaddingStart(),
+ issueCard.getPaddingTop(),
+ issueCard.getPaddingEnd(),
/* bottom= */ getContext()
.getResources()
.getDimensionPixelSize(R.dimen.sc_card_margin_bottom));
} else {
- holder.itemView.setPaddingRelative(
- holder.itemView.getPaddingStart(),
- holder.itemView.getPaddingTop(),
- holder.itemView.getPaddingEnd(),
+ issueCard.setPaddingRelative(
+ issueCard.getPaddingStart(),
+ issueCard.getPaddingTop(),
+ issueCard.getPaddingEnd(),
/* bottom= */ 0);
}
}
@@ -427,9 +431,7 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
TypedValue buttonThemeValue = new TypedValue();
mContext.getTheme()
.resolveAttribute(
- R.attr.scActionButtonTheme,
- buttonThemeValue,
- /* resolveRefs= */ false);
+ R.attr.scActionButtonTheme, buttonThemeValue, /* resolveRefs= */ false);
mContextThemeWrapper = new ContextThemeWrapper(context, buttonThemeValue.data);
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt
index a63e19984..5c86b4515 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt
@@ -24,6 +24,7 @@ import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.R
import com.android.permissioncontroller.safetycenter.ui.view.MoreIssuesHeaderView
+import com.android.settingslib.widget.GroupSectionDividerMixin
/** A preference that displays a card linking to a list of more {@link SafetyCenterIssue}. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@@ -34,8 +35,8 @@ internal class MoreIssuesCardPreference(
private var newMoreIssuesCardData: MoreIssuesCardData,
private val dismissedOnly: Boolean,
val isStaticHeader: Boolean,
- private val onClickListener: () -> Unit
-) : Preference(context), ComparablePreference {
+ private val onClickListener: () -> Unit,
+) : Preference(context), ComparablePreference, GroupSectionDividerMixin {
init {
layoutResource = R.layout.preference_more_issues_card
@@ -44,11 +45,12 @@ internal class MoreIssuesCardPreference(
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
- val issueHeaderView = holder.itemView as MoreIssuesHeaderView
+ val issueHeaderView =
+ holder.itemView.requireViewById<MoreIssuesHeaderView>(R.id.more_issues_card)
if (isStaticHeader) {
issueHeaderView.showStaticHeader(
context.getString(R.string.safety_center_dismissed_issues_card_title),
- newMoreIssuesCardData.severityLevel
+ newMoreIssuesCardData.severityLevel,
)
} else {
issueHeaderView.showExpandableHeader(
@@ -62,7 +64,7 @@ internal class MoreIssuesCardPreference(
}
),
overrideChevronIconResId,
- onClickListener
+ onClickListener,
)
}
}
@@ -94,5 +96,5 @@ internal class MoreIssuesCardPreference(
internal data class MoreIssuesCardData(
val severityLevel: Int,
val hiddenIssueCount: Int,
- val isExpanded: Boolean
+ val isExpanded: Boolean,
)
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt
index 57e4175ca..c5287af53 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt
@@ -28,11 +28,12 @@ import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
import com.android.permissioncontroller.R
import com.android.permissioncontroller.safetycenter.SafetyCenterConstants
+import com.android.settingslib.widget.GroupSectionDividerMixin
/** A preference that displays the Security and Privacy brand name on a Safety Center subpage. */
@RequiresApi(UPSIDE_DOWN_CAKE)
internal class SafetyBrandChipPreference(context: Context, attrs: AttributeSet) :
- Preference(context, attrs) {
+ Preference(context, attrs), GroupSectionDividerMixin {
init {
setLayoutResource(R.layout.preference_brand_chip)
@@ -67,7 +68,7 @@ internal class SafetyBrandChipPreference(context: Context, attrs: AttributeSet)
fun closeSubpage(
fragmentActivity: FragmentActivity,
fragmentContext: Context,
- sessionId: Long
+ sessionId: Long,
) {
val openedFromHomepage =
fragmentActivity
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
index c6f2d146f..04206479f 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
@@ -29,6 +29,8 @@ import static com.android.permissioncontroller.safetycenter.SafetyCenterConstant
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVATE_PROFILE_SUFFIX;
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.WORK_PROFILE_SUFFIX;
+import static java.util.Objects.requireNonNull;
+
import android.app.ActionBar;
import android.content.Intent;
import android.content.res.Configuration;
@@ -60,6 +62,7 @@ import com.android.permissioncontroller.permission.utils.Utils;
import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel.Pref;
import com.android.settingslib.activityembedding.ActivityEmbeddingUtils;
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+import com.android.settingslib.widget.SettingsThemeHelper;
import java.util.List;
import java.util.Objects;
@@ -78,10 +81,10 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
private static final String EXTRA_PREVENT_TRAMPOLINE_TO_SETTINGS =
"com.android.permissioncontroller.safetycenter.extra.PREVENT_TRAMPOLINE_TO_SETTINGS";
- private SafetyCenterManager mSafetyCenterManager;
+ @Nullable private SafetyCenterManager mSafetyCenterManager;
@Override
- public void onCreate(Bundle savedInstanceState) {
+ public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSafetyCenterManager = getSystemService(SafetyCenterManager.class);
@@ -93,18 +96,25 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
return;
}
+ if (SettingsThemeHelper.isExpressiveTheme(this)) {
+ // Setting a theme programmatically causes standard preferences to display weirdly.
+ // See b/377519324.
+ setTheme(R.style.Theme_SafetyCenterExpressive);
+ }
+
Fragment frag;
+ Intent intent = getIntent();
final boolean maybeOpenSubpage =
SafetyCenterUiFlags.getShowSubpages()
- && getIntent().getAction().equals(ACTION_SAFETY_CENTER);
- if (maybeOpenSubpage && getIntent().hasExtra(EXTRA_SAFETY_SOURCES_GROUP_ID)) {
- String groupId = getIntent().getStringExtra(EXTRA_SAFETY_SOURCES_GROUP_ID);
+ && Objects.equals(intent.getAction(), ACTION_SAFETY_CENTER);
+ if (maybeOpenSubpage && intent.hasExtra(EXTRA_SAFETY_SOURCES_GROUP_ID)) {
+ String groupId = intent.getStringExtra(EXTRA_SAFETY_SOURCES_GROUP_ID);
frag = openRelevantSubpage(groupId);
- } else if (maybeOpenSubpage && getIntent().hasExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY)) {
- String preferenceKey = getIntent().getStringExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY);
+ } else if (maybeOpenSubpage && intent.hasExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY)) {
+ String preferenceKey = intent.getStringExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY);
String groupId = getParentGroupId(preferenceKey);
frag = openRelevantSubpage(groupId);
- } else if (getIntent().getAction().equals(PRIVACY_CONTROLS_ACTION)) {
+ } else if (Objects.equals(intent.getAction(), PRIVACY_CONTROLS_ACTION)) {
setTitle(R.string.privacy_controls_title);
frag = PrivacyControlsFragment.newInstance();
} else {
@@ -306,7 +316,8 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
return PRIVACY_SOURCES_GROUP_ID;
}
- SafetyCenterConfig safetyCenterConfig = mSafetyCenterManager.getSafetyCenterConfig();
+ SafetyCenterConfig safetyCenterConfig =
+ requireNonNull(mSafetyCenterManager).getSafetyCenterConfig();
String[] splitKey;
if (preferenceKey.endsWith(PERSONAL_PROFILE_SUFFIX)) {
splitKey = preferenceKey.split("_" + PERSONAL_PROFILE_SUFFIX);
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
index efbd57080..ed6bc382c 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
@@ -56,6 +56,7 @@ import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData;
import com.android.safetycenter.internaldata.SafetyCenterBundles;
import com.android.safetycenter.resources.SafetyCenterResourcesApk;
+import com.android.settingslib.widget.SettingsThemeHelper;
import kotlin.Unit;
@@ -121,12 +122,16 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
mEntriesGroup = getPreferenceScreen().findPreference(ENTRIES_GROUP_KEY);
mStaticEntriesGroup = getPreferenceScreen().findPreference(STATIC_ENTRIES_GROUP_KEY);
+ Preference spacerPreference = getPreferenceScreen().findPreference(SPACER_KEY);
+ if (SettingsThemeHelper.isExpressiveTheme(requireContext())) {
+ getPreferenceScreen().removePreference(spacerPreference);
+ }
+
if (mIsQuickSettingsFragment) {
getPreferenceScreen().removePreference(mEntriesGroup);
mEntriesGroup = null;
getPreferenceScreen().removePreference(mStaticEntriesGroup);
mStaticEntriesGroup = null;
- Preference spacerPreference = getPreferenceScreen().findPreference(SPACER_KEY);
getPreferenceScreen().removePreference(spacerPreference);
}
getSafetyCenterViewModel().getStatusUiLiveData().observe(this, this::updateStatus);
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
index 9feecf5d4..b5a66da06 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
@@ -22,7 +22,6 @@ import android.safetycenter.SafetyCenterErrorDetails
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.lifecycle.ViewModelProvider
-import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import androidx.recyclerview.widget.RecyclerView
import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
@@ -33,10 +32,11 @@ import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterVi
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel
import com.android.safetycenter.resources.SafetyCenterResourcesApk
+import com.android.settingslib.widget.SettingsBasePreferenceFragment
/** A base fragment that represents a page in Safety Center. */
@RequiresApi(TIRAMISU)
-abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
+abstract class SafetyCenterFragment : SettingsBasePreferenceFragment() {
lateinit var safetyCenterViewModel: SafetyCenterViewModel
lateinit var sameTaskSourceIds: List<String>
@@ -51,11 +51,11 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
override fun onCreateAdapter(
preferenceScreen: PreferenceScreen
- ): RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ ): RecyclerView.Adapter<out RecyclerView.ViewHolder> {
/* The scroll-to-result functionality for settings search is currently implemented only for
* subpages i.e. non expand-and-collapse type entries. Hence, we check that the flag is
* enabled before using an adapter that does the highlighting and scrolling. */
- val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
+ val adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder> =
if (SafetyCenterUiFlags.getShowSubpages()) {
highlightManager.createAdapter(preferenceScreen)
} else {
@@ -80,7 +80,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
safetyCenterViewModel =
ViewModelProvider(
requireActivity(),
- LiveSafetyCenterViewModelFactory(requireActivity().getApplication())
+ LiveSafetyCenterViewModelFactory(requireActivity().getApplication()),
)
.get(SafetyCenterViewModel::class.java)
safetyCenterViewModel.safetyCenterUiLiveData.observe(this) { uiData: SafetyCenterUiData? ->
@@ -177,7 +177,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
safetyCenterViewModel.interactionLogger.recordForIssue(
Action.SAFETY_CENTER_VIEWED,
maybeIssue,
- isDismissed = false
+ isDismissed = false,
)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java
index 2ad282449..d9f45cc08 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java
@@ -24,7 +24,9 @@ import android.permission.PermissionManager;
import androidx.fragment.app.FragmentActivity;
import com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.settingslib.widget.SettingsThemeHelper;
/** Activity for the Safety Center Quick Settings Activity */
public class SafetyCenterQsActivity extends FragmentActivity {
@@ -39,6 +41,12 @@ public class SafetyCenterQsActivity extends FragmentActivity {
return;
}
+ if (SettingsThemeHelper.isExpressiveTheme(this)) {
+ // Safe to set expressive theme here since QS doesn't display vanilla preferences.
+ // See b/377519324.
+ setTheme(R.style.Theme_SafetyCenterQsExpressive);
+ }
+
configureFragment();
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
index fdade2189..2e5ba49ae 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
@@ -28,6 +28,7 @@ import com.android.permissioncontroller.safetycenter.ui.SafetyBrandChipPreferenc
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
import com.android.safetycenter.resources.SafetyCenterResourcesApk
import com.android.settingslib.widget.FooterPreference
+import com.android.settingslib.widget.SettingsThemeHelper
/** A fragment that represents a generic subpage in Safety Center. */
@RequiresApi(UPSIDE_DOWN_CAKE)
@@ -45,15 +46,16 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
setPreferencesFromResource(R.xml.safety_center_subpage, rootKey)
sourceGroupId = requireArguments().getString(SOURCE_GROUP_ID_KEY)!!
- subpageBrandChip = getPreferenceScreen().findPreference(BRAND_CHIP_KEY)!!
- subpageIllustration = getPreferenceScreen().findPreference(ILLUSTRATION_KEY)!!
- subpageIssueGroup = getPreferenceScreen().findPreference(ISSUE_GROUP_KEY)!!
- subpageEntryGroup = getPreferenceScreen().findPreference(ENTRY_GROUP_KEY)!!
- subpageFooter = getPreferenceScreen().findPreference(FOOTER_KEY)!!
+ subpageBrandChip = preferenceScreen.findPreference(BRAND_CHIP_KEY)!!
+ subpageIllustration = preferenceScreen.findPreference(ILLUSTRATION_KEY)!!
+ subpageIssueGroup = preferenceScreen.findPreference(ISSUE_GROUP_KEY)!!
+ subpageEntryGroup = preferenceScreen.findPreference(ENTRY_GROUP_KEY)!!
+ subpageFooter = preferenceScreen.findPreference(FOOTER_KEY)!!
subpageBrandChip.setupListener(requireActivity(), safetyCenterSessionId)
setupIllustration()
setupFooter()
+ maybeRemoveSpacer()
prerenderCurrentSafetyCenterData()
}
@@ -80,7 +82,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
return
}
- requireActivity().setTitle(entryGroup.title)
+ requireActivity().title = entryGroup.title
updateSafetyCenterIssues(uiData)
updateSafetyCenterEntries(entryGroup)
}
@@ -91,7 +93,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
val drawable = SafetyCenterResourcesApk(context).getDrawableByName(resName, context.theme)
if (drawable == null) {
Log.w(TAG, "$sourceGroupId doesn't have any matching illustration")
- subpageIllustration.setVisible(false)
+ subpageIllustration.isVisible = false
}
subpageIllustration.illustrationDrawable = drawable
@@ -102,12 +104,19 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
val footerText = SafetyCenterResourcesApk(requireContext()).getStringByName(resName)
if (footerText.isEmpty()) {
Log.w(TAG, "$sourceGroupId doesn't have any matching footer")
- subpageFooter.setVisible(false)
+ subpageFooter.isVisible = false
}
// footer is ordered last by default
// in order to keep a spacer after the footer, footer needs to be the second from last
- subpageFooter.setOrder(Int.MAX_VALUE - 2)
- subpageFooter.setSummary(footerText)
+ subpageFooter.order = Int.MAX_VALUE - 2
+ subpageFooter.summary = footerText
+ }
+
+ private fun maybeRemoveSpacer() {
+ if (SettingsThemeHelper.isExpressiveTheme(requireContext())) {
+ val spacerPreference = preferenceScreen.findPreference<SpacerPreference>(SPACER_KEY)!!
+ preferenceScreen.removePreference(spacerPreference)
+ }
}
private fun updateSafetyCenterIssues(uiData: SafetyCenterUiData?) {
@@ -131,7 +140,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
subpageIssues,
subpageDismissedIssues,
uiData.resolvedIssues,
- requireActivity().getTaskId()
+ requireActivity().taskId,
)
}
@@ -145,23 +154,24 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
PendingIntentSender.getTaskIdForEntry(
entry.id,
sameTaskSourceIds,
- requireActivity()
+ requireActivity(),
),
entry,
- safetyCenterViewModel
+ safetyCenterViewModel,
)
)
}
}
companion object {
- private val TAG: String = SafetyCenterSubpageFragment::class.java.simpleName
- private const val BRAND_CHIP_KEY: String = "subpage_brand_chip"
- private const val ILLUSTRATION_KEY: String = "subpage_illustration"
- private const val ISSUE_GROUP_KEY: String = "subpage_issue_group"
- private const val ENTRY_GROUP_KEY: String = "subpage_entry_group"
- private const val FOOTER_KEY: String = "subpage_footer"
- private const val SOURCE_GROUP_ID_KEY: String = "source_group_id"
+ private val TAG = SafetyCenterSubpageFragment::class.java.simpleName
+ private const val BRAND_CHIP_KEY = "subpage_brand_chip"
+ private const val ILLUSTRATION_KEY = "subpage_illustration"
+ private const val ISSUE_GROUP_KEY = "subpage_issue_group"
+ private const val ENTRY_GROUP_KEY = "subpage_entry_group"
+ private const val FOOTER_KEY = "subpage_footer"
+ private const val SPACER_KEY = "subpage_spacer"
+ private const val SOURCE_GROUP_ID_KEY = "source_group_id"
/** Creates an instance of SafetyCenterSubpageFragment with the arguments set */
@JvmStatic
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt
index 5acb27131..3976fa7e3 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt
@@ -25,11 +25,12 @@ import androidx.annotation.RequiresApi
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.R
+import com.android.settingslib.widget.GroupSectionDividerMixin
/** A preference that displays the illustration on a Safety Center subpage. */
@RequiresApi(UPSIDE_DOWN_CAKE)
internal class SafetyIllustrationPreference(context: Context, attrs: AttributeSet) :
- Preference(context, attrs) {
+ Preference(context, attrs), GroupSectionDividerMixin {
init {
layoutResource = R.layout.preference_illustration
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
index 811841845..abf159955 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
@@ -40,6 +40,7 @@ import com.android.permissioncontroller.R;
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData;
import com.android.permissioncontroller.safetycenter.ui.view.StatusCardView;
+import com.android.settingslib.widget.GroupSectionDividerMixin;
import kotlin.Pair;
@@ -48,7 +49,8 @@ import java.util.Objects;
/** Preference which displays a visual representation of {@link SafetyCenterStatus}. */
@RequiresApi(TIRAMISU)
-public class SafetyStatusPreference extends Preference implements ComparablePreference {
+public class SafetyStatusPreference extends Preference
+ implements ComparablePreference, GroupSectionDividerMixin {
private static final String TAG = "SafetyStatusPreference";
@@ -82,7 +84,8 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
}
Context context = getContext();
- StatusCardView statusCardView = (StatusCardView) holder.itemView;
+ StatusCardView statusCardView = holder.itemView.requireViewById(R.id.status_card);
+
configureButtons(context, statusCardView);
statusCardView
.getTitleAndSummaryContainerView()
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
index 030b67be9..7f619d1ca 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
@@ -55,6 +55,7 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) :
}
private var maxKnownToolbarHeight = 0
+
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
val spacer = holder.itemView
@@ -74,7 +75,7 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) :
oldLeft: Int,
oldTop: Int,
oldRight: Int,
- oldBottom: Int
+ oldBottom: Int,
) {
adjustHeight(spacer)
}
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/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt
index 99aa4baa9..15a37532f 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt
@@ -16,19 +16,27 @@
package com.android.permissioncontroller.tests.mocking.hibernation
+import android.Manifest
import android.app.job.JobScheduler
+import android.content.ComponentName
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.database.ContentObserver
import android.net.Uri
+import android.os.Binder
import android.os.Build
import android.os.SystemClock
+import android.os.UserHandle
import android.os.UserManager
import android.preference.PreferenceManager
import android.provider.DeviceConfig
import android.provider.Settings
+import android.telecom.PhoneAccountHandle
+import android.telecom.TelecomManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
@@ -37,14 +45,19 @@ import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.hibernation.HibernationBroadcastReceiver
import com.android.permissioncontroller.hibernation.ONE_DAY_MS
-import com.android.permissioncontroller.hibernation.PREF_KEY_SYSTEM_TIME_SNAPSHOT
import com.android.permissioncontroller.hibernation.PREF_KEY_ELAPSED_REALTIME_SNAPSHOT
import com.android.permissioncontroller.hibernation.PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING
+import com.android.permissioncontroller.hibernation.PREF_KEY_SYSTEM_TIME_SNAPSHOT
import com.android.permissioncontroller.hibernation.SNAPSHOT_UNINITIALIZED
import com.android.permissioncontroller.hibernation.getStartTimeOfUnusedAppTracking
+import com.android.permissioncontroller.hibernation.isPackageHibernationExemptBySystem
+import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
+import com.android.permissioncontroller.permission.utils.ContextCompat
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -71,11 +84,14 @@ class HibernationPolicyTest {
private val application = Mockito.mock(PermissionControllerApplication::class.java)
private const val USER_SETUP_INCOMPLETE = 0
private const val USER_SETUP_COMPLETE = 1
+ private const val TEST_PKG_NAME = "test.package"
}
@Mock lateinit var jobScheduler: JobScheduler
@Mock lateinit var context: Context
@Mock lateinit var userManager: UserManager
+ @Mock lateinit var packageManager: PackageManager
+ @Mock lateinit var telecomManager: TelecomManager
@Mock lateinit var contentResolver: ContentResolver
private lateinit var realContext: Context
@@ -83,6 +99,7 @@ class HibernationPolicyTest {
private lateinit var sharedPreferences: SharedPreferences
private lateinit var mockitoSession: MockitoSession
private lateinit var filesDir: File
+ private lateinit var userHandle: UserHandle
@Before
fun setup() {
@@ -100,11 +117,15 @@ class HibernationPolicyTest {
`when`(Settings.Secure.getUriFor(any())).thenReturn(Mockito.mock(Uri::class.java))
realContext = ApplicationProvider.getApplicationContext()
+ userHandle = UserHandle.getUserHandleForUid(Binder.getCallingUid())
sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(realContext.applicationContext)
`when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPreferences)
`when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
+ `when`(application.getSystemService(TelecomManager::class.java)).thenReturn(telecomManager)
+ `when`(application.packageManager).thenReturn(packageManager)
+ `when`(application.applicationContext).thenReturn(context)
filesDir = realContext.cacheDir
`when`(application.filesDir).thenReturn(filesDir)
@@ -207,6 +228,30 @@ class HibernationPolicyTest {
.isNotEqualTo(systemTimeSnapshot)
}
+ @Test
+ @Ignore("b/371061181")
+ // This method under test initializes several SmartAsyncMediatorLiveData classes which run code
+ // on GlobalScope which the unit test has no control over. This can lead to the code running
+ // during other tests which may not have the right static mocks.
+ // Until this is fixed, this test should be ignored to prevent flaky test faliures.
+ fun isPackageExemptBySystem_isCallingApp_returnsTrue() = runBlocking<Unit> {
+ val pkgInfo = makePackageInfo(TEST_PKG_NAME)
+
+ `when`(context.checkPermission(
+ eq(Manifest.permission.MANAGE_OWN_CALLS), anyInt(), eq(pkgInfo.uid)))
+ .thenReturn(PERMISSION_GRANTED)
+ `when`(context.checkPermission(
+ eq(Manifest.permission.RECORD_AUDIO), anyInt(), eq(pkgInfo.uid)))
+ .thenReturn(PERMISSION_GRANTED)
+ `when`(context.checkPermission(
+ eq(Manifest.permission.WRITE_CALL_LOG), anyInt(), eq(pkgInfo.uid)))
+ .thenReturn(PERMISSION_GRANTED)
+ `when`(telecomManager.selfManagedPhoneAccounts).thenReturn(
+ listOf(PhoneAccountHandle(ComponentName(TEST_PKG_NAME, "Service"), "id")))
+
+ assertThat(isPackageHibernationExemptBySystem(pkgInfo, userHandle)).isTrue()
+ }
+
private fun assertAdjustedTime(systemTimeSnapshot: Long, realtimeSnapshot: Long) {
val newStartTimeOfUnusedAppTracking =
sharedPreferences.getLong(
@@ -223,4 +268,23 @@ class HibernationPolicyTest {
assertThat(newRealtimeSnapshot).isNotEqualTo(SNAPSHOT_UNINITIALIZED)
assertThat(newRealtimeSnapshot).isAtLeast(realtimeSnapshot)
}
+
+ private fun makePackageInfo(packageName: String): LightPackageInfo {
+ return LightPackageInfo(
+ packageName,
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ 0 /* uid */,
+ Build.VERSION_CODES.CUR_DEVELOPMENT,
+ false /* isInstantApp */,
+ true /* enabled */,
+ 0 /* appFlags */,
+ 0 /* firstInstallTime */,
+ 0 /* lastUpdateTime */,
+ false /* areAttributionsUserVisible */,
+ emptyMap() /* attributionTagsToLabels */,
+ ContextCompat.DEVICE_ID_DEFAULT
+ )
+ }
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionGroupUsageDetailsUseCaseTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionGroupUsageDetailsUseCaseTest.kt
index da2d05f63..cb36de2b1 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionGroupUsageDetailsUseCaseTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionGroupUsageDetailsUseCaseTest.kt
@@ -34,6 +34,7 @@ import com.android.permissioncontroller.permission.domain.model.v31.PermissionTi
import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModelWrapper
import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageDetailsUseCase
import com.android.permissioncontroller.permission.domain.usecase.v31.TELECOM_PACKAGE
+import com.android.permissioncontroller.permission.utils.LocationUtils
import com.android.permissioncontroller.pm.data.model.v31.PackageAttributionModel
import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel
import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository
@@ -56,9 +57,11 @@ import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
@@ -72,7 +75,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
private var mockitoSession: MockitoSession? = null
private lateinit var packageInfos: MutableMap<String, PackageInfoModel>
- private val currentUser = android.os.Process.myUserHandle()
+ private val currentUser = UserHandle.of(0)
private val privateProfile = UserHandle.of(10)
private val guestUser = UserHandle.of(20)
@@ -88,11 +91,13 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
mockitoSession =
ExtendedMockito.mockitoSession()
.mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(LocationUtils::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(PermissionControllerApplication.get()).thenReturn(application)
whenever(application.applicationContext).thenReturn(context)
+ whenever(LocationUtils.isLocationProvider(Mockito.any(), Mockito.any())).thenReturn(false)
packageInfos =
mapOf(
@@ -102,7 +107,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
systemPackageName to
getPackageInfoModel(
systemPackageName,
- applicationFlags = ApplicationInfo.FLAG_SYSTEM
+ applicationFlags = ApplicationInfo.FLAG_SYSTEM,
),
)
.toMutableMap()
@@ -124,7 +129,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
emit(
listOf(
DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
- DiscretePackageOpsModel(guestUserPkgName, guestUser.identifier, appOpEvents)
+ DiscretePackageOpsModel(guestUserPkgName, guestUser.identifier, appOpEvents),
)
)
}
@@ -150,11 +155,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
emit(
listOf(
DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
- DiscretePackageOpsModel(
- testPackageName,
- privateProfile.identifier,
- appOpEvents
- ),
+ DiscretePackageOpsModel(testPackageName, privateProfile.identifier, appOpEvents),
)
)
}
@@ -168,8 +169,8 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
currentUserProfiles =
listOf(currentUser.identifier, privateProfile.identifier),
quietUserProfiles = listOf(privateProfile.identifier),
- showInQuiteModeProfiles = listOf(privateProfile.identifier)
- )
+ showInQuiteModeProfiles = listOf(privateProfile.identifier),
+ ),
)
val permissionTimelineUsages = getResult(underTest, this)
@@ -190,11 +191,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
emit(
listOf(
DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
- DiscretePackageOpsModel(
- testPackageName,
- privateProfile.identifier,
- appOpEvents
- ),
+ DiscretePackageOpsModel(testPackageName, privateProfile.identifier, appOpEvents),
)
)
}
@@ -208,7 +205,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
currentUserProfiles =
listOf(currentUser.identifier, privateProfile.identifier),
quietUserProfiles = listOf(privateProfile.identifier),
- )
+ ),
)
val permissionTimelineUsages = getResult(underTest, this)
@@ -254,7 +251,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -280,7 +277,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -303,23 +300,23 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(1),
- MINUTES.toMillis(1)
+ MINUTES.toMillis(1),
),
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(2),
- MINUTES.toMillis(1)
+ MINUTES.toMillis(1),
),
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(3),
- MINUTES.toMillis(2)
+ MINUTES.toMillis(2),
),
)
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -344,23 +341,19 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
DiscreteOpModel(
AppOpsManager.OPSTR_FINE_LOCATION,
hours3 + MINUTES.toMillis(59),
- -1
+ -1,
),
DiscreteOpModel(
AppOpsManager.OPSTR_FINE_LOCATION,
hours4 + MINUTES.toMillis(0),
- -1
- ),
- DiscreteOpModel(
- AppOpsManager.OPSTR_FINE_LOCATION,
- hours4 + MINUTES.toMillis(1),
- -1
+ -1,
),
+ DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, hours4 + MINUTES.toMillis(1), -1),
)
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -383,18 +376,18 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(1),
- MINUTES.toMillis(3)
+ MINUTES.toMillis(3),
),
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(3),
- MINUTES.toMillis(2)
+ MINUTES.toMillis(2),
),
)
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -420,7 +413,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -448,18 +441,18 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(1),
- MINUTES.toMillis(3)
+ MINUTES.toMillis(3),
),
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(5),
- MINUTES.toMillis(5)
+ MINUTES.toMillis(5),
),
)
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -483,13 +476,11 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
@Test
fun singleDiscreteAccess() = runTest {
val appOpEvents =
- listOf(
- DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1),
- )
+ listOf(DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1))
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -512,13 +503,13 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(1),
- MINUTES.toMillis(3)
- ),
+ MINUTES.toMillis(3),
+ )
)
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -541,29 +532,29 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(1),
- MINUTES.toMillis(2)
+ MINUTES.toMillis(2),
),
// This entry says the camera was accessed for 15 minutes starting at minute 3
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(3),
- MINUTES.toMillis(15)
+ MINUTES.toMillis(15),
),
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(4),
- MINUTES.toMillis(1)
+ MINUTES.toMillis(1),
),
DiscreteOpModel(
AppOpsManager.OPSTR_CAMERA,
MINUTES.toMillis(6),
- MINUTES.toMillis(1)
+ MINUTES.toMillis(1),
),
)
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -582,9 +573,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
@Test
fun verifyUserSensitiveFlags() = runTest {
val appOpEvents =
- listOf(
- DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1),
- )
+ listOf(DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1))
val discretePackageOps = flow {
emit(
listOf(
@@ -620,13 +609,11 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
@Test
fun verifyNotUserSensitiveFlagsForSystemPackage() = runTest {
val appOpEvents =
- listOf(
- DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1),
- )
+ listOf(DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1))
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -651,14 +638,12 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
@Test
fun verifyCameraUserSensitiveFlagsForTelecomPackage() = runTest {
val appOpEvents =
- listOf(
- DiscreteOpModel(AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(1), -1),
- )
+ listOf(DiscreteOpModel(AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(1), -1))
packageInfos[TELECOM_PACKAGE] = getPackageInfoModel(TELECOM_PACKAGE)
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(TELECOM_PACKAGE, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(TELECOM_PACKAGE, currentUser.identifier, appOpEvents)
)
)
}
@@ -683,14 +668,12 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
@Test
fun verifyLocationUserSensitiveFlagsForTelecomPackage() = runTest {
val appOpEvents =
- listOf(
- DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1),
- )
+ listOf(DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1))
packageInfos[TELECOM_PACKAGE] = getPackageInfoModel(TELECOM_PACKAGE)
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(TELECOM_PACKAGE, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(TELECOM_PACKAGE, currentUser.identifier, appOpEvents)
)
)
}
@@ -714,7 +697,75 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
}
@Test
- fun verifyAttributionTagsAreGroupedAndClustered() = runTest {
+ @RequiresFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_PERMISSION_TIMELINE_ATTRIBUTION_LABEL_FIX
+ )
+ @Ignore("b/365004787")
+ fun verifyAccessIsNotGroupedByAttributionLabelAndClustered() = runTest {
+ // The package is not a location provider.
+ val appOpEvents =
+ listOf(
+ // These entries should be clustered even though they have
+ // different attribution labels.
+ DiscreteOpModel(
+ AppOpsManager.OPSTR_COARSE_LOCATION,
+ MINUTES.toMillis(1),
+ -1,
+ "tag1",
+ ),
+ DiscreteOpModel(
+ AppOpsManager.OPSTR_COARSE_LOCATION,
+ MINUTES.toMillis(2),
+ -1,
+ "tag1",
+ ),
+ DiscreteOpModel(
+ AppOpsManager.OPSTR_COARSE_LOCATION,
+ MINUTES.toMillis(3),
+ -1,
+ "tag3",
+ ),
+ DiscreteOpModel(
+ AppOpsManager.OPSTR_COARSE_LOCATION,
+ MINUTES.toMillis(4),
+ -1,
+ "tag2",
+ ),
+ )
+ val discretePackageOps = flow {
+ emit(
+ listOf(
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
+ )
+ )
+ }
+ val packageAttributions = mutableMapOf<String, PackageAttributionModel>()
+ // tag1 and tag3 refers to same attribution label.
+ val attributionTagToLabelRes = mapOf("tag1" to 100, "tag2" to 200, "tag3" to 100)
+ val attributionsMap = mapOf(100 to "Tag1 Label", 200 to "Tag2 Label")
+ packageAttributions[testPackageName] =
+ PackageAttributionModel(
+ testPackageName,
+ true,
+ attributionTagToLabelRes,
+ attributionsMap,
+ )
+
+ val underTest =
+ getPermissionGroupUsageDetailsUseCase(
+ LOCATION_PERMISSION_GROUP,
+ discretePackageOps,
+ packageRepository = FakePackageRepository(packageInfos, packageAttributions),
+ attributionLabelFix = true,
+ )
+ val permissionTimelineUsages = getResult(underTest, this)
+
+ Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1)
+ }
+
+ @Test
+ fun verifyAccessIsGroupedByAttributionLabelAndClustered() = runTest {
+ whenever(LocationUtils.isLocationProvider(Mockito.any(), Mockito.any())).thenReturn(true)
val appOpEvents =
listOf(
// These 3 entries should be grouped and clustered.
@@ -722,38 +773,38 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
AppOpsManager.OPSTR_COARSE_LOCATION,
MINUTES.toMillis(1),
-1,
- "tag1"
+ "tag1",
),
DiscreteOpModel(
AppOpsManager.OPSTR_COARSE_LOCATION,
MINUTES.toMillis(2),
-1,
- "tag1"
+ "tag1",
),
DiscreteOpModel(
AppOpsManager.OPSTR_COARSE_LOCATION,
MINUTES.toMillis(3),
-1,
- "tag3"
+ "tag3",
),
// The access at minute 4 should not be grouped or clustered.
DiscreteOpModel(
AppOpsManager.OPSTR_COARSE_LOCATION,
MINUTES.toMillis(4),
-1,
- "tag2"
+ "tag2",
),
DiscreteOpModel(
AppOpsManager.OPSTR_COARSE_LOCATION,
MINUTES.toMillis(8),
-1,
- "tag2"
+ "tag2",
),
)
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -766,14 +817,14 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
testPackageName,
true,
attributionTagToLabelRes,
- attributionsMap
+ attributionsMap,
)
val underTest =
getPermissionGroupUsageDetailsUseCase(
LOCATION_PERMISSION_GROUP,
discretePackageOps,
- packageRepository = FakePackageRepository(packageInfos, packageAttributions)
+ packageRepository = FakePackageRepository(packageInfos, packageAttributions),
)
val permissionTimelineUsages = getResult(underTest, this)
@@ -794,6 +845,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
}
@Test
+ @Ignore("b/365004787")
@RequiresFlagsEnabled(Flags.FLAG_LOCATION_BYPASS_PRIVACY_DASHBOARD_ENABLED)
fun emergencyAccessesAreNotClusteredWithRegularAccesses() = runTest {
Assume.assumeTrue(SdkLevel.isAtLeastV())
@@ -805,7 +857,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -829,7 +881,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
val discretePackageOps = flow {
emit(
listOf(
- DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents),
+ DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)
)
)
}
@@ -843,7 +895,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
private fun TestScope.getResult(
useCase: GetPermissionGroupUsageDetailsUseCase,
- coroutineScope: CoroutineScope
+ coroutineScope: CoroutineScope,
): List<PermissionTimelineUsageModel> {
val usages by collectLastValue(useCase(coroutineScope))
return (usages as PermissionTimelineUsageModelWrapper.Success).timelineUsageModels
@@ -854,7 +906,8 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
discreteUsageFlow: Flow<List<DiscretePackageOpsModel>>,
permissionFlags: Map<String, Int> = emptyMap(),
userRepository: UserRepository = FakeUserRepository(listOf(currentUser.identifier)),
- packageRepository: PackageRepository = FakePackageRepository(packageInfos)
+ packageRepository: PackageRepository = FakePackageRepository(packageInfos),
+ attributionLabelFix: Boolean = false,
): GetPermissionGroupUsageDetailsUseCase {
val permissionRepository = FakePermissionRepository(permissionFlags)
val appOpUsageRepository = FakeAppOpRepository(emptyFlow(), discreteUsageFlow)
@@ -865,7 +918,8 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
permissionRepository,
appOpUsageRepository,
roleRepository,
- userRepository
+ userRepository,
+ attributionLabelFix,
)
}
@@ -877,7 +931,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest {
listOf(
PackageInfo.REQUESTED_PERMISSION_GRANTED,
PackageInfo.REQUESTED_PERMISSION_GRANTED,
- PackageInfo.REQUESTED_PERMISSION_GRANTED
+ PackageInfo.REQUESTED_PERMISSION_GRANTED,
),
applicationFlags: Int = 0,
) = PackageInfoModel(packageName, requestedPermissions, permissionsFlags, applicationFlags)
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt
index 31e81e588..edaea9aba 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt
@@ -32,6 +32,7 @@ import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOps
import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageDetailsUseCase
import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState
import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModelV2
+import com.android.permissioncontroller.permission.utils.LocationUtils
import com.android.permissioncontroller.permission.utils.StringUtils
import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel
@@ -93,6 +94,7 @@ class PermissionUsageDetailsViewModelTest {
.mockStatic(DeviceUtils::class.java)
.mockStatic(StringUtils::class.java)
.mockStatic(Flags::class.java)
+ .mockStatic(LocationUtils::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
@@ -109,6 +111,7 @@ class PermissionUsageDetailsViewModelTest {
)
)
.thenReturn("Duration Summary")
+ whenever(LocationUtils.isLocationProvider(any(), any())).thenReturn(false)
packageInfos =
mapOf(
@@ -500,7 +503,8 @@ class PermissionUsageDetailsViewModelTest {
permissionRepository,
appOpUsageRepository,
roleRepository,
- userRepository
+ userRepository,
+ false,
)
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/WorkPolicyInfoTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/WorkPolicyInfoTest.kt
index bbbf0e86a..38baee3ed 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/WorkPolicyInfoTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/WorkPolicyInfoTest.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.os.Build
+import android.os.Process
import android.os.UserManager
import android.safetycenter.SafetyCenterManager
import android.safetycenter.SafetyEvent
@@ -118,6 +119,8 @@ class WorkPolicyInfoTest {
whenever(PermissionControllerApplication.get()).thenReturn(application)
whenever(application.applicationContext).thenReturn(application)
workPolicyInfo = WorkPolicyInfo(mockWorkPolicyUtils)
+ val userId = Process.myUserHandle().identifier
+ whenever(mockWorkPolicyUtils.managedProfileUserId).thenReturn(userId)
}
@After
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt
index ec6ab4eff..da1430c0a 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt
@@ -16,9 +16,17 @@
package com.android.permissioncontroller.tests.mocking.role.model
+import android.app.AppOpsManager
+import android.content.pm.PackageManager
+import android.content.pm.PermissionInfo
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.permissioncontroller.role.model.RoleParserInitializer
+import com.android.role.controller.model.AppOp
+import com.android.role.controller.model.Permission
+import com.android.role.controller.model.PermissionSet
+import com.android.role.controller.model.Role
import com.android.role.controller.model.RoleParser
import org.junit.BeforeClass
import org.junit.Test
@@ -37,9 +45,10 @@ class RoleParserTest {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
private val uiAutomation = instrumentation.uiAutomation
private val targetContext = instrumentation.targetContext
+ private val packageManager = targetContext.packageManager
@Test
- fun testParseRolesWithValidation() {
+ fun parseRoles() {
// We may need to call privileged APIs to determine availability of things.
uiAutomation.adoptShellPermissionIdentity()
try {
@@ -48,4 +57,129 @@ class RoleParserTest {
uiAutomation.dropShellPermissionIdentity()
}
}
+
+ @Test
+ fun validateRoles() {
+ // We may need to call privileged APIs to determine availability of things.
+ uiAutomation.adoptShellPermissionIdentity()
+ try {
+ val xml = RoleParser(targetContext).parseRolesXml()
+ requireNotNull(xml)
+ validateRoles(xml.first, xml.second)
+ } finally {
+ uiAutomation.dropShellPermissionIdentity()
+ }
+ }
+
+ fun validateRoles(permissionSets: Map<String, PermissionSet>, roles: Map<String, Role>) {
+ for (permissionSet in permissionSets.values) {
+ for (permission in permissionSet.permissions) {
+ validatePermission(permission)
+ }
+ }
+
+ for (role in roles.values) {
+ if (!role.isAvailableByFeatureFlagAndSdkVersion) {
+ continue
+ }
+
+ for (requiredComponent in role.requiredComponents) {
+ val permission = requiredComponent.permission
+ if (permission != null) {
+ validatePermission(permission)
+ }
+ }
+
+ for (permission in role.permissions) {
+ validatePermission(permission)
+ }
+
+ for (appOp in role.appOps) {
+ validateAppOp(appOp)
+ }
+
+ for (appOpPermission in role.appOpPermissions) {
+ validateAppOpPermission(appOpPermission)
+ }
+
+ for (preferredActivity in role.preferredActivities) {
+ require(preferredActivity.activity in role.requiredComponents) {
+ "<activity> of <preferred-activity> not required in <required-components>," +
+ " role: ${role.name}, preferred activity: $preferredActivity"
+ }
+ }
+ }
+ }
+
+ private fun validatePermission(permission: Permission) {
+ if (!permission.isAvailableAsUser(Process.myUserHandle(), targetContext)) {
+ return
+ }
+ validatePermission(permission.name, true)
+ }
+
+ private fun validatePermission(permissionName: String) {
+ validatePermission(permissionName, false)
+ }
+
+ private fun validatePermission(permissionName: String, enforceIsRuntimeOrRole: Boolean) {
+ val isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ // Skip validation for car permissions which may not be available on all build targets.
+ if (!isAutomotive && permissionName.startsWith("android.car")) {
+ return
+ }
+
+ val permissionInfo =
+ try {
+ packageManager.getPermissionInfo(permissionName, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ throw IllegalArgumentException("Unknown permission: $permissionName", e)
+ }
+
+ if (enforceIsRuntimeOrRole) {
+ require(
+ permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS ||
+ permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_ROLE ==
+ PermissionInfo.PROTECTION_FLAG_ROLE
+ ) {
+ "Permission is not a runtime or role permission: $permissionName"
+ }
+ }
+ }
+
+ private fun validateAppOpPermission(appOpPermission: Permission) {
+ if (!appOpPermission.isAvailableAsUser(Process.myUserHandle(), targetContext)) {
+ return
+ }
+ validateAppOpPermission(appOpPermission.name)
+ }
+
+ private fun validateAppOpPermission(permissionName: String) {
+ val permissionInfo =
+ try {
+ packageManager.getPermissionInfo(permissionName, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ throw IllegalArgumentException("Unknown app op permission: $permissionName", e)
+ }
+ require(
+ permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_APPOP ==
+ PermissionInfo.PROTECTION_FLAG_APPOP
+ ) {
+ "Permission is not an app op permission: $permissionName"
+ }
+ }
+
+ private fun validateAppOp(appOp: AppOp) {
+ if (!appOp.isAvailableByFeatureFlagAndSdkVersion) {
+ return
+ }
+ // This throws IllegalArgumentException if app op is unknown.
+ val permissionName = AppOpsManager.opToPermission(appOp.name)
+ if (permissionName != null) {
+ val permissionInfo = packageManager.getPermissionInfo(permissionName, 0)
+ require(permissionInfo.protection != PermissionInfo.PROTECTION_DANGEROUS) {
+ "App op has an associated runtime permission: ${appOp.name}"
+ }
+ }
+ }
}
diff --git a/PermissionController/tests/permissionui/Android.bp b/PermissionController/tests/permissionui/Android.bp
index 6e1839d1d..5f177f40c 100644
--- a/PermissionController/tests/permissionui/Android.bp
+++ b/PermissionController/tests/permissionui/Android.bp
@@ -47,7 +47,7 @@ android_test {
"androidx.test.ext.truth",
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
- "com.android.permission.flags-aconfig-java",
+ "com.android.permission.flags-aconfig-java-export",
"compatibility-device-util-axt",
"flag-junit",
"permission-test-util-lib",
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/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt
index eb7be564b..b38f5f40a 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt
@@ -26,10 +26,12 @@ import androidx.test.uiautomator.By
import com.android.compatibility.common.util.SystemUtil.eventually
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
+import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull
import com.android.permissioncontroller.permissionui.getUsageCountsFromUi
import com.android.permissioncontroller.permissionui.wakeUpScreen
import com.google.common.truth.Truth.assertThat
import org.junit.After
+import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -47,6 +49,7 @@ class ManageCustomPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
private val PERM_LABEL = "Permission A"
private val PERM = "com.android.permissioncontroller.tests.A"
private val ADDITIONAL_PERMISSIONS_LABEL = "Additional permissions"
+ private val BODY_SENSORS_LABEL = "Body sensors"
@Before
fun setup() {
@@ -92,6 +95,14 @@ class ManageCustomPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
eventually { assertThat(getUsageCountsFromUi(PERM_LABEL)).isEqualTo(original) }
}
+ @Test
+ fun bodySensorsEitherDisplayedInMainPageOrInAdditional() {
+ if (waitFindObjectOrNull(By.textContains(BODY_SENSORS_LABEL)) == null) {
+ waitFindObject(By.textContains(ADDITIONAL_PERMISSIONS_LABEL)).click()
+ assertNotNull(waitFindObjectOrNull(By.textContains(BODY_SENSORS_LABEL)))
+ }
+ }
+
@After
fun tearDown() {
uninstallApp(DEFINER_PKG)
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt
index 1ad876245..fcce09450 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt
@@ -23,6 +23,8 @@ import android.permission.cts.PermissionUtils.grantPermission
import android.permission.cts.PermissionUtils.install
import android.permission.cts.PermissionUtils.revokePermission
import android.permission.cts.PermissionUtils.uninstallApp
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
@@ -30,17 +32,23 @@ import com.android.compatibility.common.util.SystemUtil.eventually
import com.android.compatibility.common.util.SystemUtil.getEventually
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull
+import com.android.permission.flags.Flags
import com.android.permissioncontroller.permissionui.getUsageCountsFromUi
import com.android.permissioncontroller.permissionui.wakeUpScreen
import com.google.common.truth.Truth.assertThat
import org.junit.After
+import org.junit.Assert.assertNull
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/** Simple tests for {@link ManageStandardPermissionsFragment} */
@RunWith(AndroidJUnit4::class)
class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
+
+ @JvmField @Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@Before
fun setup() {
wakeUpScreen()
@@ -117,7 +125,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
assertThat(afterInstall.granted).isEqualTo(original.granted)
assertThat(afterInstall.total).isEqualTo(original.total + 1)
},
- TIMEOUT
+ TIMEOUT,
)
}
@@ -127,13 +135,13 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(LOCATION_USER_APK)
eventually(
{ assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL)).isNotEqualTo(original) },
- TIMEOUT
+ TIMEOUT,
)
uninstallApp(LOCATION_USER_PKG)
eventually(
{ assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL)).isEqualTo(original) },
- TIMEOUT
+ TIMEOUT,
)
}
@@ -147,7 +155,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).total)
.isEqualTo(original.total + 1)
},
- TIMEOUT
+ TIMEOUT,
)
grantPermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
@@ -170,7 +178,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).granted)
.isNotEqualTo(original.granted)
},
- TIMEOUT
+ TIMEOUT,
)
revokePermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
@@ -190,7 +198,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
{
assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore + 1)
},
- TIMEOUT
+ TIMEOUT,
)
}
@@ -202,13 +210,13 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(ADDITIONAL_USER_APK)
eventually(
{ assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore) },
- TIMEOUT
+ TIMEOUT,
)
uninstallApp(ADDITIONAL_USER_PKG)
eventually(
{ assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore) },
- TIMEOUT
+ TIMEOUT,
)
}
@@ -220,16 +228,23 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(ADDITIONAL_USER_APK)
eventually(
{ assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore) },
- TIMEOUT
+ TIMEOUT,
)
uninstallApp(ADDITIONAL_DEFINER_PKG)
eventually(
{ assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore) },
- TIMEOUT
+ TIMEOUT,
)
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DECLUTTERED_PERMISSION_MANAGER_ENABLED)
+ fun noUnusedPermissionGroupDisplayedInTheMainPage() {
+ eventually { assertNull(waitFindObjectOrNull(By.hasChild(By.textStartsWith("0 of 0")))) }
+ eventually { assertNull(waitFindObjectOrNull(By.hasChild(By.textStartsWith("0/0")))) }
+ }
+
companion object {
private val LOG_TAG = ManageStandardPermissionsFragmentTest::class.java.simpleName
diff --git a/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml b/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml
new file mode 100644
index 000000000..de033ac44
--- /dev/null
+++ b/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml
@@ -0,0 +1,158 @@
+<!--
+ ~ 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.
+ -->
+
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="AndroidLockScreenSources"
+ title="@com.android.safetycenter.resources:string/lock_screen_sources_title"
+ summary="@com.android.safetycenter.resources:string/lock_screen_sources_summary">
+ <dynamic-safety-source
+ id="AndroidLockScreen"
+ packageName="com.android.settings"
+ profile="primary_profile_only"
+ title="@com.android.safetycenter.resources:string/lock_screen_title"
+ summary="@com.android.safetycenter.resources:string/lock_screen_summary_disabled"
+ searchTerms="@com.android.safetycenter.resources:string/lock_screen_search_terms"
+ initialDisplayState="disabled"
+ notificationsAllowed="true"/>
+ <dynamic-safety-source
+ id="AndroidBiometrics"
+ packageName="com.android.settings"
+ profile="all_profiles"
+ title="@com.android.safetycenter.resources:string/biometrics_title"
+ titleForWork="@com.android.safetycenter.resources:string/biometrics_title_for_work"
+ titleForPrivateProfile="@com.android.safetycenter.resources:string/biometrics_title_for_private_profile"
+ searchTerms="@com.android.safetycenter.resources:string/biometrics_search_terms"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="AndroidCellularNetworkSecuritySources"
+ title="@com.android.safetycenter.resources:string/cellular_network_security_title"
+ summary="@com.android.safetycenter.resources:string/cellular_network_security_summary">
+ <dynamic-safety-source
+ id="AndroidCellularNetworkSecurity"
+ packageName="com.android.phone"
+ profile="primary_profile_only"
+ notificationsAllowed="true"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="AndroidPrivacySources"
+ title="@com.android.safetycenter.resources:string/privacy_sources_title"
+ summary="@com.android.safetycenter.resources:string/privacy_sources_summary"
+ statelessIconType="privacy">
+ <static-safety-source
+ id="AndroidPermissionManager"
+ profile="primary_profile_only"
+ intentAction="android.intent.action.MANAGE_PERMISSIONS"
+ title="@com.android.safetycenter.resources:string/permission_manager_title"
+ summary="@com.android.safetycenter.resources:string/permission_manager_summary"
+ searchTerms="@com.android.safetycenter.resources:string/permission_manager_search_terms"/>
+ <dynamic-safety-source
+ id="AndroidHealthConnect"
+ profile="primary_profile_only"
+ packageName="com.android.healthconnect.controller"
+ initialDisplayState="hidden"
+ refreshOnPageOpenAllowed="false"
+ title="@com.android.safetycenter.resources:string/health_connect_title"
+ searchTerms="@com.android.safetycenter.resources:string/health_connect_search_terms"/>
+ <dynamic-safety-source
+ id="AndroidPrivacyAppDataSharingUpdates"
+ packageName="com.android.permissioncontroller"
+ profile="primary_profile_only"
+ initialDisplayState="hidden"
+ refreshOnPageOpenAllowed="true"
+ title="@com.android.safetycenter.resources:string/app_data_sharing_updates_title"
+ searchTerms="@com.android.safetycenter.resources:string/app_data_sharing_updates_search_terms"/>
+ <static-safety-source
+ id="AndroidPrivacyControls"
+ profile="primary_profile_only"
+ intentAction="android.settings.PRIVACY_CONTROLS"
+ title="@com.android.safetycenter.resources:string/privacy_controls_title"
+ summary="@com.android.safetycenter.resources:string/privacy_controls_summary"
+ searchTerms="@com.android.safetycenter.resources:string/privacy_controls_search_terms"/>
+ <issue-only-safety-source
+ id="AndroidAccessibility"
+ packageName="com.android.permissioncontroller"
+ profile="all_profiles"
+ notificationsAllowed="true"
+ refreshOnPageOpenAllowed="true"/>
+ <issue-only-safety-source
+ id="AndroidNotificationListener"
+ packageName="com.android.permissioncontroller"
+ profile="primary_profile_only"
+ notificationsAllowed="true"
+ refreshOnPageOpenAllowed="true"/>
+ <issue-only-safety-source
+ id="AndroidBackgroundLocation"
+ packageName="com.android.permissioncontroller"
+ profile="all_profiles"
+ notificationsAllowed="true"
+ refreshOnPageOpenAllowed="true"/>
+ <issue-only-safety-source
+ id="AndroidPermissionAutoRevoke"
+ packageName="com.android.permissioncontroller"
+ profile="all_profiles"
+ notificationsAllowed="true"
+ refreshOnPageOpenAllowed="true"/>
+ <issue-only-safety-source
+ id="AndroidCertificateTransparency"
+ packageName="android"
+ profile="primary_profile_only"
+ notificationsAllowed="false"
+ refreshOnPageOpenAllowed="true"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="AndroidPrivacySourcesAdditional"
+ title="@com.android.safetycenter.resources:string/privacy_additional_title">
+ <static-safety-source
+ id="AndroidPermissionUsage"
+ profile="primary_profile_only"
+ intentAction="android.intent.action.REVIEW_PERMISSION_USAGE"
+ title="@com.android.safetycenter.resources:string/permission_usage_title"
+ summary="@com.android.safetycenter.resources:string/permission_usage_summary"
+ searchTerms="@com.android.safetycenter.resources:string/permission_usage_search_terms"/>
+ <dynamic-safety-source
+ id="AndroidPrivateSpace"
+ packageName="com.android.settings"
+ profile="primary_profile_only"
+ title="@com.android.safetycenter.resources:string/private_space_title"
+ summary="@com.android.safetycenter.resources:string/private_space_summary"
+ searchTerms="@com.android.safetycenter.resources:string/private_space_search_terms"
+ initialDisplayState="hidden"
+ maxSeverityLevel="0"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="AndroidAdvancedSources"
+ title="@com.android.safetycenter.resources:string/advanced_title">
+ <dynamic-safety-source
+ id="AndroidWorkPolicyInfo"
+ packageName="com.android.permissioncontroller"
+ profile="primary_profile_only"
+ title="@com.android.safetycenter.resources:string/work_policy_title"
+ initialDisplayState="hidden"
+ refreshOnPageOpenAllowed="true"/>
+ <static-safety-source
+ id="AndroidMoreSettings"
+ profile="primary_profile_only"
+ intentAction="com.android.settings.MORE_SECURITY_PRIVACY_SETTINGS"
+ title="@com.android.safetycenter.resources:string/more_settings_title"
+ summary="@com.android.safetycenter.resources:string/more_settings_summary"
+ searchTerms="@com.android.safetycenter.resources:string/more_settings_search_terms"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Resources/res/values-de-v34/strings.xml b/SafetyCenter/Resources/res/values-de-v34/strings.xml
index 4289c14a0..8f75bdfbb 100644
--- a/SafetyCenter/Resources/res/values-de-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-de-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Änderungen bei der Weitergabe von Standortdaten"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Daten, Datenweitergabe, Aktualisierungen der Datenweitergabe, Aktualisierungen der Weitergabe von Standortdaten, Weitergabe"</string>
<string name="advanced_title" msgid="6259362998269627310">"Weitere Einstellungen"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"Mehr Sicherheit und Datenschutz"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"Mehr Sicherheit &amp; Datenschutz"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"Autofill, Benachrichtigungen und mehr"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"Informationen zu den Arbeitsrichtlinien"</string>
diff --git a/flags/Android.bp b/flags/Android.bp
index aba1e44a9..d22da26c3 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -36,6 +36,7 @@ java_aconfig_library {
libs: ["framework-configinfrastructure.stubs.module_lib"],
visibility: [
"//packages/modules/Permission:__subpackages__",
+ "//vendor:__subpackages__",
],
apex_available: [
"com.android.permission",
diff --git a/flags/flags.aconfig b/flags/flags.aconfig
index c94614654..4cb084988 100644
--- a/flags/flags.aconfig
+++ b/flags/flags.aconfig
@@ -104,7 +104,52 @@ flag {
name: "app_permission_fragment_uses_preferences"
is_exported: true
namespace: "permissions"
- description: "This flag enables AppPermissionFragment rather than LegacyAppPermissionFragment (to support BC25)"
+ description: "This flag enables AppPermissionFragment rather than LegacyAppPermissionFragment"
bug: "349675008"
is_fixed_read_only: true
}
+
+flag {
+ name: "cross_user_role_enabled"
+ is_exported: true
+ namespace: "permissions"
+ description: "This flag enables cross-user roles support and API"
+ bug: "367732307"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "permission_timeline_attribution_label_fix"
+ is_exported: true
+ namespace: "permissions"
+ description: "This flag is used for the attribution label fix on permission timeline page"
+ bug: "369606734"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "wear_compose_material3"
+ is_exported: true
+ namespace: "permissions"
+ description: "This flag enables material3 design system for wear ui components"
+ bug: "370489422"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "decluttered_permission_manager_enabled"
+ is_exported: true
+ namespace: "permissions"
+ description: "Enables displaying unused permission groups in the additional page, instead of displaying them in the main permission manager page"
+ bug: "365823624"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "expressive_design_enabled"
+ is_exported: true
+ namespace: "permissions"
+ description: "This flag is used to enable Expressive Design for Settings pages inside PermissionController"
+ bug: "375480184"
+ is_fixed_read_only: true
+} \ No newline at end of file
diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt
index e15887576..212996b24 100644
--- a/framework-s/api/system-current.txt
+++ b/framework-s/api/system-current.txt
@@ -37,7 +37,9 @@ 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 @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getDefaultHoldersForTest(@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);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -45,18 +47,24 @@ package android.app.role {
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isBypassingRoleQualification();
method @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isRoleFallbackEnabled(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isRoleVisibleForTest(@NonNull String);
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("com.android.permission.flags.cross_user_role_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setDefaultHoldersForTest(@NonNull String, @NonNull java.util.List<java.lang.String>);
method @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setRoleFallbackEnabled(@NonNull String, boolean);
method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
+ method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setRoleVisibleForTest(@NonNull String, boolean);
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_DEPENDENCY_INSTALLER = "android.app.role.SYSTEM_DEPENDENCY_INSTALLER";
field public static final String ROLE_SYSTEM_SUPERVISION = "android.app.role.SYSTEM_SUPERVISION";
field public static final String ROLE_SYSTEM_WELLBEING = "android.app.role.SYSTEM_WELLBEING";
}
diff --git a/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java
index 74062165e..db05a0af6 100644
--- a/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java
+++ b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java
@@ -20,6 +20,7 @@ import static android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_AC
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
@@ -35,15 +36,13 @@ import android.os.RemoteException;
import android.permission.flags.Flags;
import android.util.ArraySet;
-import androidx.annotation.NonNull;
-
import java.lang.annotation.Retention;
/**
* This class provides the core API for ECM (Enhanced Confirmation Mode). ECM is a feature that
* restricts access to protected **settings** (i.e., sensitive resources) by restricted **apps**
* (apps from from dangerous sources, such as sideloaded packages or packages downloaded from a web
- * browser).
+ * browser), or restricts settings globally based on device state.
*
* <p>Specifically, this class provides the ability to:
*
@@ -71,6 +70,9 @@ import java.lang.annotation.Retention;
* particular app restricted is an implementation detail of ECM. However, the user is able to
* clear any restricted app's restriction status (i.e, un-restrict it), after which ECM will
* consider the app **not restricted**.
+ * <li>A setting may be globally restricted based on device state. In this case, any app may be
+ * automatically considered *restricted*, regardless of the app's restriction state. Users
+ * cannot un-restrict the app, in these cases.
* </ol>
*
* Why is ECM needed? Consider the following (pre-ECM) scenario:
@@ -313,6 +315,9 @@ public final class EnhancedConfirmationManager {
* <p>This should be called from the "Restricted setting" dialog (which {@link
* #createRestrictedSettingDialogIntent} directs to) upon being presented to the user.
*
+ * <p>This restriction clearing does not apply to any settings that are restricted based on
+ * global device state
+ *
* @param packageName package name of the application which should be considered acknowledged
* @throws NameNotFoundException if the provided package was not found
*/
diff --git a/framework-s/java/android/app/role/IRoleManager.aidl b/framework-s/java/android/app/role/IRoleManager.aidl
index 522967630..cf98a6016 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,
@@ -80,4 +84,13 @@ interface IRoleManager {
boolean isApplicationVisibleForRoleAsUser(in String roleName, in String packageName,
int userId);
+
+ List<String> getDefaultHoldersForTestAsUser(in String roleName, int userId);
+
+ void setDefaultHoldersForTestAsUser(in String roleName, in List<String> packageNames,
+ int userId);
+
+ boolean isRoleVisibleForTestAsUser(in String roleName, int userId);
+
+ void setRoleVisibleForTestAsUser(in String roleName, boolean visible, int userId);
}
diff --git a/framework-s/java/android/app/role/RoleManager.java b/framework-s/java/android/app/role/RoleManager.java
index 4b8c9b388..70bcfbf36 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 })
@@ -253,6 +264,19 @@ public final class RoleManager {
public static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
"com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
+ /**
+ * The name of the system dependency installer role.
+ *
+ * A dependency installer installs missing SDK or static shared library dependencies that an app
+ * requires to be installed.
+ *
+ * @hide
+ */
+ @SuppressLint("UnflaggedApi")
+ @SystemApi
+ public static final String ROLE_SYSTEM_DEPENDENCY_INSTALLER =
+ "android.app.role.SYSTEM_DEPENDENCY_INSTALLER";
+
@NonNull
private final Context mContext;
@@ -574,6 +598,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) {
@@ -1074,6 +1179,124 @@ public final class RoleManager {
}
}
+ /**
+ * Get the default holders of this role, which will be added when the role is added for the
+ * first time.
+ * <p>
+ * <strong>Note:</strong> Use of this API should be limited to tests. The values returned are
+ * not persisted.
+ * <p>
+ * Throws {@link IllegalArgumentException} if role is not a test role
+ *
+ * @param roleName the name of the role to get test default holders for
+ * @return the list of package names of the default holders
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @RequiresApi(Build.VERSION_CODES.BAKLAVA)
+ @SystemApi
+ @UserHandleAware
+ @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @NonNull
+ public List<String> getDefaultHoldersForTest(@NonNull String roleName) {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ try {
+ return mService.getDefaultHoldersForTestAsUser(roleName,
+ mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the default holders of this role, which will be added when the role is added for the
+ * first time.
+ * <p>
+ * <strong>Note:</strong> Use of this API should be limited to tests. The values used are
+ * not persisted.
+ * <p>
+ * Throws {@link IllegalArgumentException} if role is not a test role
+ * Throws {@link NullPointerException} if packageNames is {@code null}
+ *
+ * @param roleName the name of the role to set test default holders for
+ * @param packageNames a list of package names of the default holders, or an empty list to unset
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @RequiresApi(Build.VERSION_CODES.BAKLAVA)
+ @SystemApi
+ @UserHandleAware
+ @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ public void setDefaultHoldersForTest(
+ @NonNull String roleName, @NonNull List<String> packageNames) {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Objects.requireNonNull(packageNames, "packageNames cannot be null");
+ try {
+ mService.setDefaultHoldersForTestAsUser(roleName, packageNames,
+ mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get whether a role should be visible for testing.
+ * <p>
+ * <strong>Note:</strong> Use of this API should be limited to tests. The values returned are
+ * not persisted.
+ * <p>
+ * Throws {@link IllegalArgumentException} if role is not a test role
+ *
+ * @param roleName the name of the role to get test visibility for
+ * @return {@code true} if role is visible, {@code false} otherwise
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @RequiresApi(Build.VERSION_CODES.BAKLAVA)
+ @SystemApi
+ @UserHandleAware
+ @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ public boolean isRoleVisibleForTest(@NonNull String roleName) {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ try {
+ return mService.isRoleVisibleForTestAsUser(roleName,
+ mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether a role should be visible for testing.
+ * <p>
+ * <strong>Note:</strong> Use of this API should be limited to tests. The values used are
+ * not persisted.
+ * <p>
+ * Throws {@link IllegalArgumentException} if role is not a test role
+ *
+ * @param roleName the name of the role to set test visibility for
+ * @param visible {@code true} to set role as visible, {@code false} otherwise
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @RequiresApi(Build.VERSION_CODES.BAKLAVA)
+ @SystemApi
+ @UserHandleAware
+ @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ public void setRoleVisibleForTest(@NonNull String roleName, boolean visible) {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ try {
+ mService.setRoleVisibleForTestAsUser(roleName, visible,
+ mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@NonNull
private RoleControllerManager getRoleControllerManager() {
synchronized (mRoleControllerManagerLock) {
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 6f851c4d2..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",
diff --git a/service/api/system-server-current.txt b/service/api/system-server-current.txt
index 30fbab484..a3db89370 100644
--- a/service/api/system-server-current.txt
+++ b/service/api/system-server-current.txt
@@ -1,4 +1,18 @@
// Signature format: 2.0
+package com.android.ecm {
+
+ @FlaggedApi("android.permission.flags.enhanced_confirmation_in_call_apis_enabled") public class EnhancedConfirmationCallTrackerService extends android.telecom.InCallService {
+ ctor public EnhancedConfirmationCallTrackerService();
+ }
+
+ @FlaggedApi("android.permission.flags.enhanced_confirmation_in_call_apis_enabled") public interface EnhancedConfirmationManagerLocal {
+ method public void addOngoingCall(@NonNull android.telecom.Call);
+ method public void clearOngoingCalls();
+ method public void removeOngoingCall(@NonNull String);
+ }
+
+}
+
package com.android.permission.persistence {
public interface RuntimePermissionsPersistence {
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index 4d4d6e050..bad5c7666 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -2,6 +2,10 @@
# RoleParser.applyJarjarTransform(), by adding NO_IFTTT=reason to your commit
# message.
# LINT.IfChange
+rule android.app.admin.flags.*FeatureFlags* com.android.permission.jarjar.@0
+rule android.app.admin.flags.FeatureFlags* com.android.permission.jarjar.@0
+rule android.app.admin.flags.FeatureFlags com.android.permission.jarjar.@0
+rule android.app.admin.flags.Flags com.android.permission.jarjar.@0
rule android.app.appfunctions.flags.*FeatureFlags* com.android.permission.jarjar.@0
rule android.app.appfunctions.flags.FeatureFlags* com.android.permission.jarjar.@0
rule android.app.appfunctions.flags.FeatureFlags com.android.permission.jarjar.@0
@@ -10,6 +14,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
@@ -34,6 +42,10 @@ rule com.android.safetycenter.annotations.** com.android.permission.jarjar.@0
rule com.android.safetycenter.internaldata.** com.android.permission.jarjar.@0
rule com.android.safetycenter.pendingintents.** com.android.permission.jarjar.@0
rule com.android.safetycenter.resources.** com.android.permission.jarjar.@0
+rule com.android.settingslib.flags.*FeatureFlags* com.android.permission.jarjar.@0
+rule com.android.settingslib.flags.FeatureFlags* com.android.permission.jarjar.@0
+rule com.android.settingslib.flags.FeatureFlags com.android.permission.jarjar.@0
+rule com.android.settingslib.flags.Flags 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
diff --git a/service/java/com/android/ecm/EnhancedConfirmationCallTrackerService.java b/service/java/com/android/ecm/EnhancedConfirmationCallTrackerService.java
new file mode 100644
index 000000000..9117d6558
--- /dev/null
+++ b/service/java/com/android/ecm/EnhancedConfirmationCallTrackerService.java
@@ -0,0 +1,80 @@
+/*
+ * 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.ecm;
+
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.os.Build;
+import android.permission.flags.Flags;
+import android.telecom.Call;
+import android.telecom.InCallService;
+
+import com.android.server.LocalManagerRegistry;
+
+/**
+ * @hide
+ *
+ * This InCallService tracks called (both incoming and outgoing), and sends their information to the
+ * EnhancedConfirmationService
+ *
+ **/
+@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_IN_CALL_APIS_ENABLED)
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
+public class EnhancedConfirmationCallTrackerService extends InCallService {
+ private EnhancedConfirmationManagerLocal mEnhancedConfirmationManagerLocal;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ if (Flags.unknownCallPackageInstallBlockingEnabled()) {
+ mEnhancedConfirmationManagerLocal =
+ LocalManagerRegistry.getManager(EnhancedConfirmationManagerLocal.class);
+ }
+ }
+
+ @Override
+ public void onCallAdded(@Nullable Call call) {
+ if (mEnhancedConfirmationManagerLocal == null || call == null) {
+ return;
+ }
+
+ mEnhancedConfirmationManagerLocal.addOngoingCall(call);
+ }
+
+ @Override
+ public void onCallRemoved(@Nullable Call call) {
+ if (mEnhancedConfirmationManagerLocal == null || call == null) {
+ return;
+ }
+
+ mEnhancedConfirmationManagerLocal.removeOngoingCall(call.getDetails().getId());
+ }
+
+ /**
+ * When unbound, we should assume all calls have finished. Notify the system of such.
+ */
+ public boolean onUnbind(@Nullable Intent intent) {
+ if (mEnhancedConfirmationManagerLocal != null) {
+ mEnhancedConfirmationManagerLocal.clearOngoingCalls();
+ }
+ return super.onUnbind(intent);
+ }
+}
diff --git a/service/java/com/android/ecm/EnhancedConfirmationManagerLocal.java b/service/java/com/android/ecm/EnhancedConfirmationManagerLocal.java
new file mode 100644
index 000000000..483071716
--- /dev/null
+++ b/service/java/com/android/ecm/EnhancedConfirmationManagerLocal.java
@@ -0,0 +1,56 @@
+/*
+ * 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.ecm;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.permission.flags.Flags;
+import android.telecom.Call;
+
+/**
+ * @hide
+ *
+ * In-process API for the Enhanced Confirmation Service
+ */
+@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_IN_CALL_APIS_ENABLED)
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
+public interface EnhancedConfirmationManagerLocal {
+ /**
+ * Inform the enhanced confirmation service of an ongoing call
+ *
+ * @param call The call to potentially track
+ *
+ */
+ void addOngoingCall(@NonNull Call call);
+
+ /**
+ * Inform the enhanced confirmation service that a call has ended
+ *
+ * @param callId The ID of the call to stop tracking
+ *
+ */
+ void removeOngoingCall(@NonNull String callId);
+
+ /**
+ * Informs the enhanced confirmation service it should clear out any ongoing calls
+ */
+ void clearOngoingCalls();
+}
diff --git a/service/java/com/android/ecm/EnhancedConfirmationManagerLocalImpl.java b/service/java/com/android/ecm/EnhancedConfirmationManagerLocalImpl.java
new file mode 100644
index 000000000..b34eac9f4
--- /dev/null
+++ b/service/java/com/android/ecm/EnhancedConfirmationManagerLocalImpl.java
@@ -0,0 +1,59 @@
+/*
+ * 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.ecm;
+
+import android.annotation.NonNull;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.permission.flags.Flags;
+import android.telecom.Call;
+
+/** @hide */
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
+class EnhancedConfirmationManagerLocalImpl implements EnhancedConfirmationManagerLocal {
+
+ private final EnhancedConfirmationService mService;
+
+ EnhancedConfirmationManagerLocalImpl(EnhancedConfirmationService service) {
+ if (Flags.unknownCallPackageInstallBlockingEnabled()) {
+ mService = service;
+ } else {
+ mService = null;
+ }
+ }
+
+ @Override
+ public void addOngoingCall(@NonNull Call call) {
+ if (mService != null) {
+ mService.addOngoingCall(call);
+ }
+ }
+
+ @Override
+ public void removeOngoingCall(@NonNull String callId) {
+ if (mService != null) {
+ mService.removeOngoingCall(callId);
+ }
+ }
+
+ @Override
+ public void clearOngoingCalls() {
+ if (mService != null) {
+ mService.clearOngoingCalls();
+ }
+ }
+}
diff --git a/service/java/com/android/ecm/EnhancedConfirmationService.java b/service/java/com/android/ecm/EnhancedConfirmationService.java
index 708884e85..65fde6daf 100644
--- a/service/java/com/android/ecm/EnhancedConfirmationService.java
+++ b/service/java/com/android/ecm/EnhancedConfirmationService.java
@@ -19,11 +19,15 @@ package com.android.ecm;
import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.ecm.EnhancedConfirmationManager;
import android.app.ecm.IEnhancedConfirmationManager;
import android.app.role.RoleManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
@@ -31,25 +35,33 @@ import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.SignedPackage;
+import android.database.Cursor;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.permission.flags.Flags;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.PhoneLookup;
+import android.telecom.Call;
+import android.telecom.PhoneAccount;
+import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.internal.util.Preconditions;
import com.android.permission.util.UserUtils;
+import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -66,16 +78,35 @@ import java.util.Set;
@Keep
@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@SuppressLint("MissingPermission")
public class EnhancedConfirmationService extends SystemService {
private static final String LOG_TAG = EnhancedConfirmationService.class.getSimpleName();
private Map<String, List<byte[]>> mTrustedPackageCertDigests;
private Map<String, List<byte[]>> mTrustedInstallerCertDigests;
+ // A map of call ID to call type
+ private final Map<String, Integer> mOngoingCalls = new ArrayMap<>();
+
+ private static final int CALL_TYPE_UNTRUSTED = 0;
+ private static final int CALL_TYPE_TRUSTED = 1;
+ private static final int CALL_TYPE_EMERGENCY = 2;
+ @IntDef(flag = true, value = {
+ CALL_TYPE_UNTRUSTED,
+ CALL_TYPE_TRUSTED,
+ CALL_TYPE_EMERGENCY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CallType {}
public EnhancedConfirmationService(@NonNull Context context) {
super(context);
+ LocalManagerRegistry.addManager(EnhancedConfirmationManagerLocal.class,
+ new EnhancedConfirmationManagerLocalImpl(this));
}
+ private ContentResolver mContentResolver;
+ private TelephonyManager mTelephonyManager;
+
@Override
public void onStart() {
Context context = getContext();
@@ -87,6 +118,8 @@ public class EnhancedConfirmationService extends SystemService {
systemConfigManager.getEnhancedConfirmationTrustedInstallers());
publishBinderService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE, new Stub());
+ mContentResolver = getContext().getContentResolver();
+ mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
}
private Map<String, List<byte[]>> toTrustedPackageMap(Set<SignedPackage> signedPackages) {
@@ -99,6 +132,97 @@ public class EnhancedConfirmationService extends SystemService {
return trustedPackageMap;
}
+ void addOngoingCall(Call call) {
+ if (!Flags.unknownCallPackageInstallBlockingEnabled()) {
+ return;
+ }
+ if (call.getDetails() == null) {
+ return;
+ }
+ mOngoingCalls.put(call.getDetails().getId(), getCallType(call));
+ }
+
+ void removeOngoingCall(String callId) {
+ if (!Flags.unknownCallPackageInstallBlockingEnabled()) {
+ return;
+ }
+ Integer returned = mOngoingCalls.remove(callId);
+ if (returned == null) {
+ // TODO b/379941144: Capture a bug report whenever this happens.
+ }
+ }
+
+ void clearOngoingCalls() {
+ mOngoingCalls.clear();
+ }
+
+ private @CallType int getCallType(Call call) {
+ String number = getPhoneNumber(call);
+ try {
+ if (number != null && mTelephonyManager.isEmergencyNumber(number)) {
+ return CALL_TYPE_EMERGENCY;
+ }
+ } catch (IllegalStateException | UnsupportedOperationException e) {
+ // If either of these are thrown, the telephony service is not available on the current
+ // device, either because the device lacks telephony calling, or the telephony service
+ // is unavailable.
+ }
+ if (number != null) {
+ return hasContactWithPhoneNumber(number) ? CALL_TYPE_TRUSTED : CALL_TYPE_UNTRUSTED;
+ } else {
+ return hasContactWithDisplayName(call.getDetails().getCallerDisplayName())
+ ? CALL_TYPE_TRUSTED : CALL_TYPE_UNTRUSTED;
+ }
+ }
+
+ private String getPhoneNumber(Call call) {
+ Uri handle = call.getDetails().getHandle();
+ if (handle == null || handle.getScheme() == null) {
+ return null;
+ }
+ if (!handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
+ return null;
+ }
+ return handle.getSchemeSpecificPart();
+ }
+
+ private boolean hasContactWithPhoneNumber(String phoneNumber) {
+ if (phoneNumber == null) {
+ return false;
+ }
+ Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
+ Uri.encode(phoneNumber));
+ String[] projection = new String[]{
+ PhoneLookup.DISPLAY_NAME,
+ ContactsContract.PhoneLookup._ID
+ };
+ try (Cursor res = mContentResolver.query(uri, projection, null, null)) {
+ return res != null && res.getCount() > 0;
+ }
+ }
+
+ private boolean hasContactWithDisplayName(String displayName) {
+ if (displayName == null) {
+ return false;
+ }
+ Uri uri = ContactsContract.Data.CONTENT_URI;
+ String[] projection = new String[]{PhoneLookup._ID};
+ String selection = StructuredName.DISPLAY_NAME + " = ?";
+ String[] selectionArgs = new String[]{displayName};
+ try (Cursor res = mContentResolver.query(uri, projection, selection, selectionArgs, null)) {
+ return res != null && res.getCount() > 0;
+ }
+ }
+
+ private boolean hasCallOfType(@CallType int callType) {
+ for (int ongoingCallType : mOngoingCalls.values()) {
+ if (ongoingCallType == callType) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private class Stub extends IEnhancedConfirmationManager.Stub {
/** A map of ECM states to their corresponding app op states */
@@ -115,6 +239,10 @@ public class EnhancedConfirmationService extends SystemService {
private static final ArraySet<String> PROTECTED_SETTINGS = new ArraySet<>();
+ // Settings restricted when an untrusted call is ongoing. These must also be added to
+ // PROTECTED_SETTINGS
+ private static final ArraySet<String> UNTRUSTED_CALL_RESTRICTED_SETTINGS = new ArraySet<>();
+
static {
// Runtime permissions
PROTECTED_SETTINGS.add(Manifest.permission.SEND_SMS);
@@ -135,6 +263,13 @@ public class EnhancedConfirmationService extends SystemService {
// Default application roles.
PROTECTED_SETTINGS.add(RoleManager.ROLE_DIALER);
PROTECTED_SETTINGS.add(RoleManager.ROLE_SMS);
+
+ if (Flags.unknownCallPackageInstallBlockingEnabled()) {
+ // Requesting package installs, limited during phone calls
+ PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES);
+ UNTRUSTED_CALL_RESTRICTED_SETTINGS.add(
+ AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES);
+ }
}
private final @NonNull Context mContext;
@@ -163,8 +298,13 @@ public class EnhancedConfirmationService extends SystemService {
"settingIdentifier cannot be null or empty");
try {
- return isSettingEcmProtected(settingIdentifier) && isPackageEcmGuarded(packageName,
- userId);
+ if (!isSettingEcmProtected(settingIdentifier)) {
+ return false;
+ }
+ if (isSettingProtectedGlobally(settingIdentifier)) {
+ return true;
+ }
+ return isPackageEcmGuarded(packageName, userId);
} catch (NameNotFoundException e) {
throw new IllegalArgumentException(e);
}
@@ -227,8 +367,21 @@ public class EnhancedConfirmationService extends SystemService {
}
}
+ private boolean isUntrustedCallOngoing() {
+ if (!Flags.unknownCallPackageInstallBlockingEnabled()) {
+ return false;
+ }
+
+ if (hasCallOfType(CALL_TYPE_EMERGENCY)) {
+ // If we have an emergency call, return false always.
+ return false;
+ }
+ return hasCallOfType(CALL_TYPE_UNTRUSTED);
+ }
+
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);
}
@@ -322,6 +475,7 @@ public class EnhancedConfirmationService extends SystemService {
return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ @SuppressLint("WrongConstant")
private void setAppEcmState(@NonNull String packageName, @EcmState int ecmState,
@UserIdInt int userId) throws NameNotFoundException {
int packageUid = getPackageUid(packageName, userId);
@@ -359,6 +513,14 @@ public class EnhancedConfirmationService extends SystemService {
return false;
}
+ private boolean isSettingProtectedGlobally(@NonNull String settingIdentifier) {
+ if (UNTRUSTED_CALL_RESTRICTED_SETTINGS.contains(settingIdentifier)) {
+ return isUntrustedCallOngoing();
+ }
+
+ return false;
+ }
+
@Nullable
private ApplicationInfo getApplicationInfoAsUser(@Nullable String packageName,
@UserIdInt int userId) {
diff --git a/service/java/com/android/permission/util/UserUtils.java b/service/java/com/android/permission/util/UserUtils.java
index 33389a88f..c69afb199 100644
--- a/service/java/com/android/permission/util/UserUtils.java
+++ b/service/java/com/android/permission/util/UserUtils.java
@@ -17,18 +17,21 @@
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.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;
/** Utility class to deal with Android users. */
@@ -40,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(
@@ -53,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);
}
}
@@ -81,6 +112,68 @@ 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 {
+ 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. */
+ @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.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return userManager.getProfileParent(UserHandle.of(userId));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
/** Returns whether a given {@code userId} corresponds to a managed profile. */
public static boolean isManagedProfile(@UserIdInt int userId, @NonNull Context context) {
UserManager userManager = context.getSystemService(UserManager.class);
@@ -107,8 +200,7 @@ public final class UserUtils {
// MANAGE_USERS, QUERY_USERS, or INTERACT_ACROSS_USERS.
final long identity = Binder.clearCallingIdentity();
try {
- Context userContext = context
- .createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
+ Context userContext = getUserContext(userId, context);
UserManager userManager = userContext.getSystemService(UserManager.class);
return userManager != null && userManager.isPrivateProfile();
} finally {
@@ -141,4 +233,13 @@ public final class UserUtils {
Binder.restoreCallingIdentity(identity);
}
}
+
+ @NonNull
+ public static Context getUserContext(@UserIdInt int userId, @NonNull Context context) {
+ if (SdkLevel.isAtLeastS() && context.getUser().getIdentifier() == userId) {
+ return context;
+ } else {
+ return context.createContextAsUser(UserHandle.of(userId), 0);
+ }
+ }
}
diff --git a/service/java/com/android/role/RoleService.java b/service/java/com/android/role/RoleService.java
index 20250b4f6..5bc79efbb 100644
--- a/service/java/com/android/role/RoleService.java
+++ b/service/java/com/android/role/RoleService.java
@@ -43,9 +43,11 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.Trace;
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,16 @@ 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.role.controller.service.RoleControllerServiceImpl;
+import com.android.role.controller.util.RoleFlags;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
import com.android.server.role.RoleServicePlatformHelper;
@@ -77,6 +82,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;
@@ -95,6 +101,7 @@ import java.util.concurrent.TimeoutException;
@RequiresApi(Build.VERSION_CODES.S)
public class RoleService extends SystemService implements RoleUserState.Callback {
private static final String LOG_TAG = RoleService.class.getSimpleName();
+ private static final String TRACE_TAG = RoleService.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -114,9 +121,24 @@ public class RoleService extends SystemService implements RoleUserState.Callback
if (SdkLevel.isAtLeastV()) {
defaultApplicationRoles.add(RoleManager.ROLE_WALLET);
}
+ if (RoleFlags.isProfileGroupExclusivityAvailable()) {
+ defaultApplicationRoles.add(
+ RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY);
+ }
DEFAULT_APPLICATION_ROLES = defaultApplicationRoles.toArray(new String[0]);
}
+ private static final String[] TEST_ROLES;
+
+ static {
+ if (RoleFlags.isProfileGroupExclusivityAvailable()) {
+ TEST_ROLES =
+ new String[] {RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY};
+ } else {
+ TEST_ROLES = new String[0];
+ }
+ }
+
@NonNull
private final AppOpsManager mAppOpsManager;
@@ -165,6 +187,11 @@ public class RoleService extends SystemService implements RoleUserState.Callback
public RoleService(@NonNull Context context) {
super(context);
+ if (RoleFlags.isProfileGroupExclusivityAvailable()) {
+ RoleControllerServiceImpl.sSetActiveUserForRoleMethod =
+ this::setActiveUserForRoleFromController;
+ }
+
mPlatformHelper = LocalManagerRegistry.getManager(RoleServicePlatformHelper.class);
RoleControllerManager.initializeRemoteServiceComponentName(context);
@@ -176,6 +203,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);
@@ -259,11 +287,16 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@Override
public void onUserStarting(@NonNull TargetUser user) {
- if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) {
- upgradeLegacyFallbackEnabledRolesIfNeeded(user);
- }
+ Trace.beginSection(TRACE_TAG + "_onUserStarting");
+ try {
+ if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) {
+ upgradeLegacyFallbackEnabledRolesIfNeeded(user);
+ }
- maybeGrantDefaultRolesSync(user.getUserHandle().getIdentifier());
+ maybeGrantDefaultRolesSync(user.getUserHandle().getIdentifier());
+ } finally {
+ Trace.endSection();
+ }
}
private void upgradeLegacyFallbackEnabledRolesIfNeeded(@NonNull TargetUser user) {
@@ -406,6 +439,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback
private void onRemoveUser(@UserIdInt int userId) {
RemoteCallbackList<IOnRoleHoldersChangedListener> listeners;
RoleUserState userState;
+ // UserManager still knows the user until ACTION_USER_REMOVED broadcasts are processed
+ int profileParentId = UserUtils.getProfileParentIdOrSelf(userId, getContext());
+ List<String> activeRoleNames = null;
synchronized (mLock) {
mGrantDefaultRolesThrottledRunnables.remove(userId);
listeners = mListeners.get(userId);
@@ -413,7 +449,29 @@ public class RoleService extends SystemService implements RoleUserState.Callback
mControllers.remove(userId);
userState = mUserStates.get(userId);
mUserStates.remove(userId);
+
+ if (RoleFlags.isProfileGroupExclusivityAvailable() && userId != profileParentId) {
+ RoleUserState profileParentState = mUserStates.get(profileParentId);
+ activeRoleNames = profileParentState.getActiveRolesForUser(userId);
+ }
}
+ if (RoleFlags.isProfileGroupExclusivityAvailable() && userId != profileParentId
+ && !CollectionUtils.isEmpty(activeRoleNames)) {
+ int activeRoleNamesSize = activeRoleNames.size();
+ for (int i = 0; i < activeRoleNamesSize; i++) {
+ String roleName = activeRoleNames.get(i);
+
+ // If the previous active user had a set role holder, attempt to fallback for
+ // the profile parent.
+ Log.i(LOG_TAG, "User " + userId + " removed, falling back to profile parent "
+ + profileParentId + " for role " + roleName);
+ // Use profileParentId instead of userId here, since userId is in a state of removal
+ // and might be excluded from UserManager#getUserHandles with excludeDying=true
+ setActiveUserForRoleAsUserInternal(roleName, profileParentId, 0, true,
+ profileParentId);
+ }
+ }
+
if (listeners != null) {
listeners.kill();
}
@@ -460,12 +518,100 @@ public class RoleService extends SystemService implements RoleUserState.Callback
}
}
+ private void enforceProfileGroupExclusiveRole(@NonNull String roleName) {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkArgument(isProfileGroupExclusiveRole(roleName, getContext()),
+ roleName + " is not a profile-group exclusive role");
+ }
+
+ /**
+ * Returns whether the given role has profile group exclusivity
+ *
+ * @param roleName The name of role to check
+ * @param context The context
+ * @return {@code true} if the role is profile group exclusive, {@code false} otherwise
+ */
+ private static boolean isProfileGroupExclusiveRole(String roleName, Context context) {
+ if (!RoleFlags.isProfileGroupExclusivityAvailable()) {
+ return false;
+ }
+ Role role = Roles.get(context).get(roleName);
+ return role != null && role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP;
+ }
+
+ private void setActiveUserForRoleFromController(@NonNull String roleName, @UserIdInt int userId,
+ @RoleManager.ManageHoldersFlags int flags) {
+ setActiveUserForRoleAsUserInternal(roleName, userId, flags, false, userId);
+ }
+
+ private void setActiveUserForRoleAsUserInternal(@NonNull String roleName,
+ @UserIdInt int activeUserId, @RoleManager.ManageHoldersFlags int flags,
+ boolean clearRoleHoldersForActiveUser, @UserIdInt int userId) {
+ Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(),
+ "setActiveUserForRoleAsUser not available");
+ enforceProfileGroupExclusiveRole(roleName);
+
+ 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;
+ }
+
+ 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++) {
+ int profilesUserId = profiles.get(i).getIdentifier();
+ if (!clearRoleHoldersForActiveUser && profilesUserId == activeUserId) {
+ continue;
+ }
+ 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());
+ }
+ });
+ 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);
+ }
+ }
+ }
+
private class Stub extends IRoleManager.Stub {
@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 +627,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 +647,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 +670,9 @@ 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());
+ boolean enforceForProfileGroup = isProfileGroupExclusiveRole(roleName, getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ enforceForProfileGroup, "addRoleHolderAsUser", getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return;
@@ -544,8 +692,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 +714,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 +733,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,8 +759,9 @@ 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",
- getContext());
+ boolean enforceForProfileGroup = isProfileGroupExclusiveRole(roleName, getContext());
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ enforceForProfileGroup, "setDefaultApplicationAsUser", getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.e(LOG_TAG, "user " + userId + " does not exist");
return;
@@ -633,10 +783,59 @@ public class RoleService extends SystemService implements RoleUserState.Callback
}
@Override
+ public int getActiveUserForRoleAsUser(@NonNull String roleName, @UserIdInt int userId) {
+ Trace.beginSection(TRACE_TAG + "_getActiveUserForRoleAsUser");
+ try {
+ Preconditions.checkState(RoleFlags.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);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ @Override
+ public void setActiveUserForRoleAsUser(@NonNull String roleName,
+ @UserIdInt int activeUserId, @RoleManager.ManageHoldersFlags int flags,
+ @UserIdInt int userId) {
+ Trace.beginSection(TRACE_TAG + "_setActiveUserForRoleAsUser");
+ try {
+ Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(),
+ "setActiveUserForRoleAsUser not available");
+ UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false,
+ /* enforceForProfileGroup= */ true, "setActiveUserForRole", getContext());
+ enforceCallingOrSelfAnyPermissions(new String[] {
+ Manifest.permission.MANAGE_DEFAULT_APPLICATIONS,
+ Manifest.permission.MANAGE_ROLE_HOLDERS
+ }, "setActiveUserForRoleAsUser");
+ setActiveUserForRoleAsUserInternal(roleName, activeUserId, flags, true, userId);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ @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 +855,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 +909,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 +928,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 +947,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 +967,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 +988,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 +1009,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 +1120,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 +1145,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 +1172,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 +1190,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;
@@ -998,6 +1207,59 @@ public class RoleService extends SystemService implements RoleUserState.Callback
return getOrCreateController(userId).isApplicationVisibleForRole(roleName, packageName);
}
+ @NonNull
+ @Override
+ public List<String> getDefaultHoldersForTestAsUser(@NonNull String roleName,
+ @UserIdInt int userId) {
+ Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(),
+ "getDefaultHoldersForTest not available");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "getDefaultHoldersForTest");
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkArgumentIsSupported(TEST_ROLES, roleName);
+
+ return getOrCreateUserState(userId).getDefaultHoldersForTest(roleName);
+ }
+
+ @Override
+ public void setDefaultHoldersForTestAsUser(@NonNull String roleName,
+ @NonNull List<String> packageNames, @UserIdInt int userId) {
+ Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(),
+ "setDefaultHoldersForTest not available");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "setDefaultHoldersForTest");
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkArgumentIsSupported(TEST_ROLES, roleName);
+ Objects.requireNonNull(packageNames, "packageNames cannot be null");
+
+ getOrCreateUserState(userId).setDefaultHoldersForTest(roleName, packageNames);
+ }
+
+ @Override
+ public boolean isRoleVisibleForTestAsUser(@NonNull String roleName, @UserIdInt int userId) {
+ Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(),
+ "isRoleVisibleForTest not available");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "isRoleVisibleForTest");
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkArgumentIsSupported(TEST_ROLES, roleName);
+
+ return getOrCreateUserState(userId).isRoleVisibleForTest(roleName);
+ }
+
+ @Override
+ public void setRoleVisibleForTestAsUser(@NonNull String roleName, boolean visible,
+ @UserIdInt int userId) {
+ Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(),
+ "setRoleVisibleForTest not available");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "setRoleVisibleForTest");
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkArgumentIsSupported(TEST_ROLES, roleName);
+
+ getOrCreateUserState(userId).setRoleVisibleForTest(roleName, visible);
+ }
+
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
@@ -1040,6 +1302,20 @@ 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 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..18574a6fc 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,18 @@ class RoleUserState {
private ArraySet<String> mFallbackEnabledRoles = new ArraySet<>();
@GuardedBy("mLock")
+ @NonNull
+ private ArrayMap<String, Integer> mActiveUserIds = new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ @NonNull
+ private final Map<String, List<String>> mDefaultHoldersForTest = new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ @NonNull
+ private final Set<String> mRolesVisibleForTest = new ArraySet<>();
+
+ @GuardedBy("mLock")
private boolean mWriteScheduled;
@GuardedBy("mLock")
@@ -423,6 +437,88 @@ 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);
+ }
+ }
+
+ @NonNull
+ public List<String> getActiveRolesForUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ List<String> activeRoleNames = new ArrayList<>();
+ int activeUserIdsSize = mActiveUserIds.size();
+ for (int i = 0; i < activeUserIdsSize; i++) {
+ int activeUserId = mActiveUserIds.valueAt(i);
+ if (activeUserId == userId) {
+ String roleName = mActiveUserIds.keyAt(i);
+ activeRoleNames.add(roleName);
+ }
+ }
+ return activeRoleNames;
+ }
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ @NonNull
+ public List<String> getDefaultHoldersForTest(@NonNull String roleName) {
+ synchronized (mLock) {
+ return mDefaultHoldersForTest.getOrDefault(roleName, Collections.emptyList());
+ }
+ }
+
+ public void setDefaultHoldersForTest(@NonNull String roleName,
+ @NonNull List<String> packageNames) {
+ synchronized (mLock) {
+ mDefaultHoldersForTest.put(roleName, packageNames);
+ }
+ }
+
+ public boolean isRoleVisibleForTest(@NonNull String roleName) {
+ synchronized (mLock) {
+ return mRolesVisibleForTest.contains(roleName);
+ }
+ }
+
+ public void setRoleVisibleForTest(@NonNull String roleName, boolean visible) {
+ synchronized (mLock) {
+ if (visible) {
+ mRolesVisibleForTest.add(roleName);
+ } else {
+ mRolesVisibleForTest.remove(roleName);
+ }
+ }
+ }
+
+ /**
* Schedule writing the state to file.
*/
@GuardedBy("mLock")
@@ -449,9 +545,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 +565,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 +585,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 +605,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 +625,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 +678,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 46a440bf7..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;
@@ -134,20 +129,16 @@ public final class UserProfileGroup {
* is disabled.
*/
public static UserProfileGroup fromUser(Context context, @UserIdInt int userId) {
- UserManager userManager = getUserManagerForUser(userId, context);
- List<UserHandle> userProfiles = getEnabledUserProfiles(userManager);
- UserHandle profileParent = getProfileParent(userManager, userId);
- int profileParentUserId = userId;
- if (profileParent != null) {
- profileParentUserId = profileParent.getIdentifier();
- }
+ Context userContext = UserUtils.getUserContext(userId, context);
+ List<UserHandle> userProfiles = UserUtils.getUserProfiles(userContext);
+ 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++) {
@@ -192,23 +183,10 @@ public final class UserProfileGroup {
}
private static UserManager getUserManagerForUser(@UserIdInt int userId, Context context) {
- Context userContext = getUserContext(context, UserHandle.of(userId));
+ Context userContext = UserUtils.getUserContext(userId, context);
return requireNonNull(userContext.getSystemService(UserManager.class));
}
- private static Context getUserContext(Context context, UserHandle userHandle) {
- if (Process.myUserHandle().equals(userHandle)) {
- return context;
- } else {
- try {
- return context.createPackageContextAsUser(
- context.getPackageName(), /* flags= */ 0, userHandle);
- } catch (PackageManager.NameNotFoundException doesNotHappen) {
- throw new IllegalStateException(doesNotHappen);
- }
- }
- }
-
private static boolean isProfile(@UserIdInt int userId, Context context) {
// This call requires the INTERACT_ACROSS_USERS permission.
final long callingId = Binder.clearCallingIdentity();
@@ -220,27 +198,6 @@ public final class UserProfileGroup {
}
}
- private static List<UserHandle> getEnabledUserProfiles(UserManager userManager) {
- // This call requires the QUERY_USERS permission.
- final long callingId = Binder.clearCallingIdentity();
- try {
- return userManager.getUserProfiles();
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
-
- @Nullable
- private static UserHandle getProfileParent(UserManager userManager, @UserIdInt int userId) {
- // This call requires the INTERACT_ACROSS_USERS permission.
- final long callingId = Binder.clearCallingIdentity();
- try {
- return userManager.getProfileParent(UserHandle.of(userId));
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
-
/** Returns the profile parent user id of the {@link UserProfileGroup}. */
public int getProfileParentUserId() {
return mProfileParentUserId;
@@ -262,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;
}
@@ -303,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);
@@ -342,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);
}
/**
@@ -395,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 f3f47631c..f4bed4ada 100644
--- a/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java
+++ b/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java
@@ -24,6 +24,7 @@ import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.content.pm.PermissionInfo.PROTECTION_INTERNAL;
+import static android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP;
import static android.permission.cts.PermissionUtils.getAppOp;
import static android.permission.cts.PermissionUtils.grantPermission;
import static android.permission.cts.PermissionUtils.install;
@@ -31,6 +32,7 @@ import static android.permission.cts.PermissionUtils.uninstallApp;
import static com.android.compatibility.common.util.SystemUtil.eventually;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertNotEquals;
@@ -43,14 +45,21 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
+import android.os.Build;
+import android.permission.flags.Flags;
import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.ArrayMap;
import android.util.Log;
+import androidx.test.filters.SdkSuppress;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,6 +81,9 @@ public class BackgroundPermissionsTest {
private static final UiAutomation sUiAutomation =
InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@After
public void uninstallTestApp() {
uninstallApp(APP_PKG);
@@ -79,9 +91,24 @@ public class BackgroundPermissionsTest {
@Test
@AppModeFull(reason = "Instant apps cannot read properties of other packages")
- public void verifybackgroundPermissionsProperties() throws Exception {
+ public void verifyBackgroundPropertiesForPlatformPermissions() throws Exception {
+ verifyBackgroundPermissionsProperties("android");
+ }
+
+ @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")
+ public void verifyBackgroundPropertiesForHealthPermissions() throws Exception {
+ String healthPackageName = sContext.getPackageManager().getPermissionGroupInfo(
+ HEALTH_PERMISSION_GROUP, /* flags= */ 0).packageName;
+ verifyBackgroundPermissionsProperties(healthPackageName);
+ }
+
+ private void verifyBackgroundPermissionsProperties(String packageName)
+ throws Exception {
PackageInfo pkg = sContext.getPackageManager().getPackageInfo(
- "android", PackageManager.GET_PERMISSIONS);
+ packageName, PackageManager.GET_PERMISSIONS);
ArrayMap<String, String> potentialBackgroundPermissionsToGroup = new ArrayMap<>();
int numPermissions = pkg.permissions.length;
@@ -97,11 +124,13 @@ public class BackgroundPermissionsTest {
}
}
+ int backgroundPermissionCount = 0;
for (int i = 0; i < numPermissions; i++) {
PermissionInfo permission = pkg.permissions[i];
String backgroundPermissionName = permission.backgroundPermission;
if (backgroundPermissionName != null) {
+ backgroundPermissionCount += 1;
Log.i(LOG_TAG, permission.name + "->" + backgroundPermissionName);
// foreground permissions must be dangerous
@@ -115,6 +144,8 @@ public class BackgroundPermissionsTest {
.containsKey(backgroundPermissionName));
}
}
+ // Tested packages must have at least one permission linked with a background permission.
+ assertThat(backgroundPermissionCount).isGreaterThan(0);
}
/**
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/permission/src/android/permission/cts/OneTimePermissionTest.java b/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java
index 2692c6e7c..291633aab 100644
--- a/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java
+++ b/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java
@@ -290,34 +290,18 @@ public class OneTimePermissionTest {
}
private void exitApp() {
- boolean[] hasExited = {false};
- try {
- new Thread(() -> {
- while (!hasExited[0]) {
- DreamManager mDreamManager = mContext.getSystemService(DreamManager.class);
- mUiDevice.pressBack();
- runWithShellPermissionIdentity(() -> {
- if (mDreamManager.isDreaming()) {
- mDreamManager.stopDream();
- }
- });
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- }
+ eventually(() -> {
+ mUiDevice.pressBack();
+ runWithShellPermissionIdentity(() -> {
+ DreamManager mDreamManager = mContext.getSystemService(DreamManager.class);
+ if (mDreamManager.isDreaming()) {
+ mDreamManager.stopDream();
}
- }).start();
- eventually(() -> {
- runWithShellPermissionIdentity(() -> {
- if (mActivityManager.getPackageImportance(APP_PKG_NAME)
- <= IMPORTANCE_FOREGROUND) {
- throw new AssertionError("Unable to exit application");
- }
- });
+ Assert.assertFalse("Unable to exit application",
+ mActivityManager.getPackageImportance(APP_PKG_NAME)
+ <= IMPORTANCE_FOREGROUND);
});
- } finally {
- hasExited[0] = true;
- }
+ });
}
private void clickOneTimeButton() throws Throwable {
diff --git a/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java b/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
index 776a1065e..f2d59cbe7 100755
--- a/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
+++ b/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
@@ -45,9 +45,15 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import android.content.Context;
+import android.health.connect.HealthPermissions;
import android.os.Build;
import android.permission.PermissionManager;
import android.permission.PermissionManager.SplitPermissionInfo;
+import android.permission.flags.Flags;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SdkSuppress;
@@ -56,6 +62,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.ApiLevelUtil;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,6 +78,9 @@ public class SplitPermissionsSystemTest {
private List<SplitPermissionInfo> mSplitPermissions;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void before() {
Context context = InstrumentationRegistry.getContext();
@@ -87,9 +97,14 @@ public class SplitPermissionsSystemTest {
for (SplitPermissionInfo split : mSplitPermissions) {
String splitPermission = split.getSplitPermission();
- boolean isAndroid = splitPermission.startsWith("android");
- if (!isAndroid) {
+ // Due to limitation with accessing flag values in tests, BODY_SENSORS relevant splits
+ // are handled in its dedicated tests.
+ boolean shouldSkip =
+ !splitPermission.startsWith("android")
+ || splitPermission.equals(BODY_SENSORS)
+ || splitPermission.equals(BODY_SENSORS_BACKGROUND);
+ if (shouldSkip) {
continue;
}
@@ -149,9 +164,6 @@ public class SplitPermissionsSystemTest {
case BLUETOOTH_SCAN:
assertSplit(split, Build.VERSION_CODES.S, BLUETOOTH, BLUETOOTH_ADMIN);
break;
- case BODY_SENSORS:
- assertSplit(split, Build.VERSION_CODES.TIRAMISU, BODY_SENSORS_BACKGROUND);
- break;
case ACCESS_MEDIA_LOCATION:
case READ_MEDIA_IMAGES:
case READ_MEDIA_VIDEO:
@@ -160,7 +172,56 @@ public class SplitPermissionsSystemTest {
}
}
- assertEquals(24, seenSplits.size());
+ assertEquals(23, seenSplits.size());
+ }
+
+ @RequiresFlagsDisabled({Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED})
+ @Test
+ public void
+ validateBodySensors_beforeGranularHealthPermissions_isSplitToBodySensorsBackground() {
+ assumeTrue(ApiLevelUtil.isAtLeast(Build.VERSION_CODES.Q));
+
+ mSplitPermissions.stream()
+ .filter(split -> split.getSplitPermission().equals(BODY_SENSORS))
+ .findFirst()
+ .ifPresent(
+ split ->
+ assertSplit(
+ split,
+ Build.VERSION_CODES.TIRAMISU,
+ BODY_SENSORS_BACKGROUND));
+ }
+
+ @RequiresFlagsEnabled({Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED})
+ @Test
+ public void validateBodySensors_afterGranularHealthPermissions_isSplitToReadHeartRate() {
+ // TODO: Change this to Baklava when available.
+ assumeTrue(ApiLevelUtil.isAtLeast(36));
+
+ SplitPermissionInfo legacyBodySensorPermissionInfo = null;
+ SplitPermissionInfo readHeartRatePermissionInfo = null;
+ SplitPermissionInfo bodySensorBackgroundPermissionInfo = null;
+ for (SplitPermissionInfo split : mSplitPermissions) {
+ if (split.getSplitPermission().equals(BODY_SENSORS)
+ && split.getNewPermissions().contains(BODY_SENSORS_BACKGROUND)) {
+ legacyBodySensorPermissionInfo = split;
+ } else if (split.getSplitPermission().equals(BODY_SENSORS)
+ && split.getNewPermissions().contains(HealthPermissions.READ_HEART_RATE)) {
+ readHeartRatePermissionInfo = split;
+ } else if (split.getSplitPermission().equals(BODY_SENSORS_BACKGROUND)) {
+ bodySensorBackgroundPermissionInfo = split;
+ }
+ }
+ // Assert BODY_SENSORS is split to BODY_SENSORS_BACKGROUND and READ_HEART_RATE.
+ assertSplit(
+ legacyBodySensorPermissionInfo,
+ Build.VERSION_CODES.TIRAMISU,
+ BODY_SENSORS_BACKGROUND);
+ assertSplit(readHeartRatePermissionInfo, HealthPermissions.READ_HEART_RATE);
+ // Assert BODY_SENSORS_BACKGROUND is split to READ_HEALTH_DATA_IN_BACKGROUND.
+ assertSplit(
+ bodySensorBackgroundPermissionInfo,
+ HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
}
private void assertSplit(SplitPermissionInfo split, int targetSdk, String... permission) {
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/permissionmultiuser/Android.bp b/tests/cts/permissionmultiuser/Android.bp
index b86b02205..23aafb7e0 100644
--- a/tests/cts/permissionmultiuser/Android.bp
+++ b/tests/cts/permissionmultiuser/Android.bp
@@ -33,6 +33,7 @@ android_test {
"compatibility-device-util-axt",
"ctstestrunner-axt",
"Harrier",
+ "bedstead-multiuser",
"modules-utils-build_system",
"Nene",
],
diff --git a/tests/cts/permissionmultiuser/AndroidTest.xml b/tests/cts/permissionmultiuser/AndroidTest.xml
index 10fd4e7a5..f6834036b 100644
--- a/tests/cts/permissionmultiuser/AndroidTest.xml
+++ b/tests/cts/permissionmultiuser/AndroidTest.xml
@@ -63,8 +63,8 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.permissionmultiuser.cts" />
- <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile" />
- <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser" />
+ <option name="exclude-annotation" value="com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile" />
+ <option name="exclude-annotation" value="com.android.bedstead.multiuser.annotations.RequireRunOnSecondaryUser" />
<option name="runtime-hint" value="5m" />
</test>
diff --git a/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt b/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt
index 2169f0f72..f3309bd3c 100644
--- a/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt
+++ b/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt
@@ -50,15 +50,16 @@ import android.support.test.uiautomator.UiObject2
import android.util.Log
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile
import com.android.bedstead.harrier.BedsteadJUnit4
import com.android.bedstead.harrier.DeviceState
import com.android.bedstead.permissions.annotations.EnsureHasPermission
import com.android.bedstead.harrier.annotations.EnsureSecureSettingSet
import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeature
import com.android.bedstead.harrier.annotations.RequireNotWatch
-import com.android.bedstead.harrier.annotations.RequireRunOnAdditionalUser
-import com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile
import com.android.bedstead.harrier.annotations.RequireSdkVersion
+import com.android.bedstead.multiuser.additionalUser
+import com.android.bedstead.multiuser.annotations.RequireRunOnAdditionalUser
import com.android.bedstead.permissions.CommonPermissions.INTERACT_ACROSS_USERS
import com.android.compatibility.common.util.ApiTest
import com.android.compatibility.common.util.DeviceConfigStateChangerRule
diff --git a/tests/cts/permissionpolicy/Android.bp b/tests/cts/permissionpolicy/Android.bp
index 4249f3c9d..07fde8bff 100644
--- a/tests/cts/permissionpolicy/Android.bp
+++ b/tests/cts/permissionpolicy/Android.bp
@@ -37,6 +37,7 @@ android_test {
"permission-test-util-lib",
"androidx.test.rules",
"flag-junit",
+ "android.app.flags-aconfig",
"android.permission.flags-aconfig-java-export",
],
srcs: [
diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
index d820ce10a..bceec18ae 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 -->
@@ -2070,6 +2083,21 @@
<permission android:name="android.permission.CONTROL_AUTOMOTIVE_GNSS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows an application to bind to a
+ android.service.PopulationDensityProviderService for the purpose of
+ querying population density. This prevents arbitrary clients connecting
+ to the service. The system server checks that the provider's intent
+ service explicitly sets this permission via the android:permission
+ attribute of the service.
+ This is only expected to be possessed by the system server outside of
+ tests.
+ @FlaggedApi(android.location.flags.Flags.FLAG_POPULATION_DENSITY_PROVIDER)
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_POPULATION_DENSITY_PROVIDER_SERVICE"
+ android:featureFlag="android.location.flags.population_density_provider"
+ android:protectionLevel="signature" />
+
<!-- ======================================= -->
<!-- Permissions for accessing networks -->
<!-- ======================================= -->
@@ -2574,6 +2602,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 +2660,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
@@ -3867,6 +3921,14 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION"
android:protectionLevel="internal|role" />
+ <!-- Allows an application to manage policy related to executing app functions.
+ <p>Protection level: internal|role
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS"
+ android:protectionLevel="internal|role"
+ android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager" />
+
<!-- Allows an application to set policy related to subscriptions downloaded by an admin.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
@@ -3925,6 +3987,51 @@
android:protectionLevel="signature|installer" />
<uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" />
+ <!-- Allows an application to toggle the device's advanced protection mode status.
+ @FlaggedApi("android.security.aapm_api")
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.MANAGE_ADVANCED_PROTECTION_MODE"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.security.aapm_api"/>
+
+ <!-- Allows an application to query the device's advanced protection mode status.
+ @FlaggedApi("android.security.aapm_api") -->
+ <permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE"
+ 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"
@@ -4068,6 +4175,62 @@
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"/>
+
+ <!--
+ @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW)
+ Allows an application to read the aggregated color zones on the screen for use cases like
+ TV ambient backlight usages.
+ <p> Protection level: normal
+ -->
+ <permission android:name="android.permission.READ_COLOR_ZONES"
+ android:protectionLevel="normal"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
+
+ <!-- ================================== -->
<!-- Permissions affecting the display of other applications -->
<!-- ================================== -->
<eat-comment />
@@ -4176,11 +4339,11 @@
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_GLASSES"
android:protectionLevel="normal" />
- <!-- Allows application to request to be associated with a virtual display capable of streaming
+ <!-- Allows application to request to be associated with a virtual device capable of streaming
Android applications
({@link android.companion.AssociationRequest#DEVICE_PROFILE_APP_STREAMING})
by {@link android.companion.CompanionDeviceManager}.
- <p>Not for use by third-party applications.
+ <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING"
android:protectionLevel="signature|privileged" />
@@ -4188,16 +4351,26 @@
<!-- Allows application to request to stream content from an Android host to a nearby device
({@link android.companion.AssociationRequest#DEVICE_PROFILE_NEARBY_DEVICE_STREAMING})
by {@link android.companion.CompanionDeviceManager}.
- <p>Not for use by third-party applications.
+ <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING"
android:protectionLevel="signature|privileged" />
+ <!-- Allows application to request to stream sensor data such as camera, audio and IMU between
+ an Android host and a nearby device
+ ({@link android.companion.AssociationRequest#DEVICE_PROFILE_SENSOR_DEVICE_STREAMING})
+ by {@link android.companion.CompanionDeviceManager}.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.companion.virtualdevice.flags.enable_limited_vdm_role"/>
+
<!-- Allows application to request to be associated with a vehicle head unit capable of
automotive projection
({@link android.companion.AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION})
by {@link android.companion.CompanionDeviceManager}.
- <p>Not for use by third-party applications.
+ <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"
android:protectionLevel="internal|role" />
@@ -4206,7 +4379,7 @@
and/or data with other devices, such as notifications, photos and media
({@link android.companion.AssociationRequest#DEVICE_PROFILE_COMPUTER})
by {@link android.companion.CompanionDeviceManager}.
- <p>Not for use by third-party applications.
+ <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER"
android:protectionLevel="signature|privileged" />
@@ -4244,6 +4417,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 -->
<!-- ================================== -->
@@ -4750,6 +4935,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 -->
<!-- ========================================= -->
@@ -5245,12 +5451,23 @@
<permission android:name="android.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE"
android:protectionLevel="signature" />
- <!-- Allows an application to subscribe to keyguard locked (i.e., showing) state.
- <p>Protection level: signature|role
- <p>Intended for use by ROLE_ASSISTANT and signature apps only.
+ <!-- Allows an application to subscribe to device locked and keyguard locked (i.e., showing)
+ state.
+ <p>Protection level: signature|module|role
+ <p>Intended for use by ROLE_ASSISTANT, VDM, and signature apps only.
+ -->
+ <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
+ android:protectionLevel="signature|module|role"
+ android:featureFlag="!android.security.subscribe_to_keyguard_locked_state_perm_priv_flag"/>
+
+ <!-- Allows an application to subscribe to device locked and keyguard locked (i.e., showing)
+ state.
+ <p>Protection level: signature|module|role|privileged
+ <p>Intended for use by ROLE_ASSISTANT, VDM, and signature / privileged apps only.
-->
<permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
- android:protectionLevel="signature|module|role"/>
+ android:protectionLevel="signature|module|role|privileged"
+ android:featureFlag="android.security.subscribe_to_keyguard_locked_state_perm_priv_flag"/>
<!-- Must be required by a {@link android.service.autofill.AutofillService},
to ensure that only the system can bind to it.
@@ -5538,6 +5755,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.
@@ -6148,6 +6376,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" />
+
<!-- @SystemApi Allows an application to capture audio for hotword detection.
<p>Not for use by third-party applications.</p>
@hide -->
@@ -6450,6 +6687,13 @@
<permission android:name="android.permission.BATTERY_STATS"
android:protectionLevel="signature|privileged|development" />
+ <!-- @SystemApi @hide Allows an application to collect high-precision PowerMonitor readings
+ <p>Protection level: signature|privileged|development
+ @FlaggedApi(android.permission.flags.Flags.FLAG_FINE_POWER_MONITOR_PERMISSION) -->
+ <permission android:name="android.permission.ACCESS_FINE_POWER_MONITORS"
+ android:protectionLevel="signature|privileged|development"
+ android:featureFlag="android.permission.flags.fine_power_monitor_permission" />
+
<!--Allows an application to manage statscompanion.
<p>Not for use by third-party applications.
@hide -->
@@ -7598,7 +7842,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> -->
@@ -7673,6 +7941,15 @@
<permission android:name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY"
android:protectionLevel="signature|role"/>
+ <!-- Allows an application to create displays that mirror other displays' content.
+ <p>Not for use by third-party applications.
+ <p>Protection level: internal|role
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE)
+ @hide @SystemApi -->
+ <permission android:name="android.permission.ADD_MIRROR_DISPLAY"
+ android:protectionLevel="internal|role"
+ android:featureFlag="android.companion.virtualdevice.flags.enable_limited_vdm_role" />
+
<!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. -->
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|role" />
@@ -8089,6 +8366,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.
@@ -8271,6 +8568,19 @@
android:protectionLevel="signature|privileged"/>
<!--
+ @SystemApi
+ @FlaggedApi("android.content.pm.uid_based_provider_lookup")
+ Allows an app to resolve components (e.g ContentProviders) on behalf of
+ other UIDs
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission
+ android:name="android.permission.RESOLVE_COMPONENT_FOR_UID"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.content.pm.uid_based_provider_lookup"/>
+
+ <!--
@TestApi
Signature permission reserved for testing. This should never be used to
gate any actual functionality.
@@ -8281,6 +8591,63 @@
<permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
android:protectionLevel="signature"/>
+ <!-- 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" />
+
+ <!-- @SystemApi
+ @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.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")
+ This permission is required when accessing information related to
+ singleUser-ed TIS session.
+ <p>This should only be used by OEM.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ @hide
+ -->
+ <permission android:name="android.permission.SINGLE_USER_TIS_ACCESS"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.kids_mode_tvdb_sharing"/>
+
+ <!-- @SystemApi
+ @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled")
+ This permission is required to access the specific text classifier you need from the
+ TextClassificationManager.
+ <p>Protection level: signature|role
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.permission.flags.text_classifier_choice_api_enabled"/>
+
+ <!--
+ This permission allows the system to receive PACKAGE_CHANGED broadcasts when the component
+ state of a non-exported component has been changed.
+ <p>Not for use by third-party applications. </p>
+ <p>Protection level: internal
+ @hide
+ -->
+ <permission
+ android:name="android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
+ android:protectionLevel="internal"
+ android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/>
+
+ <uses-permission
+ android:name="android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
+ android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/>
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
@@ -8614,15 +8981,6 @@
</intent-filter>
</receiver>
- <receiver android:name="com.android.server.updates.CertificateTransparencyLogInstallReceiver"
- android:exported="true"
- android:permission="android.permission.UPDATE_CONFIG">
- <intent-filter>
- <action android:name="android.intent.action.UPDATE_CT_LOGS" />
- <data android:scheme="content" android:host="*" android:mimeType="*/*" />
- </intent-filter>
- </receiver>
-
<receiver android:name="com.android.server.updates.LangIdInstallReceiver"
android:exported="true"
android:permission="android.permission.UPDATE_CONFIG">
@@ -8871,6 +9229,17 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service android:name="android.app.ecm.EnhancedConfirmationCallTrackerService"
+ android:permission="android.permission.BIND_INCALL_SERVICE"
+ android:featureFlag="android.permission.flags.enhanced_confirmation_in_call_apis_enabled"
+ android:exported="true">
+ <meta-data android:name="android.telecom.INCLUDE_SELF_MANAGED_CALLS"
+ android:value="true" />
+ <intent-filter>
+ <action android:name="android.telecom.InCallService"/>
+ </intent-filter>
+ </service>
+
<service android:name="com.android.server.companion.datatransfer.contextsync.CallMetadataSyncInCallService"
android:permission="android.permission.BIND_INCALL_SERVICE"
android:exported="true">
diff --git a/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml b/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml
index 783cd7f6b..2b40d3ed7 100644
--- a/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml
+++ b/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml
@@ -80,6 +80,12 @@
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_control_car_seats"
android:description="@string/car_permission_desc_control_car_seats"/>
+ <permission android:name="android.car.permission.READ_CAR_SEATS"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_read_car_seats"
+ android:description="@string/car_permission_desc_read_car_seats"
+ android:featureFlag="android.car.feature.vehicle_property_25q2_3p_permissions"/>
<permission android:name="android.car.permission.CONTROL_CAR_AIRBAGS"
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_control_car_airbags"
@@ -88,14 +94,55 @@
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_mileage"
android:description="@string/car_permission_desc_mileage"/>
+ <permission android:name="android.car.permission.CAR_MILEAGE_3P"
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_mileage_3p"
+ android:description="@string/car_permission_desc_mileage_3p"
+ android:featureFlag="android.car.feature.android_b_vehicle_properties" />
+ <permission android:name="android.car.permission.READ_CAR_HORN"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_read_car_horn"
+ android:description="@string/car_permission_desc_read_car_horn"
+ android:featureFlag="android.car.feature.android_b_vehicle_properties" />
+ <permission android:name="android.car.permission.CONTROL_CAR_HORN"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_horn"
+ android:description="@string/car_permission_desc_control_car_horn"
+ android:featureFlag="android.car.feature.android_b_vehicle_properties" />
+ <permission android:name="android.car.permission.READ_CAR_PEDALS"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_read_car_pedals"
+ android:description="@string/car_permission_desc_read_car_pedals"
+ android:featureFlag="android.car.feature.android_b_vehicle_properties" />
+ <permission android:name="android.car.permission.READ_BRAKE_INFO"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_read_brake_info"
+ android:description="@string/car_permission_desc_read_brake_info"
+ android:featureFlag="android.car.feature.android_b_vehicle_properties" />
<permission android:name="android.car.permission.CAR_TIRES"
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_car_tires"
android:description="@string/car_permission_desc_car_tires"/>
+ <permission android:name="android.car.permission.CAR_TIRES_3P"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_car_tires_3p"
+ android:description="@string/car_permission_desc_car_tires_3p"
+ android:featureFlag="android.car.feature.vehicle_property_25q2_3p_permissions"/>
<permission android:name="android.car.permission.READ_CAR_STEERING"
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_car_steering"
android:description="@string/car_permission_desc_car_steering"/>
+ <permission android:name="android.car.permission.READ_CAR_STEERING_3P"
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_read_car_steering_3p"
+ android:description="@string/car_permission_desc_read_car_steering_3p"
+ android:featureFlag="android.car.feature.vehicle_property_25q2_3p_permissions"/>
<permission android:name="android.car.permission.READ_CAR_DISPLAY_UNITS"
android:protectionLevel="normal"
android:label="@string/car_permission_label_read_car_display_units"
@@ -121,6 +168,12 @@
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_car_engine_detailed"
android:description="@string/car_permission_desc_car_engine_detailed"/>
+ <permission android:name="android.car.permission.CAR_ENGINE_DETAILED_3P"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_car_engine_detailed_3p"
+ android:description="@string/car_permission_desc_car_engine_detailed_3p"
+ android:featureFlag="android.car.feature.vehicle_property_25q2_3p_permissions"/>
<permission android:name="android.car.permission.CAR_DYNAMICS_STATE"
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_vehicle_dynamics_state"
@@ -186,6 +239,12 @@
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_control_car_exterior_lights"
android:description="@string/car_permission_desc_control_car_exterior_lights"/>
+ <permission android:name="android.car.permission.READ_CAR_EXTERIOR_LIGHTS"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_read_car_exterior_lights"
+ android:description="@string/car_permission_desc_car_read_exterior_lights"
+ android:featureFlag="android.car.feature.android_b_vehicle_properties" />
<permission android:name="android.car.permission.READ_CAR_INTERIOR_LIGHTS"
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_car_interior_lights"
@@ -270,6 +329,12 @@
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_driving_state"
android:description="@string/car_permission_desc_driving_state"/>
+ <permission android:name="android.car.permission.CAR_DRIVING_STATE_3P"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_driving_state_3p"
+ android:description="@string/car_permission_desc_driving_state_3p"
+ android:featureFlag="android.car.feature.vehicle_property_25q2_3p_permissions"/>
<permission android:name="android.car.permission.USE_CAR_TELEMETRY_SERVICE"
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_use_telemetry_service"
@@ -602,6 +667,12 @@
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_read_windshield_wipers"
android:description="@string/car_permission_desc_read_windshield_wipers"/>
+ <permission android:name="android.car.permission.READ_WINDSHIELD_WIPERS_3P"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_read_windshield_wipers_3p"
+ android:description="@string/car_permission_desc_read_windshield_wipers_3p"
+ android:featureFlag="android.car.feature.vehicle_property_25q2_3p_permissions"/>
<permission android:name="android.car.permission.CONTROL_WINDSHIELD_WIPERS"
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_control_windshield_wipers"
@@ -620,4 +691,16 @@
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_bind_app_card_provider"
android:description="@string/car_permission_desc_bind_app_card_provider" />
+ <permission
+ android:name="android.car.permission.RECORD_VEHICLE_PROPERTIES"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_record_vehicle_properties"
+ android:description="@string/car_permission_desc_record_vehicle_properties"
+ android:featureFlag="android.car.feature.car_property_simulation" />
+ <permission
+ android:name="android.car.permission.INJECT_VEHICLE_PROPERTIES"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_inject_vehicle_properties"
+ android:description="@string/car_permission_desc_inject_vehicle_properties"
+ android:featureFlag="android.car.feature.car_property_simulation" />
</manifest>
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/AndroidManifest.xml b/tests/cts/permissionui/AndroidManifest.xml
index 3b80b8d8b..b5c9e2ad0 100644
--- a/tests/cts/permissionui/AndroidManifest.xml
+++ b/tests/cts/permissionui/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<application>
@@ -78,6 +79,23 @@
<meta-data android:name="android.accessibilityservice"
android:resource="@xml/test_accessibilityservice"/>
</service>
+ <service android:name=".VoipHelperTestConnectionService"
+ android:exported="true"
+ android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
+ <intent-filter>
+ <action android:name="android.telecom.ConnectionService" />
+ </intent-filter>
+ </service>
+
+ <service android:name=".EcmInCallTestInCallService"
+ android:permission="android.permission.BIND_INCALL_SERVICE"
+ android:exported="true">
+ <meta-data android:name="android.telecom.INCLUDE_SELF_MANAGED_CALLS"
+ android:value="true" />
+ <intent-filter>
+ <action android:name="android.telecom.InCallService"/>
+ </intent-filter>
+ </service>
</application>
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt
index 0db639d49..421e93e2d 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt
@@ -311,6 +311,7 @@ class AppDataSharingUpdatesTest : BaseUsePermissionTest() {
}
@Test
+ @Ignore("b/381298073")
fun clickUpdate_opensAppLocationPermissionPage() {
installAndWaitTillPackageAdded(
APP_APK_NAME_31,
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt
index 68bd91546..92599b617 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt
@@ -1011,7 +1011,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() {
protected fun clickPermissionRationaleContentInAppPermission() {
clickAndWaitForWindowTransition(
By.text(getPermissionControllerString(APP_PERMISSION_RATIONALE_SUBTITLE_TEXT))
- .displayId(displayId))
+ .displayId(displayId)
+ )
}
protected fun clickPermissionRationaleViewInGrantDialog() {
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
index b9d1cb305..798b1942f 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
@@ -23,6 +23,7 @@ import android.content.AttributionSource
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
+import android.content.res.Resources
import android.hardware.camera2.CameraManager
import android.os.Build
import android.os.Process
@@ -34,7 +35,6 @@ import android.platform.test.annotations.AsbSecurityTest
import android.platform.test.rule.ScreenRecordRule
import android.provider.DeviceConfig
import android.provider.Settings
-import android.safetycenter.SafetyCenterManager
import android.server.wm.WindowManagerStateHelper
import android.util.Log
import androidx.annotation.RequiresApi
@@ -146,7 +146,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
DeviceConfig.getString(
DeviceConfig.NAMESPACE_PRIVACY,
SAFETY_CENTER_ENABLED,
- false.toString()
+ false.toString(),
)!!
}
@@ -162,7 +162,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
@Before
fun setUp() {
- // Camera and Mic are not supported for secondary user visible as a background user.
+ // Skip the tests as Camera and Mic are not supported for visible background users.
assumeFalse(isCar && UserHelper(context).isVisibleBackgroundUser())
runWithShellPermissionIdentity {
screenTimeoutBeforeTest =
@@ -170,7 +170,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
Settings.System.putLong(
context.contentResolver,
Settings.System.SCREEN_OFF_TIMEOUT,
- 1800000L
+ 1800000L,
)
}
@@ -188,7 +188,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
"feature not present on this device",
callWithShellPermissionIdentity {
CompatChanges.isChangeEnabled(PERMISSION_INDICATORS_NOT_PRESENT, Process.SYSTEM_UID)
- }
+ },
)
install()
}
@@ -203,7 +203,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
DeviceConfig.NAMESPACE_PRIVACY,
INDICATORS_FLAG,
shouldBeEnabled.toString(),
- false
+ false,
)
}
}
@@ -212,7 +212,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
@After
fun tearDown() {
- // Camera and Mic are not supported for secondary user visible as a background user.
+ // Skip the tests as Camera and Mic are not supported for visible background users.
if (isCar && UserHelper(context).isVisibleBackgroundUser()) {
return
}
@@ -223,7 +223,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
}
eventually(
{ assertIndicatorsShown(false, false, false) },
- AUTO_MIC_INDICATOR_DISMISSAL_TIMEOUT_MS
+ AUTO_MIC_INDICATOR_DISMISSAL_TIMEOUT_MS,
)
if (!wasEnabled) {
setIndicatorsEnabledStateIfNeeded(false)
@@ -232,7 +232,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
Settings.System.putLong(
context.contentResolver,
Settings.System.SCREEN_OFF_TIMEOUT,
- screenTimeoutBeforeTest
+ screenTimeoutBeforeTest,
)
}
changeSafetyCenterFlag(safetyCenterEnabled)
@@ -248,7 +248,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
useMic: Boolean,
useCamera: Boolean,
useHotword: Boolean,
- finishEarly: Boolean = false
+ finishEarly: Boolean = false,
) {
context.startActivity(
Intent(USE_INTENT_ACTION).apply {
@@ -289,7 +289,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
useMic = true,
useCamera = false,
finishEarly = true,
- safetyCenterEnabled = getSafetyCenterEnabled()
+ safetyCenterEnabled = getSafetyCenterEnabled(),
)
}
@@ -350,7 +350,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
useMic = false,
useCamera = false,
useHotword = true,
- safetyCenterEnabled = true
+ safetyCenterEnabled = true,
)
}
@@ -365,7 +365,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
useMic = false,
useCamera = true,
chainUsage = true,
- safetyCenterEnabled = true
+ safetyCenterEnabled = true,
)
}
@@ -375,12 +375,12 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
useHotword: Boolean = false,
chainUsage: Boolean = false,
safetyCenterEnabled: Boolean = false,
- finishEarly: Boolean = false
+ finishEarly: Boolean = false,
) {
Log.d(
TAG,
"testCameraAndMicIndicator useMic=$useMic useCamera=$useCamera " +
- "safetyCenterEnabled=$safetyCenterEnabled finishEarly=$finishEarly"
+ "safetyCenterEnabled=$safetyCenterEnabled finishEarly=$finishEarly",
)
// If camera is not available skip the test
if (useCamera) {
@@ -409,7 +409,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
permissionManager.checkPermissionForStartDataDelivery(
Manifest.permission.RECORD_AUDIO,
chainAttribution!!,
- ""
+ "",
)
assertEquals(PermissionManager.PERMISSION_GRANTED, ret)
}
@@ -442,7 +442,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
runWithShellPermissionIdentity {
permissionManager.finishDataDelivery(
Manifest.permission.RECORD_AUDIO,
- chainAttribution
+ chainAttribution,
)
}
}
@@ -481,7 +481,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
micInUse,
useCamera,
chainUsage,
- safetyCenterEnabled
+ safetyCenterEnabled,
)
uiDevice.pressBack()
}
@@ -490,7 +490,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
private fun assertWatchIndicatorsShown(
useMic: Boolean,
useCamera: Boolean,
- useHotword: Boolean
+ useHotword: Boolean,
) {
if (useMic || useHotword || (!useMic && !useCamera && !useHotword)) {
val iconView = UiAutomatorUtils2.waitFindObjectOrNull(By.descContains(WEAR_MIC_LABEL))
@@ -530,7 +530,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
useMic: Boolean,
useCamera: Boolean,
useHotword: Boolean,
- chainUsage: Boolean
+ chainUsage: Boolean,
) {
eventually {
// Ensure the privacy chip is present (or not)
@@ -575,17 +575,17 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
val micLabelView = uiDevice.findObject(UiSelector().textContains(micLabel))
assertFalse(
"View with text $micLabel found, but did not expect to",
- micLabelView.exists()
+ micLabelView.exists(),
)
val cameraLabelView = uiDevice.findObject(UiSelector().textContains(cameraLabel))
assertFalse(
"View with text $cameraLabel found, but did not expect to",
- cameraLabelView.exists()
+ cameraLabelView.exists(),
)
val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
assertFalse(
"View with text $APP_LABEL found, but did not expect to",
- appView.exists()
+ appView.exists(),
)
}
}
@@ -595,7 +595,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
useMic: Boolean,
useCamera: Boolean,
chainUsage: Boolean,
- safetyCenterEnabled: Boolean = false
+ safetyCenterEnabled: Boolean = false,
) {
// Ensure the privacy chip is present
if (useCamera || useMic) {
@@ -655,7 +655,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
context.packageName,
null,
null,
- permissionManager.registerAttributionSource(childAttribution)
+ permissionManager.registerAttributionSource(childAttribution),
)
attrSource = permissionManager.registerAttributionSource(attribution)
} catch (e: PackageManager.NameNotFoundException) {
@@ -705,7 +705,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
DeviceConfig.NAMESPACE_PRIVACY,
SAFETY_CENTER_ENABLED,
safetyCenterEnabled,
- false
+ false,
)
}
}
@@ -720,9 +720,21 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
}
private fun getSafetyCenterEnabled(): Boolean {
- val safetyCenterManager =
- context.getSystemService(SafetyCenterManager::class.java) ?: return false
- return runWithShellPermissionIdentity<Boolean> { safetyCenterManager.isSafetyCenterEnabled }
+ if (!SdkLevel.isAtLeastT()) {
+ // Safety Center does not exist below T.
+ return false
+ }
+ val systemResources = Resources.getSystem()
+ val resId = systemResources.getIdentifier("config_enableSafetyCenter", "bool", "android")
+ val safetyCenterSupported = context.getResources().getBoolean(resId)
+ if (!SdkLevel.isAtLeastU()) {
+ // On T, Safety Center is controlled by the DeviceConfig flag.
+ return safetyCenterSupported && safetyCenterEnabled.toBoolean()
+ }
+ // On UDC+, Safety Center is no longer controlled by DeviceConfig.
+ // The only way it can be disabled is if the OEM suppresses it at the
+ // config level using config_enableSafetyCenter.
+ return safetyCenterSupported
}
protected fun waitFindObject(selector: BySelector): UiObject2? {
@@ -731,7 +743,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
private fun findObjectWithRetry(
automatorMethod: (timeoutMillis: Long) -> UiObject2?,
- timeoutMillis: Long = TIMEOUT_MILLIS
+ timeoutMillis: Long = TIMEOUT_MILLIS,
): UiObject2? {
val startTime = SystemClock.elapsedRealtime()
return try {
@@ -754,7 +766,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
permissionControllerContext.resources.getIdentifier(
resourceName,
"string",
- "com.android.permissioncontroller"
+ "com.android.permissioncontroller",
)
return permissionControllerContext.getString(resourceId)
} catch (e: PackageManager.NameNotFoundException) {
@@ -767,7 +779,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
val micView = waitFindObject(byOneOfText(originalMicLabel, safetyCenterMicLabel))
assertNotNull(
"View with text '$originalMicLabel' or '$safetyCenterMicLabel' not found",
- micView
+ micView,
)
}
@@ -776,7 +788,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
val cameraView = waitFindObject(byOneOfText(originalCameraLabel, safetyCenterCameraLabel))
assertNotNull(
"View with text '$originalCameraLabel' or '$safetyCenterCameraLabel' not found",
- cameraView
+ cameraView,
)
}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt
new file mode 100644
index 000000000..16a27c9a8
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt
@@ -0,0 +1,201 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.app.AppOpsManager
+import android.app.Instrumentation
+import android.app.ecm.EnhancedConfirmationManager
+import android.content.ContentProviderOperation
+import android.content.Context
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Build
+import android.permission.flags.Flags
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.provider.ContactsContract
+import android.provider.ContactsContract.CommonDataKinds
+import android.provider.ContactsContract.Data
+import android.provider.ContactsContract.RawContacts
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import org.junit.After
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * This test verifies the behavior of the Enhanced Confirmation Manager APIs that deal with unknown
+ * callers
+ */
+@AppModeFull(reason = "Instant apps cannot install packages")
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+@RequiresFlagsEnabled(Flags.FLAG_UNKNOWN_CALL_PACKAGE_INSTALL_BLOCKING_ENABLED)
+// @CddTest(requirement = "TBD")
+class EnhancedConfirmationInCallTest {
+ private val ecm = context.getSystemService(EnhancedConfirmationManager::class.java)!!
+ private val packageManager = context.packageManager
+ private val addedContacts = mutableMapOf<String, List<Uri>>()
+
+ @JvmField
+ @Rule
+ val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @Before
+ fun assumeNotAutoOrTv() {
+ Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
+ }
+
+ companion object {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context: Context = instrumentation.targetContext
+ private lateinit var voipService: VoipCallHelper
+
+ @JvmStatic
+ @BeforeClass
+ fun setupVoipService() {
+ voipService = VoipCallHelper(context)
+ voipService.registerPhoneAccount()
+ }
+
+ @JvmStatic
+ @AfterClass
+ fun tearDownVoipService() {
+ voipService.removePhoneAccount()
+ }
+
+ const val CONTACT_DISPLAY_NAME = "Alice Bobson"
+ const val NON_CONTACT_DISPLAY_NAME = "Eve McEve"
+ const val CONTACT_PHONE_NUMBER = "8888888888"
+ const val NON_CONTACT_PHONE_NUMBER = "1111111111"
+ }
+
+ private fun addContact(displayName: String, phoneNumber: String) {
+ runWithShellPermissionIdentity {
+ val ops: ArrayList<ContentProviderOperation> = ArrayList()
+ ops.add(
+ ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
+ .withValue(RawContacts.ACCOUNT_TYPE, "test type")
+ .withValue(RawContacts.ACCOUNT_NAME, "test account")
+ .build()
+ )
+ ops.add(
+ ContentProviderOperation.newInsert(Data.CONTENT_URI)
+ .withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ .withValue(Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+ .withValue(CommonDataKinds.StructuredName.DISPLAY_NAME, displayName)
+ .build()
+ )
+ ops.add(
+ ContentProviderOperation.newInsert(Data.CONTENT_URI)
+ .withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ .withValue(Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+ .withValue(CommonDataKinds.Phone.NUMBER, phoneNumber)
+ .build()
+ )
+ val results = context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
+ val resultsForDisplayName = mutableListOf<Uri>()
+ results.forEach { resultsForDisplayName.add(it.uri!!) }
+ addedContacts[displayName] = resultsForDisplayName
+ }
+ }
+
+ private fun removeContact(displayName: String) {
+ runWithShellPermissionIdentity {
+ var totalRowsRemoved = 0
+ for (data in addedContacts[displayName] ?: emptyList()) {
+ totalRowsRemoved += context.contentResolver.delete(data, null)
+ }
+ // There are multiple contacts tables, and removing from the raw_contacts table
+ // can cause row removals from the data table, so we may get some uris that don't
+ // report a delete, but we should get at least one, and not more than the number of uris
+ Assert.assertNotEquals(
+ "Expected at least one contact row to be removed",
+ 0,
+ totalRowsRemoved,
+ )
+ Assert.assertTrue(
+ "Unexpectedly large number of contact rows removed",
+ totalRowsRemoved <= (addedContacts[displayName]?.size ?: 0),
+ )
+ addedContacts.remove(displayName)
+ }
+ }
+
+ @After
+ fun tearDown() {
+ voipService.endCallAndWaitForInactive()
+ addedContacts.keys.forEach { removeContact(it) }
+ }
+
+ private fun isSettingRestricted(): Boolean {
+ return callWithShellPermissionIdentity {
+ ecm.isRestricted(context.packageName, AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES)
+ }
+ }
+
+ @Test
+ fun testIncomingCall_NonContact() {
+ voipService.createCallAndWaitForActive(NON_CONTACT_DISPLAY_NAME, NON_CONTACT_PHONE_NUMBER)
+ Assert.assertTrue(isSettingRestricted())
+ voipService.endCallAndWaitForInactive()
+ Assert.assertFalse(isSettingRestricted())
+ }
+
+ @Test
+ fun testIncomingCall_Contact_DisplayNameMatches_PhoneNotGiven() {
+ addContact(CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER)
+ // If no phone number is given, the display name will be checked
+ voipService.createCallAndWaitForActive(CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER)
+ Assert.assertFalse(isSettingRestricted())
+ voipService.endCallAndWaitForInactive()
+ Assert.assertFalse(isSettingRestricted())
+ }
+
+ @Test
+ fun testIncomingCall_Contact_PhoneNumberMatches() {
+ addContact(CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER)
+ // If the phone number matches, the display name is not checked
+ voipService.createCallAndWaitForActive(NON_CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER)
+ Assert.assertFalse(isSettingRestricted())
+ voipService.endCallAndWaitForInactive()
+ Assert.assertFalse(isSettingRestricted())
+ }
+
+ @Test
+ fun testCall_DoesntBecomeTrustedIfCallerAddedDuringCall() {
+ val tempContactDisplay = "TEMP CONTACT"
+ val tempContactPhone = "999-999-9999"
+ voipService.createCallAndWaitForActive(tempContactDisplay, tempContactPhone)
+ addContact(tempContactDisplay, tempContactPhone)
+ // State should not be recomputed just because the contact is newly added
+ Assert.assertTrue(isSettingRestricted())
+ voipService.endCallAndWaitForInactive()
+ voipService.createCallAndWaitForActive(tempContactDisplay, tempContactPhone)
+ // A new call should recognize our contact, and mark the call as trusted
+ Assert.assertFalse(isSettingRestricted())
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt
index c8298b19a..75a8914bf 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt
@@ -141,7 +141,7 @@ class NotificationPermissionTest : BaseUsePermissionTest() {
launchApp()
killTestApp()
launchApp()
- waitFindObject(By.textContains(ALLOW))
+ waitFindObject(By.textContains(ALLOW).displayId(displayId))
clickPermissionRequestAllowButton()
}
@@ -201,9 +201,10 @@ class NotificationPermissionTest : BaseUsePermissionTest() {
installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
launchApp(startSecondActivity = true)
if (isAutomotive || isWatch) {
- waitFindObject(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)))
+ waitFindObject(
+ By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)).displayId(displayId))
} else {
- waitFindObject(By.res(ALLOW_BUTTON))
+ waitFindObject(By.res(ALLOW_BUTTON).displayId(displayId))
}
pressBack()
clickPermissionRequestAllowButton()
@@ -239,7 +240,7 @@ class NotificationPermissionTest : BaseUsePermissionTest() {
try {
// Watch does not have app bar
if (!isWatch) {
- waitFindObject(By.textContains(SECOND_ACTIVITY_LABEL))
+ waitFindObject(By.textContains(SECOND_ACTIVITY_LABEL).displayId(displayId))
}
assertDialogNotShowing()
} finally {
@@ -400,7 +401,7 @@ class NotificationPermissionTest : BaseUsePermissionTest() {
// Watch does not have app bar
if (!isWatch) {
- waitFindObject(By.textContains(ACTIVITY_LABEL))
+ waitFindObject(By.textContains(ACTIVITY_LABEL).displayId(displayId))
}
}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
index d509add3a..e71ac32a5 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
@@ -17,15 +17,20 @@
package android.permissionui.cts
import android.os.Build
+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)
@@ -55,23 +60,32 @@ class PermissionSplitTest : BaseUsePermissionTest() {
testLocationPermissionSplit(false)
}
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @SdkSuppress(
+ minSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ maxSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ )
@Test
- fun testBodySensorSplit() {
+ fun testBodySensorSplitOnTToV() {
installPackage(APP_APK_PATH_31)
testBodySensorPermissionSplit(true)
}
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @SdkSuppress(
+ minSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ maxSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ )
@Test
- fun testBodySensorSplit32() {
+ fun testBodySensorSplit32OnTToV() {
installPackage(APP_APK_PATH_32)
testBodySensorPermissionSplit(true)
}
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @SdkSuppress(
+ minSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ maxSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ )
@Test
- fun testBodySensorNonSplit() {
+ fun testBodySensorNonSplitonTToV() {
installPackage(APP_APK_PATH_LATEST)
testBodySensorPermissionSplit(false)
}
@@ -82,7 +96,7 @@ class PermissionSplitTest : BaseUsePermissionTest() {
requestAppPermissionsAndAssertResult(
android.Manifest.permission.ACCESS_FINE_LOCATION to true,
- waitForWindowTransition = false
+ waitForWindowTransition = false,
) {
if (expectSplit) {
clickPermissionRequestSettingsLinkAndAllowAlways()
@@ -104,7 +118,7 @@ class PermissionSplitTest : BaseUsePermissionTest() {
requestAppPermissionsAndAssertResult(
android.Manifest.permission.BODY_SENSORS to true,
- waitForWindowTransition = false
+ waitForWindowTransition = false,
) {
if (expectSplit) {
clickPermissionRequestSettingsLinkAndAllowAlways()
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt
index e434e9c70..55f028e17 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt
@@ -190,7 +190,7 @@ class ReviewAccessibilityServicesTest {
!uiDevice.performActionAndWait(
{ block() },
Until.newWindow(),
- NEW_WINDOW_TIMEOUT_MILLIS
+ NEW_WINDOW_TIMEOUT_MILLIS,
)
if (timeoutOccurred) {
@@ -212,17 +212,11 @@ class ReviewAccessibilityServicesTest {
private fun waitForSettingsButtonToDisappear() {
SystemUtil.eventually {
- findPCObjectByClassAndText(false,
- "android.widget.Button",
- "Settings"
- )
+ findPCObjectByClassAndText(false, "android.widget.Button", "Settings")
}
}
- private fun findObjectByTextWithoutRetry(
- shouldBePresent: Boolean,
- text: String,
- ): UiObject2? {
+ private fun findObjectByTextWithoutRetry(shouldBePresent: Boolean, text: String): UiObject2? {
val containsWithoutCaseSelector =
By.text(Pattern.compile(".*$text.*", Pattern.CASE_INSENSITIVE))
val view =
@@ -235,7 +229,7 @@ class ReviewAccessibilityServicesTest {
assertEquals(
"Expected to find view with text $text: $shouldBePresent",
shouldBePresent,
- view != null
+ view != null,
)
return view
}
@@ -251,15 +245,16 @@ class ReviewAccessibilityServicesTest {
private fun findPCObjectByClassAndText(
shouldBePresent: Boolean,
className: String,
- text: String
+ text: String,
): UiObject2? {
- val selector = By.pkg(packageName)
- .clazz(className)
- .text(text)
+ val selector = By.pkg(packageName).clazz(className).text(text)
val view = waitFindObjectOrNull(selector)
assertEquals(
"Expected to find view with packageName '$packageName' className '$className' " +
- "text '$text' : $shouldBePresent", shouldBePresent, view != null)
+ "text '$text' : $shouldBePresent",
+ shouldBePresent,
+ view != null,
+ )
return view
}
}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/VoipCallHelper.kt b/tests/cts/permissionui/src/android/permissionui/cts/VoipCallHelper.kt
new file mode 100644
index 000000000..480d7bff3
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/VoipCallHelper.kt
@@ -0,0 +1,171 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.content.ComponentName
+import android.content.Context
+import android.net.Uri
+import android.os.Bundle
+import android.os.Process
+import android.permissionui.cts.VoipCallHelper.Companion.EXTRA_DISPLAY_NAME
+import android.permissionui.cts.VoipCallHelper.Companion.awaitingCallStateLatch
+import android.permissionui.cts.VoipCallHelper.Companion.currentActiveConnection
+import android.telecom.Connection
+import android.telecom.ConnectionRequest
+import android.telecom.ConnectionService
+import android.telecom.DisconnectCause
+import android.telecom.PhoneAccount
+import android.telecom.PhoneAccountHandle
+import android.telecom.TelecomManager
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.Assert
+
+/** A helper class which can register a phone account, and make/end VOIP phone calls */
+class VoipCallHelper(val context: Context) {
+ private val telecomManager by lazy { context.getSystemService(TelecomManager::class.java) }
+ private lateinit var phoneAccount: PhoneAccount
+ private val accountHandle =
+ PhoneAccountHandle(
+ ComponentName(context, VoipHelperTestConnectionService::class.java),
+ "cts-voip-helper-test",
+ Process.myUserHandle(),
+ )
+
+ init {
+ registerPhoneAccount()
+ }
+
+ companion object {
+ var currentActiveConnection: VoIPConnection? = null
+ var awaitingCallStateLatch: CallPlacedLatch? = null
+
+ const val EXTRA_DISPLAY_NAME = "display_name"
+ const val CUSTOM_ADDRESS_SCHEMA = "custom_schema"
+ const val CALL_STATE_WAIT_MS = 1000L
+ const val CALL_TIMEOUT_MS = 10000L
+ }
+
+ fun registerPhoneAccount() {
+ val phoneAccountBuilder = PhoneAccount.builder(accountHandle, "CTS VOIP HELPER")
+ phoneAccountBuilder.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+ // see b/343674176. Some OEMs expect the PhoneAccount.getExtras() to be non-null
+ val defaultBundle = Bundle()
+ phoneAccountBuilder.setExtras(defaultBundle)
+
+ // build and register the PhoneAccount via the Platform API
+ phoneAccount = phoneAccountBuilder.build()
+ telecomManager.registerPhoneAccount(phoneAccount)
+ }
+
+ fun removePhoneAccount() {
+ telecomManager.unregisterPhoneAccount(phoneAccount.accountHandle)
+ }
+
+ fun createCallAndWaitForActive(displayName: String?, phoneNumber: String?) {
+ val extras = Bundle()
+
+ val phoneUri =
+ if (phoneNumber != null) {
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null)
+ } else {
+ // If we don't have a phone number, provide a custom address URI, like many VOIP
+ // apps that aren't tied to a phone number do
+ Uri.fromParts(CUSTOM_ADDRESS_SCHEMA, "custom_address", null)
+ }
+ if (displayName != null) {
+ extras.putString(EXTRA_DISPLAY_NAME, displayName)
+ }
+ extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, phoneUri)
+ awaitingCallStateLatch = CallPlacedLatch(phoneUri, displayName)
+ telecomManager.addNewIncomingCall(phoneAccount.accountHandle, extras)
+ Assert.assertTrue(
+ "Timed out waiting for call to start",
+ awaitingCallStateLatch!!.await(CALL_TIMEOUT_MS, TimeUnit.MILLISECONDS),
+ )
+ // TODO b/379941144: Replace wait with waiting until a test InCallService gets a callback
+ Thread.sleep(CALL_STATE_WAIT_MS)
+ }
+
+ fun endCallAndWaitForInactive() {
+ currentActiveConnection?.let { connection ->
+ connection.setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
+ connection.destroy()
+ // TODO b/379941144: Replace wait with waiting until a test InCallService gets a
+ // callback
+ Thread.sleep(CALL_STATE_WAIT_MS)
+ }
+ currentActiveConnection = null
+ }
+}
+
+class CallPlacedLatch(val address: Uri?, val displayName: String?) : CountDownLatch(1) {
+ fun nameAndNumberMatch(connection: Connection): Boolean {
+ return connection.address == address && connection.callerDisplayName == displayName
+ }
+}
+
+class VoIPConnection : Connection() {
+ init {
+ setConnectionProperties(PROPERTY_SELF_MANAGED)
+ setAudioModeIsVoip(true)
+ setActive()
+ }
+
+ override fun onShowIncomingCallUi() {
+ super.onShowIncomingCallUi()
+ setActive()
+ currentActiveConnection = this
+ if (awaitingCallStateLatch?.nameAndNumberMatch(this) == true) {
+ awaitingCallStateLatch?.countDown()
+ }
+ }
+}
+
+class VoipHelperTestConnectionService : ConnectionService() {
+ override fun onCreateOutgoingConnection(
+ connectionManagerPhoneAccount: PhoneAccountHandle,
+ request: ConnectionRequest,
+ ): Connection {
+ return createConnection(request)
+ }
+
+ override fun onCreateIncomingConnection(
+ connectionManagerPhoneAccount: PhoneAccountHandle?,
+ request: ConnectionRequest?,
+ ): Connection {
+ return createConnection(request)
+ }
+
+ private fun createConnection(request: ConnectionRequest?): Connection {
+ val connection = VoIPConnection()
+ if (request?.extras?.containsKey(EXTRA_DISPLAY_NAME) == true) {
+ connection.setCallerDisplayName(
+ request.extras.getString(EXTRA_DISPLAY_NAME),
+ TelecomManager.PRESENTATION_ALLOWED,
+ )
+ connection.setAddress(
+ request.extras.getParcelable(
+ TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
+ Uri::class.java,
+ ),
+ TelecomManager.PRESENTATION_ALLOWED,
+ )
+ }
+ return connection
+ }
+}
diff --git a/tests/cts/role/Android.bp b/tests/cts/role/Android.bp
index f0095b7dd..9f1e6cff6 100644
--- a/tests/cts/role/Android.bp
+++ b/tests/cts/role/Android.bp
@@ -30,9 +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 e31659dbc..f26bc0eb5 100644
--- a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
@@ -16,6 +16,7 @@
package android.app.role.cts;
+import static com.android.bedstead.multiuser.MultiUserDeviceStateExtensionsKt.privateProfile;
import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
@@ -44,6 +45,7 @@ import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.permission.flags.Flags;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -66,7 +68,7 @@ import androidx.test.uiautomator.Until;
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.EnsureHasPrivateProfile;
+import com.android.bedstead.multiuser.annotations.EnsureHasPrivateProfile;
import com.android.bedstead.nene.types.OptionalBoolean;
import com.android.compatibility.common.util.DisableAnimationRule;
import com.android.compatibility.common.util.FreezeRotationRule;
@@ -103,6 +105,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";
@@ -184,6 +188,7 @@ public class RoleManagerTest {
@Before
public void setUp() throws Exception {
+ assumeTrue(RoleManagerUtil.INSTANCE.isCddCompliantScreenSize());
saveRoleHolder();
installApp();
wakeUpScreen();
@@ -864,7 +869,7 @@ public class RoleManagerTest {
return;
}
- UserHandle privateProfile = sDeviceState.privateProfile().userHandle();
+ UserHandle privateProfile = privateProfile(sDeviceState).userHandle();
assertThat(privateProfile).isNotNull();
installPackage(APP_APK_PATH, privateProfile);
installPackage(APP_CLONE_APK_PATH, privateProfile);
@@ -1347,6 +1352,283 @@ public class RoleManagerTest {
});
}
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @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)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @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)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @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)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @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)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @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());
+ });
+ }
+
+ @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotGetDefaultHoldersForTestFlagDisabled() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalStateException.class, () ->
+ sRoleManager.getDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotGetDefaultHoldersForTestNoPermissions() throws Exception {
+ assertThrows(SecurityException.class, () ->
+ sRoleManager.getDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME));
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotGetDefaultHoldersForTestEmptyRoleName() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.getDefaultHoldersForTest(""));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotGetDefaultHoldersForTestNonTestRoleName() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.getDefaultHoldersForTest(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER));
+ });
+ }
+
+ @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotSetDefaultHoldersForTestFlagDisabled() throws Exception {
+ List<String> testRoleHolders = List.of("a", "b", "c");
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalStateException.class, () ->
+ sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME,
+ testRoleHolders));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotSetDefaultHoldersForTestNoPermissions() throws Exception {
+ List<String> testRoleHolders = List.of("a", "b", "c");
+ assertThrows(SecurityException.class, () ->
+ sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME,
+ testRoleHolders));
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotSetDefaultHoldersForTestEmptyRoleName() throws Exception {
+ List<String> testRoleHolders = List.of("a", "b", "c");
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.setDefaultHoldersForTest("", testRoleHolders));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotSetDefaultHoldersForTestNonTestRoleName() throws Exception {
+ List<String> testRoleHolders = List.of("a", "b", "c");
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.setDefaultHoldersForTest(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER, testRoleHolders));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotSetDefaultHoldersForTestNullPackageNames() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(NullPointerException.class, () ->
+ sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME,
+ null));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void setAndGetDefaultHolders() throws Exception {
+ List<String> testRoleHolders = List.of("a", "b", "c");
+ runWithShellPermissionIdentity(() -> {
+ sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME,
+ testRoleHolders);
+ List<String> roleHolders =
+ sRoleManager.getDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME);
+ assertThat(roleHolders).isEqualTo(testRoleHolders);
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void setAndGetDefaultHoldersNoRoleHolders() throws Exception {
+ List<String> initialRoleHolders = List.of("a", "b", "c");
+ List<String> testRoleHolders = Collections.emptyList();
+ runWithShellPermissionIdentity(() -> {
+ sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME,
+ initialRoleHolders);
+ sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME,
+ testRoleHolders);
+ List<String> roleHolders =
+ sRoleManager.getDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME);
+ assertThat(roleHolders).isEqualTo(testRoleHolders);
+ });
+ }
+
+ @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotGetIsRoleVisibleForTestFlagDisabled() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalStateException.class, () ->
+ sRoleManager.isRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotGetIsRoleVisibleForTestNoPermissions() throws Exception {
+ assertThrows(SecurityException.class, () ->
+ sRoleManager.isRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME));
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotGetIsRoleVisibleForTestEmptyRoleName() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.isRoleVisibleForTest(""));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotGetIsRoleVisibleForTestNonTestRoleName() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.isRoleVisibleForTest(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER));
+ });
+ }
+
+ @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotSetRoleVisibleForTestFlagDisabled() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalStateException.class, () ->
+ sRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, false));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotSetRoleVisibleForTestNoPermissions() throws Exception {
+ assertThrows(SecurityException.class, () ->
+ sRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, false));
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotSetRoleVisibleForTestEmptyRoleName() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.setRoleVisibleForTest("", false));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void cannotSetRoleVisibleForTestNonTestRoleName() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.setRoleVisibleForTest(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER,
+ false));
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void setAndGetIsRoleVisibleForTestSetTrue() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ sRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, true);
+ boolean isRoleVisibleForTest =
+ sRoleManager.isRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME);
+ assertThat(isRoleVisibleForTest).isEqualTo(true);
+ });
+ }
+
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ public void setAndGetIsRoleVisibleForTestSetFalse() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ sRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, false);
+ boolean isRoleVisibleForTest =
+ sRoleManager.isRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME);
+ assertThat(isRoleVisibleForTest).isEqualTo(false);
+ });
+ }
+
@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/RoleManagerUtil.kt b/tests/cts/role/src/android/app/role/cts/RoleManagerUtil.kt
new file mode 100644
index 000000000..10a3834a2
--- /dev/null
+++ b/tests/cts/role/src/android/app/role/cts/RoleManagerUtil.kt
@@ -0,0 +1,62 @@
+/*
+ * 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 android.app.role.cts
+
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.util.Log
+
+object RoleManagerUtil {
+ private val TAG = RoleManagerUtil::class.java.getSimpleName()
+
+ /**
+ * This method checks for the minimum screen size described in CDD {@see
+ * https://source.android.com/docs/compatibility/14/android-14-cdd#7111_screen_size_and_shape}
+ */
+ fun isCddCompliantScreenSize(): Boolean {
+ if (
+ Resources.getSystem().configuration.uiMode and Configuration.UI_MODE_TYPE_MASK ==
+ Configuration.UI_MODE_TYPE_WATCH
+ ) {
+ Log.d(TAG, "UI mode is UI_MODE_TYPE_WATCH, skipping the min dp check")
+ return true
+ }
+
+ val screenSize =
+ Resources.getSystem().configuration.screenLayout and
+ Configuration.SCREENLAYOUT_SIZE_MASK
+ return when (screenSize) {
+ Configuration.SCREENLAYOUT_SIZE_SMALL -> hasMinScreenSize(426, 320)
+ Configuration.SCREENLAYOUT_SIZE_NORMAL -> hasMinScreenSize(480, 320)
+ Configuration.SCREENLAYOUT_SIZE_LARGE -> hasMinScreenSize(640, 480)
+ Configuration.SCREENLAYOUT_SIZE_XLARGE -> hasMinScreenSize(960, 720)
+ else -> {
+ Log.e(TAG, "Unknown screen size: $screenSize")
+ true
+ }
+ }
+ }
+
+ private fun hasMinScreenSize(minWidthDp: Int, minHeightDp: Int): Boolean {
+ val dpi = Resources.getSystem().displayMetrics.densityDpi
+ val widthDp = (160f / dpi) * Resources.getSystem().displayMetrics.widthPixels
+ val heightDp = (160f / dpi) * Resources.getSystem().displayMetrics.heightPixels
+
+ // CDD does seem to follow width & height convention correctly, hence checking both ways
+ return (widthDp >= minWidthDp && heightDp >= minHeightDp) ||
+ (widthDp >= minHeightDp && heightDp >= minWidthDp)
+ }
+}
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..c9dc97b8f 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,31 @@ class RoleShellCommandTest {
assertThat(isBypassingRoleQualification()).isFalse()
}
+ @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @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)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @Test
+ fun setActiveUserForNonProfileGroupExclusiveRoleThenFails() {
+ assertThrows(AssertionError::class.java) { setActiveUserForRole(ROLE_NAME, userId) }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+ @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 +235,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/Android.bp b/tests/cts/rolemultiuser/Android.bp
new file mode 100644
index 000000000..7a49bc4e5
--- /dev/null
+++ b/tests/cts/rolemultiuser/Android.bp
@@ -0,0 +1,50 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsRoleMultiUserTestCases",
+ defaults: ["mts-target-sdk-version-current"],
+ sdk_version: "test_current",
+ min_sdk_version: "30",
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "bedstead-flags",
+ "bedstead-multiuser",
+ "com.android.permission.flags-aconfig-java-export",
+ "ctstestrunner-axt",
+ "Harrier",
+ "flag-junit",
+ "Nene",
+ "truth",
+ ],
+
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "mcts-permission",
+ ],
+
+ data: [
+ ":CtsRoleTestApp",
+ ],
+}
diff --git a/tests/cts/rolemultiuser/AndroidManifest.xml b/tests/cts/rolemultiuser/AndroidManifest.xml
new file mode 100644
index 000000000..77710a5aa
--- /dev/null
+++ b/tests/cts/rolemultiuser/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.rolemultiuser.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ <activity
+ android:name=".WaitForResultActivity"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"/>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.app.rolemultiuser.cts"
+ android:label="CTS multi-user tests of android.app.role">
+ </instrumentation>
+</manifest>
diff --git a/tests/cts/rolemultiuser/AndroidTest.xml b/tests/cts/rolemultiuser/AndroidTest.xml
new file mode 100644
index 000000000..510b70fdb
--- /dev/null
+++ b/tests/cts/rolemultiuser/AndroidTest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<configuration description="Config for CTS role multi-user test cases">
+
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="permissions" />
+ <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="multiuser" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsRoleMultiUserTestCases.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts-role" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts-role"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsRoleTestApp.apk->/data/local/tmp/cts-role/CtsRoleTestApp.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.app.rolemultiuser.cts" />
+ <option name="exclude-annotation" value="com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile" />
+ <option name="exclude-annotation" value="com.android.bedstead.multiuser.annotations.RequireRunOnSecondaryUser" />
+ <option name="runtime-hint" value="5m" />
+ </test>
+</configuration>
diff --git a/tests/cts/rolemultiuser/TEST_MAPPING b/tests/cts/rolemultiuser/TEST_MAPPING
new file mode 100644
index 000000000..323e3094c
--- /dev/null
+++ b/tests/cts/rolemultiuser/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "CtsRoleMultiUserTestCases"
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsRoleMultiUserTestCases[com.google.android.permission.apex]"
+ }
+ ]
+}
diff --git a/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt
new file mode 100644
index 000000000..ee00c2c39
--- /dev/null
+++ b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt
@@ -0,0 +1,1602 @@
+/*
+ * 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 android.app.rolemultiuser.cts
+
+import android.app.Activity
+import android.app.role.RoleManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Process
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.Pair
+import androidx.test.filters.SdkSuppress
+import androidx.test.rule.ActivityTestRule
+import androidx.test.uiautomator.By
+import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile
+import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile
+import com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile
+import com.android.bedstead.enterprise.workProfile
+import com.android.bedstead.flags.annotations.RequireFlagsEnabled
+import com.android.bedstead.harrier.BedsteadJUnit4
+import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.multiuser.annotations.EnsureCanAddUser
+import com.android.bedstead.multiuser.annotations.EnsureHasAdditionalUser
+import com.android.bedstead.multiuser.annotations.EnsureHasPrivateProfile
+import com.android.bedstead.multiuser.annotations.EnsureHasSecondaryUser
+import com.android.bedstead.multiuser.annotations.RequireRunNotOnSecondaryUser
+import com.android.bedstead.multiuser.annotations.RequireRunOnPrimaryUser
+import com.android.bedstead.multiuser.privateProfile
+import com.android.bedstead.multiuser.secondaryUser
+import com.android.bedstead.nene.TestApis.context
+import com.android.bedstead.nene.TestApis.permissions
+import com.android.bedstead.nene.TestApis.users
+import com.android.bedstead.nene.types.OptionalBoolean
+import com.android.bedstead.nene.users.UserReference
+import com.android.bedstead.nene.users.UserType
+import com.android.bedstead.permissions.CommonPermissions.INTERACT_ACROSS_USERS_FULL
+import com.android.bedstead.permissions.CommonPermissions.MANAGE_DEFAULT_APPLICATIONS
+import com.android.bedstead.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS
+import com.android.bedstead.permissions.annotations.EnsureDoesNotHavePermission
+import com.android.bedstead.permissions.annotations.EnsureHasPermission
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
+import com.android.compatibility.common.util.SystemUtil
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.UiAutomatorUtils2.getUiDevice
+import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import java.util.Objects
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
+import org.junit.After
+import org.junit.Assert.assertThrows
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
+@RunWith(BedsteadJUnit4::class)
+class RoleManagerMultiUserTest {
+
+ @JvmField
+ @Rule
+ var activityRule: ActivityTestRule<WaitForResultActivity> =
+ ActivityTestRule(WaitForResultActivity::class.java)
+
+ @Before
+ @Throws(java.lang.Exception::class)
+ fun setUp() {
+ installAppForAllUsers()
+ }
+
+ @After
+ @Throws(java.lang.Exception::class)
+ fun tearDown() {
+ uninstallAppForAllUsers()
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @EnsureHasPrivateProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(Exception::class)
+ fun isAvailableAsUserForProfileGroupExclusiveRole() {
+ val workProfileRoleManager = getRoleManagerForUser(deviceState.workProfile())
+ val privateProfileRoleManager = getRoleManagerForUser(deviceState.privateProfile())
+
+ assertThat(roleManager.isRoleAvailable(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)).isTrue()
+ assertThat(workProfileRoleManager.isRoleAvailable(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isTrue()
+ assertThat(privateProfileRoleManager.isRoleAvailable(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isFalse()
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotGetActiveUserForNonCrossUserRole() {
+ assertThrows(IllegalArgumentException::class.java) {
+ roleManager.getActiveUserForRole(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER)
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(MANAGE_ROLE_HOLDERS)
+ @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL)
+ @Test
+ @Throws(Exception::class)
+ fun cannotGetActiveUserForRoleWithoutInteractAcrossUserPermission() {
+ assertThrows(SecurityException::class.java) {
+ roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL)
+ @EnsureDoesNotHavePermission(MANAGE_ROLE_HOLDERS, MANAGE_DEFAULT_APPLICATIONS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotGetActiveUserForRoleWithoutManageRoleAndManageDefaultApplicationsPermission() {
+ assertThrows(SecurityException::class.java) {
+ roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForNonCrossUserRole() {
+ assertThrows(IllegalArgumentException::class.java) {
+ roleManager.setActiveUserForRole(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER,
+ Process.myUserHandle(),
+ 0,
+ )
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(MANAGE_ROLE_HOLDERS)
+ @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleWithoutInteractAcrossUserPermission() {
+ assertThrows(SecurityException::class.java) {
+ roleManager.setActiveUserForRole(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ Process.myUserHandle(),
+ 0,
+ )
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL)
+ @EnsureDoesNotHavePermission(MANAGE_ROLE_HOLDERS, MANAGE_DEFAULT_APPLICATIONS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleWithoutManageRoleAndManageDefaultApplicationsPermission() {
+ assertThrows(SecurityException::class.java) {
+ roleManager.setActiveUserForRole(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ Process.myUserHandle(),
+ 0,
+ )
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleToNonExistentUser() {
+ val targetActiveUser = users().nonExisting().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isNotEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasPrivateProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleToPrivateProfileUser() {
+ val targetActiveUser = deviceState.privateProfile().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isNotEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasAdditionalUser(installInstrumentedApp = OptionalBoolean.TRUE)
+ @EnsureHasSecondaryUser
+ @RequireRunNotOnSecondaryUser
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleToUserNotInProfileGroup() {
+ val targetActiveUser = deviceState.secondaryUser().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isNotEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @EnsureHasAdditionalUser(installInstrumentedApp = OptionalBoolean.TRUE)
+ @EnsureHasSecondaryUser
+ @RequireRunNotOnSecondaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun ensureRoleHasActiveUser() {
+ val primaryUser = deviceState.initialUser()
+ val primaryUserId = primaryUser.userHandle().identifier
+ val primaryUserRoleManager = getRoleManagerForUser(primaryUser)
+ val secondaryUser = deviceState.secondaryUser()
+ val secondaryUserId = secondaryUser.userHandle().identifier
+ val secondaryUserRoleManager = getRoleManagerForUser(secondaryUser)
+
+ assertWithMessage("Expected active user in profile group for user $primaryUserId")
+ .that(primaryUserRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isNotNull()
+ assertWithMessage("Expected active user in profile group for user $secondaryUserId")
+ .that(
+ secondaryUserRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ )
+ .isNotNull()
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureDoesNotHavePermission(MANAGE_DEFAULT_APPLICATIONS)
+ @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @Test
+ @Throws(Exception::class)
+ fun setAndGetActiveUserForRoleSetCurrentUserWithManageRoleHoldersPermission() {
+ assumeFalse(
+ "setActiveUser not supported for private profile",
+ users().current().type().name() == PRIVATE_PROFILE_TYPE_NAME,
+ )
+
+ val targetActiveUser = users().current().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_DEFAULT_APPLICATIONS)
+ @EnsureDoesNotHavePermission(MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @Test
+ @Throws(Exception::class)
+ fun setAndGetActiveUserForRoleSetCurrentUserWithManageDefaultApplicationPermission() {
+ assumeFalse(
+ "setActiveUser not supported for private profile",
+ users().current().type().name() == PRIVATE_PROFILE_TYPE_NAME,
+ )
+
+ val targetActiveUser = users().current().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @Test
+ @Throws(Exception::class)
+ fun setAndGetActiveUserForRoleSetCurrentUserEnsureRoleNotHeldByInactiveUser() {
+ assumeFalse(
+ "setActiveUser not supported for private profile",
+ users().current().type().name() == PRIVATE_PROFILE_TYPE_NAME,
+ )
+ // initialUser needs to be not the targetUser
+ val targetActiveUser = users().current().userHandle()
+ val initialUser =
+ if (Objects.equals(targetActiveUser, deviceState.initialUser().userHandle())) {
+ deviceState.workProfile().userHandle()
+ } else {
+ deviceState.initialUser().userHandle()
+ }
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0)
+
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+
+ roleManager.setActiveUserForRole(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ targetActiveUser,
+ 0,
+ )
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ // We can assume targetActiveUser is role holder since fallback is enabled
+ eventually { assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) }
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @Test
+ @Throws(Exception::class)
+ fun setAndGetActiveUserForRoleSetWorkProfile() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+
+ val targetActiveUser = deviceState.workProfile().userHandle()
+ roleManager.setActiveUserForRole(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ targetActiveUser,
+ 0,
+ )
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ // We can assume targetActiveUser is role holder since fallback is enabled
+ eventually { assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) }
+ } finally {
+ setDefaultHoldersForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(MANAGE_ROLE_HOLDERS)
+ @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(Exception::class)
+ fun cannotAddRoleHolderAsUserForProfileExclusiveRoleWithoutInteractAcrossUserPermission() {
+ // Set other user as active
+ val initialUser = deviceState.workProfile().userHandle()
+ // setActiveUserForRole and getActiveUserForRole is used to ensure initial active users
+ // state and requires INTERACT_ACROSS_USERS_FULL
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL).use { _ ->
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialUser)
+ }
+
+ val targetActiveUser = users().current().userHandle()
+ val future = CallbackFuture()
+ assertThrows(SecurityException::class.java) {
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ targetActiveUser,
+ context.mainExecutor,
+ future,
+ )
+ }
+ assertThat(
+ roleManager.getRoleHoldersAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ targetActiveUser,
+ )
+ )
+ .isEmpty()
+
+ // getActiveUserForRole is used to ensure addRoleHolderAsUser didn't set active user, and
+ // requires INTERACT_ACROSS_USERS_FULL
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL).use { _ ->
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialUser)
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun addRoleHolderAsUserSetsCurrentUserAsActive() {
+ // Set other user as active
+ val initialUser = deviceState.workProfile().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialUser)
+
+ val targetActiveUser = users().current().userHandle()
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ targetActiveUser,
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @RequireRunOnWorkProfile
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun addRoleHolderAsUserSetsWorkProfileAsActive() {
+ // Set other user as active
+ val initialUser = deviceState.initialUser().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialUser)
+
+ val targetActiveUser = deviceState.workProfile().userHandle()
+
+ assertThat(targetActiveUser).isNotEqualTo(initialUser)
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ targetActiveUser,
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(MANAGE_DEFAULT_APPLICATIONS)
+ @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetDefaultApplicationForProfileExclusiveRoleWithoutInteractAcrossUserPermission() {
+ // Set other user as active
+ val initialUser = deviceState.workProfile().userHandle()
+ // setActiveUserForRole and getActiveUserForRole is used to ensure initial active users
+ // state and requires INTERACT_ACROSS_USERS_FULL
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL).use { _ ->
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialUser)
+ }
+
+ val future = CallbackFuture()
+ assertThrows(SecurityException::class.java) {
+ roleManager.setDefaultApplication(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ context.mainExecutor,
+ future,
+ )
+ }
+ assertThat(roleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)).isNull()
+
+ // getActiveUserForRole is used to ensure setDefaultApplication didn't set active user,
+ // and requires INTERACT_ACROSS_USERS_FULL
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL).use { _ ->
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialUser)
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_DEFAULT_APPLICATIONS)
+ @EnsureHasWorkProfile
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun setDefaultApplicationSetsCurrentUserAsActive() {
+ // Set other user as active
+ val initialUser = deviceState.workProfile().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialUser)
+
+ val targetActiveUser = users().current().userHandle()
+ val future = CallbackFuture()
+ roleManager.setDefaultApplication(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ eventually { assertExpectedProfileHasRoleUsingGetDefaultApplication(targetActiveUser) }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_DEFAULT_APPLICATIONS)
+ @EnsureHasWorkProfile
+ @RequireRunOnWorkProfile
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun setDefaultApplicationSetsWorkProfileAsActive() {
+ // Set other user as active
+ val initialUser = deviceState.initialUser().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialUser)
+
+ val targetActiveUser = deviceState.workProfile().userHandle()
+ assertThat(targetActiveUser).isNotEqualTo(initialUser)
+ val future = CallbackFuture()
+ roleManager.setDefaultApplication(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ eventually { assertExpectedProfileHasRoleUsingGetDefaultApplication(targetActiveUser) }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureCanAddUser
+ @EnsureHasNoWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(Exception::class)
+ fun ensureActiveUserSetToParentOnUserRemoved() {
+ users()
+ .createUser()
+ .parent(users().initial())
+ .type(users().supportedType(UserType.MANAGED_PROFILE_TYPE_NAME))
+ .createAndStart()
+ .use { userReference ->
+ val targetActiveUser = userReference.userHandle()
+ roleManager.setActiveUserForRole(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ targetActiveUser,
+ 0,
+ )
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+
+ userReference.remove()
+ }
+
+ // Removal of users in roles service might take a moment
+ eventually {
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(users().current().userHandle())
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndSetDefaultAppThenIsDefaultApp() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = users().current().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ }
+
+ if (isWatch) {
+ waitFindObject(
+ By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel))
+ )
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ }
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndSetWorkDefaultAppThenIsDefaultApp() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = deviceState.workProfile().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ }
+
+ if (isWatch) {
+ waitFindObject(
+ By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel))
+ )
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ }
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndSetDefaultAppThenSetNoneThenHasNoneDefaultApp() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = users().current().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ getUiDevice().waitForIdle()
+ waitFindObject(By.clickable(true).hasDescendant(By.text(NONE_LABEL))).click()
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ getUiDevice().waitForIdle()
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+ .click()
+ }
+
+ if (isWatch) {
+ waitFindObject(By.clickable(true).checked(true).hasDescendant(By.text(NONE_LABEL)))
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+ }
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(deviceState.initialUser().userHandle())
+ assertNoRoleHoldersUsingGetRoleHoldersAsUser()
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndSetWorkDefaultAppThenSetNoneThenHasNoneDefaultApp() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = deviceState.workProfile().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ getUiDevice().waitForIdle()
+ waitFindObject(By.clickable(true).hasDescendant(By.text(NONE_LABEL))).click()
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ getUiDevice().waitForIdle()
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+ .click()
+ }
+
+ if (isWatch) {
+ waitFindObject(By.clickable(true).checked(true).hasDescendant(By.text(NONE_LABEL)))
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+ }
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(deviceState.initialUser().userHandle())
+ assertNoRoleHoldersUsingGetRoleHoldersAsUser()
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndSetDefaultAppThenIsDefaultAppInList() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = users().current().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ waitFindObject(
+ By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel))
+ )
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ }
+ pressBack()
+
+ waitFindObject(By.text(targetAppLabel))
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndSetWorkDefaultAppThenIsDefaultAppInList() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = deviceState.workProfile().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ waitFindObject(
+ By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel))
+ )
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ }
+ pressBack()
+
+ waitFindObject(By.text(targetAppLabel))
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndSetDefaultAppThenSetNoneThenIsNoneDefaultAppInList() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = users().current().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ getUiDevice().waitForIdle()
+ waitFindObject(By.clickable(true).hasDescendant(By.text(NONE_LABEL))).click()
+ waitFindObject(By.clickable(true).checked(true).hasDescendant(By.text(NONE_LABEL)))
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ getUiDevice().waitForIdle()
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+ .click()
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+ }
+ pressBack()
+
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(deviceState.initialUser().userHandle())
+ assertNoRoleHoldersUsingGetRoleHoldersAsUser()
+
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndSetWorkDefaultAppThenSetNoneThenIsNoneDefaultAppInList() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = deviceState.workProfile().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ getUiDevice().waitForIdle()
+ waitFindObject(By.clickable(true).hasDescendant(By.text(NONE_LABEL))).click()
+ waitFindObject(By.clickable(true).checked(true).hasDescendant(By.text(NONE_LABEL)))
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ getUiDevice().waitForIdle()
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+ .click()
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+ }
+ pressBack()
+
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL))
+ .hasDescendant(By.text(NONE_LABEL))
+ )
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(deviceState.initialUser().userHandle())
+ assertNoRoleHoldersUsingGetRoleHoldersAsUser()
+
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun requestRoleAndAllowPrimaryThenIsRoleHolder() {
+ try {
+ // setDefaultHoldersForTestForAllUsers and setRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-primary selected first. Request exits early if user and package
+ // already the role holder
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ deviceState.workProfile().userHandle(),
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ requestRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+
+ val targetActiveUser = deviceState.initialUser().userHandle()
+ respondToRoleRequest(true, targetActiveUser)
+
+ // getActiveUserForRole and getRoleHoldersAsUser require INTERACT_ACROSS_USERS_FULL and
+ // MANAGE_ROLE_HOLDERS permissions to validate cross user role active user and role
+ // holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+ }
+ } finally {
+ // clearDefaultHoldersForTestForAllUsers and clearRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun requestRoleAndAllowWorkThenWorkIsRoleHolder() {
+ try {
+ // setDefaultHoldersForTestForAllUsers and setRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-primary selected first. Request exits early if user and package
+ // already the role holder
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ deviceState.workProfile().userHandle(),
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ requestRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+
+ val targetActiveUser = deviceState.workProfile().userHandle()
+ respondToRoleRequest(true, targetActiveUser)
+
+ // getActiveUserForRole and getRoleHoldersAsUser require INTERACT_ACROSS_USERS_FULL and
+ // MANAGE_ROLE_HOLDERS permissions to validate cross user role active user and role
+ // holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+ }
+ } finally {
+ // clearDefaultHoldersForTestForAllUsers and clearRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasWorkProfile
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun requestRoleAndSelectNoneThenIsNoneRoleHolder() {
+ try {
+ // setDefaultHoldersForTestForAllUsers and setRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-primary selected first. Request exits early if user and package
+ // already the role holder
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ deviceState.workProfile().userHandle(),
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ requestRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ respondNoneToRoleRequest()
+
+ // getActiveUserForRole and getRoleHoldersAsUser require INTERACT_ACROSS_USERS_FULL and
+ // MANAGE_ROLE_HOLDERS permissions to validate cross user role active user and role
+ // holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(deviceState.initialUser().userHandle())
+ assertNoRoleHoldersUsingGetRoleHoldersAsUser()
+ }
+ } finally {
+ // clearDefaultHoldersForTestForAllUsers and clearRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasWorkProfile
+ @RequireRunOnWorkProfile
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun requestRoleFromWorkProfileAndAllowPrimaryThenIsRoleHolder() {
+ try {
+ // setDefaultHoldersForTestForAllUsers and setRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-work selected first. Request exits early if user and package
+ // already the role holder
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ deviceState.initialUser().userHandle(),
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ requestRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+
+ val targetActiveUser = deviceState.initialUser().userHandle()
+ respondToRoleRequest(true, targetActiveUser)
+
+ // getActiveUserForRole and getRoleHoldersAsUser require INTERACT_ACROSS_USERS_FULL and
+ // MANAGE_ROLE_HOLDERS permissions to validate cross user role active user and role
+ // holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+ }
+ } finally {
+ // clearDefaultHoldersForTestForAllUsers and clearRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasWorkProfile
+ @RequireRunOnWorkProfile
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun requestRoleFromWorkProfileAndAllowWorkThenWorkIsRoleHolder() {
+ try {
+ // setDefaultHoldersForTestForAllUsers and setRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-work selected first. Request exits early if user and package
+ // already the role holder
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ deviceState.initialUser().userHandle(),
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ requestRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+
+ val targetActiveUser = deviceState.workProfile().userHandle()
+ respondToRoleRequest(true, targetActiveUser)
+
+ // getActiveUserForRole and getRoleHoldersAsUser require INTERACT_ACROSS_USERS_FULL and
+ // MANAGE_ROLE_HOLDERS permissions to validate cross user role active user and role
+ // holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+ }
+ } finally {
+ // clearDefaultHoldersForTestForAllUsers and clearRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasWorkProfile
+ @RequireRunOnWorkProfile
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun requestRoleFromWorkProfileAndSelectNoneThenIsNoneRoleHolder() {
+ try {
+ // setDefaultHoldersForTestForAllUsers and setRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-work selected first. Request exits early if user and package
+ // already the role holder
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ deviceState.initialUser().userHandle(),
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ requestRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ respondNoneToRoleRequest()
+
+ // getActiveUserForRole and getRoleHoldersAsUser require INTERACT_ACROSS_USERS_FULL and
+ // MANAGE_ROLE_HOLDERS permissions to validate cross user role active user and role
+ // holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(deviceState.initialUser().userHandle())
+ assertNoRoleHoldersUsingGetRoleHoldersAsUser()
+ }
+ } finally {
+ // clearDefaultHoldersForTestForAllUsers and clearRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+ }
+
+ @Throws(java.lang.Exception::class)
+ private fun installAppForAllUsers() {
+ SystemUtil.runShellCommandOrThrow("pm install -r --user all $APP_APK_PATH")
+ SystemUtil.waitForBroadcasts()
+ }
+
+ private fun uninstallAppForAllUsers() {
+ SystemUtil.runShellCommand("pm uninstall $APP_PACKAGE_NAME")
+ }
+
+ private fun pressBack() {
+ getUiDevice().pressBack()
+ getUiDevice().waitForIdle()
+ }
+
+ private fun requestRole(roleName: String) {
+ val intent =
+ Intent()
+ .setComponent(ComponentName(APP_PACKAGE_NAME, APP_REQUEST_ROLE_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_ROLE_NAME, roleName)
+ activityRule.getActivity().startActivityToWaitForResult(intent)
+ }
+
+ private fun respondToRoleRequest(allow: Boolean, targetActiveUser: UserHandle) {
+ if (allow) {
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ waitFindObject(By.text(targetAppLabel)).click()
+ }
+ val result: Pair<Int, Intent?> = clickButtonAndWaitForResult(allow)
+ val expectedResult =
+ if (allow && Objects.equals(targetActiveUser, users().instrumented().userHandle()))
+ Activity.RESULT_OK
+ else Activity.RESULT_CANCELED
+
+ assertThat(result.first).isEqualTo(expectedResult)
+ }
+
+ private fun respondNoneToRoleRequest() {
+ waitFindObject(By.text(NONE_LABEL)).click()
+ val result: Pair<Int, Intent?> = clickButtonAndWaitForResult(true)
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED)
+ }
+
+ private fun clickButtonAndWaitForResult(positive: Boolean): Pair<Int, Intent?> {
+ waitFindObject(if (positive) POSITIVE_BUTTON_SELECTOR else NEGATIVE_BUTTON_SELECTOR).click()
+ return waitForResult()
+ }
+
+ @Throws(InterruptedException::class)
+ private fun waitForResult(): Pair<Int, Intent?> {
+ return activityRule.getActivity().waitForActivityResult(TIMEOUT_MILLIS)
+ }
+
+ private fun assertNoRoleHoldersUsingGetRoleHoldersAsUser() {
+ for (userReference in users().profileGroup(deviceState.initialUser())) {
+ val user = userReference.userHandle()
+ // Verify the non-active user does not hold the role
+ assertWithMessage(
+ "Expected user ${user.identifier} to not have a role holder for" +
+ " $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME"
+ )
+ .that(roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user))
+ .isEmpty()
+ }
+ }
+
+ private fun assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(
+ expectedActiveUser: UserHandle
+ ) {
+ for (userReference in users().profileGroup(deviceState.initialUser())) {
+ val user = userReference.userHandle()
+ if (Objects.equals(user, expectedActiveUser)) {
+ val roleHolders =
+ roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user)
+ assertWithMessage(
+ "Expected user ${user.identifier} to have a role holder for " +
+ " $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME"
+ )
+ .that(roleHolders)
+ .isNotEmpty()
+ assertWithMessage(
+ "Expected user ${user.identifier} to have $APP_PACKAGE_NAME as role " +
+ "holder for $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME"
+ )
+ .that(roleHolders.first())
+ .isEqualTo(APP_PACKAGE_NAME)
+ } else {
+ // Verify the non-active user does not hold the role
+ assertWithMessage(
+ "Expected user ${user.identifier} to not have a role holder for" +
+ " $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME"
+ )
+ .that(
+ roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user)
+ )
+ .isEmpty()
+ }
+ }
+ }
+
+ private fun assertExpectedProfileHasRoleUsingGetDefaultApplication(
+ expectedActiveUser: UserHandle
+ ) {
+ for (userReference in users().profileGroup(deviceState.initialUser())) {
+ val userRoleManager = getRoleManagerForUser(userReference)
+ val user = userReference.userHandle()
+ if (Objects.equals(user, expectedActiveUser)) {
+ assertWithMessage("Expected default application for user ${user.identifier}")
+ .that(
+ userRoleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ )
+ .isEqualTo(APP_PACKAGE_NAME)
+ } else {
+ // Verify the non-active user does not hold the role
+ assertWithMessage("Expected no default application for user ${user.identifier}")
+ .that(
+ userRoleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ )
+ .isNull()
+ }
+ }
+ }
+
+ private fun setDefaultHoldersForTestForAllUsers() {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ for (userRoleManager in
+ users().profileGroup(deviceState.initialUser()).map { getRoleManagerForUser(it) }) {
+ userRoleManager.setDefaultHoldersForTest(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ listOf(APP_PACKAGE_NAME),
+ )
+ }
+ }
+
+ private fun clearDefaultHoldersForTestForAllUsers() {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ for (userRoleManager in
+ users().profileGroup(deviceState.initialUser()).map { getRoleManagerForUser(it) }) {
+ userRoleManager.setDefaultHoldersForTest(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ emptyList(),
+ )
+ }
+ }
+
+ private fun setRoleVisibleForTestForAllUsers() {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ for (userRoleManager in
+ users().profileGroup(deviceState.initialUser()).map { getRoleManagerForUser(it) }) {
+ userRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, true)
+ }
+ }
+
+ private fun clearRoleVisibleForTestForAllUsers() {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ for (userRoleManager in
+ users().profileGroup(deviceState.initialUser()).map { getRoleManagerForUser(it) }) {
+ userRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, false)
+ }
+ }
+
+ private fun getRoleManagerForUser(user: UserReference): RoleManager {
+ val userContext = context().androidContextAsUser(user)
+ return userContext.getSystemService(RoleManager::class.java)
+ }
+
+ class CallbackFuture : CompletableFuture<Boolean?>(), Consumer<Boolean?> {
+ override fun accept(successful: Boolean?) {
+ complete(successful)
+ }
+ }
+
+ companion object {
+ private const val TIMEOUT_MILLIS: Long = (15 * 1000).toLong()
+ private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME =
+ RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY
+ private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL =
+ "Test profile group exclusive role app"
+ private const val PRIVATE_PROFILE_TYPE_NAME = "android.os.usertype.profile.PRIVATE"
+ 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_LABEL = "CtsRoleTestApp"
+ private const val APP_REQUEST_ROLE_ACTIVITY_NAME = APP_PACKAGE_NAME + ".RequestRoleActivity"
+ private const val NONE_LABEL = "None"
+
+ private val context: Context = context().instrumentedContext()
+ private val roleManager: RoleManager = context.getSystemService(RoleManager::class.java)
+ private val packageManager: PackageManager = context.packageManager
+ private val isWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+
+ private val NEGATIVE_BUTTON_SELECTOR =
+ if (isWatch) By.text("Cancel") else By.res("android:id/button2")
+ private val POSITIVE_BUTTON_SELECTOR =
+ if (isWatch) By.text("Set as default") else By.res("android:id/button1")
+
+ @JvmField @ClassRule @Rule val deviceState = DeviceState()
+
+ @JvmField
+ @ClassRule
+ @Rule
+ var disableAnimationRule: DisableAnimationRule = DisableAnimationRule()
+
+ @JvmField @ClassRule @Rule var freezeRotationRule: FreezeRotationRule = FreezeRotationRule()
+ }
+}
diff --git a/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/WaitForResultActivity.kt b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/WaitForResultActivity.kt
new file mode 100644
index 000000000..84590ce91
--- /dev/null
+++ b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/WaitForResultActivity.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.rolemultiuser.cts
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.util.Pair
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+/** An Activity that can start another Activity and wait for its result. */
+class WaitForResultActivity : Activity() {
+ private var mLatch: CountDownLatch? = null
+ private var mResultCode = 0
+ private var mData: Intent? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState != null) {
+ throw RuntimeException(
+ ("Activity was recreated (perhaps due to a configuration change?) " +
+ "and this activity doesn't currently know how to gracefully handle " +
+ "configuration changes.")
+ )
+ }
+ }
+
+ fun startActivityToWaitForResult(intent: Intent) {
+ mLatch = CountDownLatch(1)
+ startActivityForResult(intent, REQUEST_CODE_WAIT_FOR_RESULT)
+ }
+
+ @Throws(InterruptedException::class)
+ fun waitForActivityResult(timeoutMillis: Long): Pair<Int, Intent?> {
+ mLatch!!.await(timeoutMillis, TimeUnit.MILLISECONDS)
+ return Pair(mResultCode, mData)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (requestCode == REQUEST_CODE_WAIT_FOR_RESULT) {
+ mResultCode = resultCode
+ mData = data
+ mLatch!!.countDown()
+ } else {
+ super.onActivityResult(requestCode, resultCode, data)
+ }
+ }
+
+ companion object {
+ private const val REQUEST_CODE_WAIT_FOR_RESULT = 1
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
index c344d7ebd..1320c2ff9 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
@@ -492,8 +492,8 @@ class SafetyCenterDataTest {
status1,
listOf(issue1),
listOf(entryOrGroup1),
- listOf(staticEntryGroup1)
- )
+ listOf(staticEntryGroup1),
+ ),
)
.addEqualityGroup(
data2,
@@ -501,19 +501,19 @@ class SafetyCenterDataTest {
status2,
listOf(issue2),
listOf(entryOrGroup2),
- listOf(staticEntryGroup2)
- )
+ listOf(staticEntryGroup2),
+ ),
)
.addEqualityGroup(
SafetyCenterData(status1, listOf(), listOf(), listOf()),
- SafetyCenterData(status1, listOf(), listOf(), listOf())
+ SafetyCenterData(status1, listOf(), listOf(), listOf()),
)
.addEqualityGroup(
SafetyCenterData(
status2,
listOf(issue1),
listOf(entryOrGroup1),
- listOf(staticEntryGroup1)
+ listOf(staticEntryGroup1),
)
)
.addEqualityGroup(
@@ -521,7 +521,7 @@ class SafetyCenterDataTest {
status1,
listOf(issue2),
listOf(entryOrGroup1),
- listOf(staticEntryGroup1)
+ listOf(staticEntryGroup1),
)
)
.addEqualityGroup(
@@ -529,7 +529,7 @@ class SafetyCenterDataTest {
status1,
listOf(issue1),
listOf(entryOrGroup2),
- listOf(staticEntryGroup1)
+ listOf(staticEntryGroup1),
)
)
.addEqualityGroup(
@@ -537,7 +537,7 @@ class SafetyCenterDataTest {
status1,
listOf(issue1),
listOf(entryOrGroup1),
- listOf(staticEntryGroup2)
+ listOf(staticEntryGroup2),
)
)
@@ -550,7 +550,7 @@ class SafetyCenterDataTest {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyCenterData.CREATOR,
ignoreToString = true,
- createCopy = { SafetyCenterData.Builder(it).build() }
+ createCopy = { SafetyCenterData.Builder(it).build() },
)
.addEqualityGroup(
data1,
@@ -558,7 +558,7 @@ class SafetyCenterDataTest {
status1,
listOf(issue1),
listOf(entryOrGroup1),
- listOf(staticEntryGroup1)
+ listOf(staticEntryGroup1),
),
SafetyCenterData.Builder(status1)
.addIssue(issue1)
@@ -570,7 +570,7 @@ class SafetyCenterDataTest {
.addEntryOrGroup(entryOrGroup1)
.addStaticEntryGroup(staticEntryGroup1)
.setExtras(unknownExtras)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyCenterData.Builder(status1)
@@ -640,7 +640,7 @@ class SafetyCenterDataTest {
.addStaticEntryGroup(staticEntryGroup1)
.addIssue(issue1)
.setExtras(filledExtrasIssuesToGroups1)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyCenterData.Builder(status1)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt
index 7ae5fb347..38ed449f7 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt
@@ -175,7 +175,7 @@ class SafetyCenterEntryGroupTest {
fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyCenterEntryGroup.CREATOR,
- createCopy = { SafetyCenterEntryGroup.Builder(it).build() }
+ createCopy = { SafetyCenterEntryGroup.Builder(it).build() },
)
.addEqualityGroup(
entryGroup1,
@@ -183,7 +183,7 @@ class SafetyCenterEntryGroupTest {
.setSummary("A group summary")
.setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
.setEntries(listOf(entry1))
- .build()
+ .build(),
)
.addEqualityGroup(entryGroup2)
.addEqualityGroup(
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt
index 116164288..2811b87f2 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt
@@ -41,18 +41,18 @@ class SafetyCenterEntryTest {
context,
0,
Intent("Fake Different Data"),
- PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_IMMUTABLE,
)
private val iconAction1 =
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR,
- pendingIntent1
+ pendingIntent1,
)
private val iconAction2 =
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO,
- pendingIntent2
+ pendingIntent2,
)
private val entry1 =
@@ -213,7 +213,7 @@ class SafetyCenterEntryTest {
fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyCenterEntry.CREATOR,
- createCopy = { SafetyCenterEntry.Builder(it).build() }
+ createCopy = { SafetyCenterEntry.Builder(it).build() },
)
.addEqualityGroup(entry1)
.addEqualityGroup(
@@ -226,7 +226,7 @@ class SafetyCenterEntryTest {
.setPendingIntent(pendingIntent1)
.setIconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO,
- pendingIntent2
+ pendingIntent2,
)
.build(),
SafetyCenterEntry.Builder("id", "a title")
@@ -238,9 +238,9 @@ class SafetyCenterEntryTest {
.setPendingIntent(pendingIntent1)
.setIconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO,
- pendingIntent2
+ pendingIntent2,
)
- .build()
+ .build(),
)
.addEqualityGroup(SafetyCenterEntry.Builder(entry1).setId("a different id").build())
.addEqualityGroup(
@@ -274,7 +274,7 @@ class SafetyCenterEntryTest {
assertThat(
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR,
- pendingIntent1
+ pendingIntent1,
)
.type
)
@@ -282,7 +282,7 @@ class SafetyCenterEntryTest {
assertThat(
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO,
- pendingIntent1
+ pendingIntent1,
)
.type
)
@@ -294,7 +294,7 @@ class SafetyCenterEntryTest {
assertThat(
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR,
- pendingIntent1
+ pendingIntent1,
)
.pendingIntent
)
@@ -302,7 +302,7 @@ class SafetyCenterEntryTest {
assertThat(
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR,
- pendingIntent2
+ pendingIntent2,
)
.pendingIntent
)
@@ -340,26 +340,26 @@ class SafetyCenterEntryTest {
iconAction1,
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR,
- pendingIntent1
- )
+ pendingIntent1,
+ ),
)
.addEqualityGroup(
iconAction2,
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO,
- pendingIntent2
- )
+ pendingIntent2,
+ ),
)
.addEqualityGroup(
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO,
- pendingIntent1
+ pendingIntent1,
)
)
.addEqualityGroup(
SafetyCenterEntry.IconAction(
SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR,
- pendingIntent2
+ pendingIntent2,
)
)
.test()
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt
index 0d97026bd..e7565bf61 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt
@@ -58,7 +58,7 @@ class SafetyCenterErrorDetailsTest {
.addEqualityGroup(errorDetails2, SafetyCenterErrorDetails("another error message"))
.addEqualityGroup(
SafetyCenterErrorDetails("a different error message"),
- SafetyCenterErrorDetails("a different error message")
+ SafetyCenterErrorDetails("a different error message"),
)
.test()
}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
index be7ca343c..fd359f600 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
@@ -47,7 +47,7 @@ class SafetyCenterIssueTest {
context,
0,
Intent("Fake Different Data"),
- PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_IMMUTABLE,
)
private val action1 =
@@ -451,7 +451,7 @@ class SafetyCenterIssueTest {
.setIsInFlight(true)
.setSuccessMessage("a success message")
.setConfirmationDialogDetails(confirmationDialogDetails)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyCenterIssue.Action.Builder("an_id", "a label", pendingIntent1)
@@ -534,7 +534,7 @@ class SafetyCenterIssueTest {
.build(),
SafetyCenterIssue.Builder(issueWithTiramisuFields)
.setAttributionTitle("Attribution title")
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyCenterIssue.Builder(issueWithTiramisuFields)
@@ -549,7 +549,7 @@ class SafetyCenterIssueTest {
)
.addEqualityGroup(
SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build(),
- SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build()
+ SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build(),
)
.addEqualityGroup(
SafetyCenterIssue.Builder(issueWithTiramisuFields)
@@ -625,7 +625,7 @@ class SafetyCenterIssueTest {
)
.addEqualityGroup(
ConfirmationDialogDetails("Title", "Text", "Accept", "Deny"),
- ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
+ ConfirmationDialogDetails("Title", "Text", "Accept", "Deny"),
)
.addEqualityGroup(ConfirmationDialogDetails("Other title", "Text", "Accept", "Deny"))
.addEqualityGroup(ConfirmationDialogDetails("Title", "Other text", "Accept", "Deny"))
@@ -643,7 +643,7 @@ class SafetyCenterIssueTest {
) =
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyCenterIssue.CREATOR,
- createCopy = createCopyFromBuilder
+ createCopy = createCopyFromBuilder,
)
.addEqualityGroup(issue1, SafetyCenterIssue.Builder(issue1).build())
.addEqualityGroup(issueWithRequiredFieldsOnly)
@@ -657,7 +657,7 @@ class SafetyCenterIssueTest {
.setSubtitle("In the neighborhood")
.setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
.setActions(listOf(action1))
- .build()
+ .build(),
)
.addEqualityGroup(SafetyCenterIssue.Builder(issue1).setId("a different id").build())
.addEqualityGroup(
@@ -685,7 +685,7 @@ class SafetyCenterIssueTest {
) =
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyCenterIssue.Action.CREATOR,
- createCopy = createCopyFromBuilder
+ createCopy = createCopyFromBuilder,
)
.addEqualityGroup(action1)
.addEqualityGroup(action2)
@@ -699,7 +699,7 @@ class SafetyCenterIssueTest {
.setWillResolve(true)
.setIsInFlight(true)
.setSuccessMessage("a success message")
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyCenterIssue.Action.Builder("an_id", "a label", pendingIntent1)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
index 8fd45efb8..b1c731f08 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
@@ -216,7 +216,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterTestHelper.setData(
DYNAMIC_OTHER_PACKAGE_ID,
- safetySourceTestData.unspecified
+ safetySourceTestData.unspecified,
)
}
@@ -281,7 +281,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterTestHelper.setData(
DYNAMIC_IN_STATELESS_ID,
- safetySourceTestData.information
+ safetySourceTestData.information,
)
}
@@ -342,7 +342,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterTestHelper.setData(
ISSUE_ONLY_BAREBONE_ID,
- safetySourceTestData.unspecified
+ safetySourceTestData.unspecified,
)
}
@@ -359,7 +359,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterTestHelper.setData(
DYNAMIC_BAREBONE_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue),
)
}
@@ -400,7 +400,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.informationWithIssue
+ safetySourceTestData.informationWithIssue,
)
}
@@ -421,7 +421,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
}
@@ -454,7 +454,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterTestHelper.setData(
DYNAMIC_ALL_OPTIONAL_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
}
@@ -490,7 +490,7 @@ class SafetyCenterManagerTest {
ISSUE_ONLY_ALL_OPTIONAL_ID,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalResolvingGeneralIssue
- )
+ ),
)
}
@@ -521,7 +521,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.issueCategoryAllowlists =
mapOf(
ISSUE_CATEGORY_DEVICE to setOf(SAMPLE_SOURCE_ID),
- ISSUE_CATEGORY_GENERAL to setOf(SAMPLE_SOURCE_ID)
+ ISSUE_CATEGORY_GENERAL to setOf(SAMPLE_SOURCE_ID),
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -539,7 +539,7 @@ class SafetyCenterManagerTest {
mapOf(
ISSUE_CATEGORY_ACCOUNT to setOf(SINGLE_SOURCE_ID, SAMPLE_SOURCE_ID),
ISSUE_CATEGORY_DEVICE to setOf(SAMPLE_SOURCE_ID),
- ISSUE_CATEGORY_GENERAL to setOf(SAMPLE_SOURCE_ID)
+ ISSUE_CATEGORY_GENERAL to setOf(SAMPLE_SOURCE_ID),
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -560,7 +560,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationWithAccountIssue
+ safetySourceTestData.recommendationWithAccountIssue,
)
}
@@ -577,7 +577,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.issueCategoryAllowlists =
mapOf(
ISSUE_CATEGORY_ACCOUNT to setOf(SAMPLE_SOURCE_ID),
- ISSUE_CATEGORY_DEVICE to setOf(SINGLE_SOURCE_ID)
+ ISSUE_CATEGORY_DEVICE to setOf(SINGLE_SOURCE_ID),
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -585,7 +585,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationWithAccountIssue
+ safetySourceTestData.recommendationWithAccountIssue,
)
}
@@ -605,7 +605,7 @@ class SafetyCenterManagerTest {
safetyCenterManager.setSafetySourceDataWithPermission(
SINGLE_SOURCE_ID,
safetySourceTestData.unspecified,
- EVENT_SOURCE_STATE_CHANGED
+ EVENT_SOURCE_STATE_CHANGED,
)
safetyCenterTestHelper.setEnabled(true)
@@ -620,7 +620,7 @@ class SafetyCenterManagerTest {
safetyCenterManager.setSafetySourceData(
SINGLE_SOURCE_ID,
safetySourceTestData.unspecified,
- EVENT_SOURCE_STATE_CHANGED
+ EVENT_SOURCE_STATE_CHANGED,
)
}
}
@@ -702,7 +702,7 @@ class SafetyCenterManagerTest {
safetyCenterManager.reportSafetySourceErrorWithPermission(
SINGLE_SOURCE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
listener.receiveSafetyCenterData()
@@ -717,7 +717,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterManager.reportSafetySourceErrorWithPermission(
SINGLE_SOURCE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
}
@@ -732,7 +732,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterManager.reportSafetySourceErrorWithPermission(
STATIC_BAREBONE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
}
@@ -749,7 +749,7 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterManager.reportSafetySourceErrorWithPermission(
DYNAMIC_OTHER_PACKAGE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
}
@@ -769,7 +769,7 @@ class SafetyCenterManagerTest {
safetyCenterManager.reportSafetySourceErrorWithPermission(
SINGLE_SOURCE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
assertFailsWith(TimeoutCancellationException::class) {
@@ -782,7 +782,7 @@ class SafetyCenterManagerTest {
assertFailsWith(SecurityException::class) {
safetyCenterManager.reportSafetySourceError(
SINGLE_SOURCE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
}
}
@@ -791,7 +791,7 @@ class SafetyCenterManagerTest {
fun safetyCenterEnabledChanged_whenImplicitReceiverHasPermission_receiverCalled() {
assumeTrue(
"Cannot toggle SafetyCenter using DeviceConfig",
- SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig()
+ SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(),
)
// Implicit broadcast is only sent to system user.
assumeTrue(context.getSystemService(UserManager::class.java)!!.isSystemUser)
@@ -813,7 +813,7 @@ class SafetyCenterManagerTest {
fun safetyCenterEnabledChanged_whenImplicitReceiverDoesntHavePermission_receiverNotCalled() {
assumeTrue(
"Cannot toggle SafetyCenter using DeviceConfig",
- SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig()
+ SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(),
)
// Implicit broadcast is only sent to system user.
assumeTrue(context.getSystemService(UserManager::class.java)!!.isSystemUser)
@@ -829,7 +829,7 @@ class SafetyCenterManagerTest {
fun safetyCenterEnabledChanged_whenSourceReceiverHasPermission_receiverCalled() {
assumeTrue(
"Cannot toggle SafetyCenter using DeviceConfig",
- SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig()
+ SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(),
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -848,14 +848,14 @@ class SafetyCenterManagerTest {
fun safetyCenterEnabledChanged_valueDoesntChange_receiverNotCalled() {
assumeTrue(
"Cannot toggle SafetyCenter using DeviceConfig",
- SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig()
+ SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(),
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
assertFailsWith(TimeoutCancellationException::class) {
SafetySourceReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait(
true,
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
}
@@ -864,7 +864,7 @@ class SafetyCenterManagerTest {
fun safetyCenterEnabledChanged_whenSourceReceiverDoesntHavePermission_receiverNotCalled() {
assumeTrue(
"Cannot toggle SafetyCenter using DeviceConfig",
- SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig()
+ SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(),
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -877,12 +877,12 @@ class SafetyCenterManagerTest {
fun safetyCenterEnabledChanged_whenSourceReceiverNotInConfig_receiverNotCalled() {
assumeTrue(
"Cannot toggle SafetyCenter using DeviceConfig",
- SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig()
+ SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(),
)
assertFailsWith(TimeoutCancellationException::class) {
SafetySourceReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait(
false,
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
}
@@ -891,14 +891,14 @@ class SafetyCenterManagerTest {
fun safetyCenterEnabledChanged_whenNoDeviceConfigFlag_receiverNotCalled() {
assumeFalse(
"SafetyCenter DeviceConfig flag is in use",
- SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig()
+ SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(),
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
assertFailsWith(TimeoutCancellationException::class) {
SafetySourceReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait(
false,
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
}
@@ -908,7 +908,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Rescan(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_RESCAN_BUTTON_CLICK
@@ -925,7 +925,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -943,7 +943,7 @@ class SafetyCenterManagerTest {
SafetySourceReceiver.runInForegroundService = true
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -961,13 +961,13 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.informationWithIssue)
+ Response.SetData(safetySourceTestData.informationWithIssue),
)
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- timeout = TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT,
)
}
@@ -982,7 +982,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.informationWithIssue)
+ Response.SetData(safetySourceTestData.informationWithIssue),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1015,11 +1015,11 @@ class SafetyCenterManagerTest {
SafetySourceReceiver.apply {
setResponse(
Request.Rescan(SOURCE_ID_1),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
setResponse(
Request.Rescan(SOURCE_ID_3),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
}
@@ -1047,11 +1047,11 @@ class SafetyCenterManagerTest {
SafetySourceReceiver.apply {
setResponse(
Request.Refresh(SOURCE_ID_1),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
setResponse(
Request.Refresh(SOURCE_ID_3),
- Response.SetData(safetySourceTestData.informationWithIssue)
+ Response.SetData(safetySourceTestData.informationWithIssue),
)
}
@@ -1077,7 +1077,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Rescan(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
assertFailsWith(TimeoutCancellationException::class) {
@@ -1094,13 +1094,13 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_whenSourceNotInConfig_sourceDoesntSendData() {
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- timeout = TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT,
)
}
}
@@ -1122,7 +1122,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Rescan(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
val broadcastId1 =
@@ -1143,7 +1143,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Rescan(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information, overrideBroadcastId = "invalid")
+ Response.SetData(safetySourceTestData.information, overrideBroadcastId = "invalid"),
)
val listener = safetyCenterTestHelper.addListener()
@@ -1157,7 +1157,7 @@ class SafetyCenterManagerTest {
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN
@@ -1172,14 +1172,14 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN
)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1201,7 +1201,7 @@ class SafetyCenterManagerTest {
)
SafetySourceReceiver.setResponse(
Request.Rescan(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1230,7 +1230,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_LONG)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1249,7 +1249,7 @@ class SafetyCenterManagerTest {
)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1269,7 +1269,7 @@ class SafetyCenterManagerTest {
for (sourceId in listOf(SOURCE_ID_2, SOURCE_ID_3)) {
SafetySourceReceiver.setResponse(
Request.Rescan(sourceId),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
}
val listener = safetyCenterTestHelper.addListener()
@@ -1289,7 +1289,7 @@ class SafetyCenterManagerTest {
for (sourceId in listOf(SOURCE_ID_2, SOURCE_ID_3)) {
SafetySourceReceiver.setResponse(
Request.Rescan(sourceId),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
}
val listener = safetyCenterTestHelper.addListener()
@@ -1308,7 +1308,7 @@ class SafetyCenterManagerTest {
// SOURCE_ID_1 and SOURCE_ID_2 will timeout
SafetySourceReceiver.setResponse(
Request.Rescan(SOURCE_ID_3),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
val listener = safetyCenterTestHelper.addListener()
@@ -1355,7 +1355,7 @@ class SafetyCenterManagerTest {
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- timeout = TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT,
)
}
}
@@ -1377,15 +1377,15 @@ class SafetyCenterManagerTest {
SafetySourceReceiver.apply {
setResponse(
Request.Refresh(SOURCE_ID_1),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
setResponse(
Request.Refresh(SOURCE_ID_2),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
setResponse(
Request.Refresh(SOURCE_ID_3),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
}
// But sources 1 and 3 should not be refreshed in background
@@ -1409,7 +1409,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
SafetyCenterFlags.backgroundRefreshDeniedSources = setOf(SINGLE_SOURCE_ID)
@@ -1426,7 +1426,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Rescan(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
SafetyCenterFlags.backgroundRefreshDeniedSources = setOf(SINGLE_SOURCE_ID)
@@ -1444,14 +1444,14 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
SafetyCenterFlags.backgroundRefreshDeniedSources = setOf(SINGLE_SOURCE_ID)
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PERIODIC,
- timeout = TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT,
)
}
@@ -1466,7 +1466,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1484,21 +1484,21 @@ class SafetyCenterManagerTest {
SafetySourceReceiver.apply {
setResponse(
Request.Refresh(SOURCE_ID_1),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
setResponse(
Request.Refresh(SOURCE_ID_2),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
setResponse(
Request.Refresh(SOURCE_ID_3),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
}
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_2)
+ safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_2),
)
val apiSafetySourceData1 =
@@ -1518,7 +1518,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
assertFailsWith(TimeoutCancellationException::class) {
@@ -1541,7 +1541,7 @@ class SafetyCenterManagerTest {
assertFails {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_3)
+ safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_3),
)
}
}
@@ -1647,7 +1647,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
@@ -1708,7 +1708,7 @@ class SafetyCenterManagerTest {
}
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(),
- oneShotListener
+ oneShotListener,
)
// Check that we don't deadlock when using a one-shot listener. This is because adding the
@@ -1725,7 +1725,7 @@ class SafetyCenterManagerTest {
val listener = SafetyCenterTestListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(),
- listener
+ listener,
)
assertFailsWith(TimeoutCancellationException::class) {
@@ -1764,7 +1764,7 @@ class SafetyCenterManagerTest {
val listener = SafetyCenterTestListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
fakeExecutor,
- listener
+ listener,
)
fakeExecutor.getNextTask().run()
listener.receiveSafetyCenterData()
@@ -1812,7 +1812,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationDismissPendingIntentIssue
+ safetySourceTestData.recommendationDismissPendingIntentIssue,
)
val apiSafetySourceDataBeforeDismissal =
safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
@@ -1822,7 +1822,7 @@ class SafetyCenterManagerTest {
)
SafetySourceReceiver.setResponse(
Request.DismissIssue(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermissionAndWait(
@@ -1839,7 +1839,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
@@ -1857,7 +1857,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID)
@@ -1878,7 +1878,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID)
@@ -1889,7 +1889,7 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- issueTypeId = "some_other_issue_type_id"
+ issueTypeId = "some_other_issue_type_id",
)
)
@@ -1903,7 +1903,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
safetyCenterTestHelper.setEnabled(false)
@@ -1931,7 +1931,7 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- userId = USER_NULL
+ userId = USER_NULL,
)
)
}
@@ -1949,7 +1949,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val apiSafetySourceDataBeforeExecution =
safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
@@ -1959,7 +1959,7 @@ class SafetyCenterManagerTest {
)
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
@@ -1967,8 +1967,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
val apiSafetySourceDataAfterExecution =
@@ -1989,8 +1989,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
val error = listener.receiveSafetyCenterErrorDetails()
@@ -2015,8 +2015,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
val error = listener.receiveSafetyCenterErrorDetails()
@@ -2043,7 +2043,7 @@ class SafetyCenterManagerTest {
}
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
fakeExecutor,
- listener
+ listener,
)
fakeExecutor.getNextTask().run()
@@ -2052,8 +2052,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
fakeExecutor.getNextTask().run()
@@ -2072,9 +2072,9 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
+ CRITICAL_ISSUE_ACTION_ID,
),
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
assertFailsWith(TimeoutCancellationException::class) {
@@ -2087,7 +2087,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
@@ -2095,8 +2095,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
listener.receiveSafetyCenterData()
@@ -2106,9 +2106,9 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
+ CRITICAL_ISSUE_ACTION_ID,
),
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
assertFailsWith(TimeoutCancellationException::class) {
@@ -2121,7 +2121,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
safetyCenterTestHelper.setEnabled(false)
@@ -2132,9 +2132,9 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
+ CRITICAL_ISSUE_ACTION_ID,
),
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
assertFailsWith(TimeoutCancellationException::class) {
@@ -2154,12 +2154,12 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
assertFailsWith(IllegalArgumentException::class) {
@@ -2168,9 +2168,9 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID + "invalid",
- CRITICAL_ISSUE_ACTION_ID
+ CRITICAL_ISSUE_ACTION_ID,
),
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
@@ -2184,12 +2184,12 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
assertFailsWith(TimeoutCancellationException::class) {
@@ -2198,9 +2198,9 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID + "invalid"
+ CRITICAL_ISSUE_ACTION_ID + "invalid",
),
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
@@ -2217,8 +2217,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SOURCE_ID_1,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
}
}
@@ -2230,14 +2230,14 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- userId = USER_NULL
+ userId = USER_NULL,
),
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
CRITICAL_ISSUE_ACTION_ID,
- userId = USER_NULL
- )
+ userId = USER_NULL,
+ ),
)
}
}
@@ -2255,7 +2255,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(SOURCE_ID_1, safetySourceTestData.unspecified)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterManager.clearAllSafetySourceDataForTestsWithPermission()
@@ -2271,7 +2271,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setEnabled(false)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt
index 71be3d5eb..0709bfc83 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt
@@ -104,11 +104,11 @@ class SafetyCenterStaticEntryGroupTest {
)
.addEqualityGroup(
staticEntryGroup,
- SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1, staticEntry2))
+ SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1, staticEntry2)),
)
.addEqualityGroup(
SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1)),
- SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1))
+ SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1)),
)
.addEqualityGroup(
SafetyCenterStaticEntryGroup("a different title", listOf(staticEntry1))
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt
index 045a50f75..7a5e6a737 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt
@@ -40,7 +40,7 @@ class SafetyCenterStaticEntryTest {
context,
0,
Intent("Fake Different Data"),
- PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_IMMUTABLE,
)
private val title1 = "a title"
@@ -101,14 +101,14 @@ class SafetyCenterStaticEntryTest {
fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyCenterStaticEntry.CREATOR,
- createCopy = { SafetyCenterStaticEntry.Builder(it).build() }
+ createCopy = { SafetyCenterStaticEntry.Builder(it).build() },
)
.addEqualityGroup(
staticEntry1,
SafetyCenterStaticEntry.Builder("a title")
.setSummary("a summary")
.setPendingIntent(pendingIntent1)
- .build()
+ .build(),
)
.addEqualityGroup(staticEntry2)
.addEqualityGroup(staticEntryMinimal, SafetyCenterStaticEntry.Builder("").build())
@@ -120,7 +120,7 @@ class SafetyCenterStaticEntryTest {
SafetyCenterStaticEntry.Builder("titlee")
.setSummary("sumaree")
.setPendingIntent(pendingIntent1)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyCenterStaticEntry.Builder(staticEntry1).setTitle("a different title").build()
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt
index 4df4e5d35..561e34d3b 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt
@@ -156,7 +156,7 @@ class SafetyCenterStatusTest {
fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyCenterStatus.CREATOR,
- createCopy = { SafetyCenterStatus.Builder(it).build() }
+ createCopy = { SafetyCenterStatus.Builder(it).build() },
)
.addEqualityGroup(
baseStatus,
@@ -164,7 +164,7 @@ class SafetyCenterStatusTest {
.setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
.setRefreshStatus(SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS)
.build(),
- SafetyCenterStatus.Builder(baseStatus).build()
+ SafetyCenterStatus.Builder(baseStatus).build(),
)
.addEqualityGroup(
SafetyCenterStatus.Builder("same title", "same summary")
@@ -172,7 +172,7 @@ class SafetyCenterStatusTest {
.build(),
SafetyCenterStatus.Builder("same title", "same summary")
.setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyCenterStatus.Builder(baseStatus).setTitle("that's not it").build()
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
index d882fc3cb..3beced151 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
@@ -119,7 +119,7 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.setSafetySourceDataWithPermission(
SINGLE_SOURCE_ID,
safetySourceTestData.criticalWithResolvingGeneralIssue,
- EVENT_SOURCE_STATE_CHANGED
+ EVENT_SOURCE_STATE_CHANGED,
)
val apiSafetySourceData =
@@ -134,7 +134,7 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.setSafetySourceData(
SINGLE_SOURCE_ID,
safetySourceTestData.unspecified,
- EVENT_SOURCE_STATE_CHANGED
+ EVENT_SOURCE_STATE_CHANGED,
)
}
}
@@ -154,12 +154,12 @@ class SafetyCenterUnsupportedTest {
val listener = SafetyCenterTestListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(),
- listener
+ listener,
)
safetyCenterManager.reportSafetySourceErrorWithPermission(
SINGLE_SOURCE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
assertFailsWith(TimeoutCancellationException::class) {
@@ -172,7 +172,7 @@ class SafetyCenterUnsupportedTest {
assertFailsWith(SecurityException::class) {
safetyCenterManager.reportSafetySourceError(
SINGLE_SOURCE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
}
}
@@ -186,7 +186,7 @@ class SafetyCenterUnsupportedTest {
assertFailsWith(TimeoutCancellationException::class) {
enabledChangedReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait(
false,
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
enabledChangedReceiver.unregister()
@@ -201,7 +201,7 @@ class SafetyCenterUnsupportedTest {
assertFailsWith(TimeoutCancellationException::class) {
SafetySourceReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait(
false,
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
}
@@ -215,7 +215,7 @@ class SafetyCenterUnsupportedTest {
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- timeout = TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT,
)
}
}
@@ -260,7 +260,7 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(),
- listener
+ listener,
)
assertFailsWith(TimeoutCancellationException::class) {
@@ -285,7 +285,7 @@ class SafetyCenterUnsupportedTest {
val listener = SafetyCenterTestListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(),
- listener
+ listener,
)
safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener)
@@ -300,7 +300,7 @@ class SafetyCenterUnsupportedTest {
val listener = SafetyCenterTestListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(),
- listener
+ listener,
)
assertFailsWith(SecurityException::class) {
@@ -316,12 +316,12 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.setSafetySourceDataWithPermission(
SINGLE_SOURCE_ID,
safetySourceTestData.criticalWithResolvingGeneralIssue,
- EVENT_SOURCE_STATE_CHANGED
+ EVENT_SOURCE_STATE_CHANGED,
)
val listener = SafetyCenterTestListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(),
- listener
+ listener,
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
@@ -348,12 +348,12 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.setSafetySourceDataWithPermission(
SINGLE_SOURCE_ID,
safetySourceTestData.criticalWithResolvingGeneralIssue,
- EVENT_SOURCE_STATE_CHANGED
+ EVENT_SOURCE_STATE_CHANGED,
)
val listener = SafetyCenterTestListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(),
- listener
+ listener,
)
assertFailsWith(TimeoutCancellationException::class) {
@@ -362,9 +362,9 @@ class SafetyCenterUnsupportedTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
+ CRITICAL_ISSUE_ACTION_ID,
),
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
assertFailsWith(TimeoutCancellationException::class) {
@@ -387,7 +387,7 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.setSafetySourceDataWithPermission(
SINGLE_SOURCE_ID,
safetySourceTestData.criticalWithResolvingGeneralIssue,
- EVENT_SOURCE_STATE_CHANGED
+ EVENT_SOURCE_STATE_CHANGED,
)
val apiSafetySourceDataBeforeClearing =
safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
index 4d1cb6a8b..cba29852f 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
@@ -203,7 +203,7 @@ class SafetyEventTest {
) =
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyEvent.CREATOR,
- createCopy = createCopyFromBuilder
+ createCopy = createCopyFromBuilder,
)
.addEqualityGroup(SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build())
.addEqualityGroup(
@@ -212,7 +212,7 @@ class SafetyEventTest {
.build(),
SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
.setRefreshBroadcastId(REFRESH_BROADCAST_ID)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED)
@@ -222,7 +222,7 @@ class SafetyEventTest {
SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED)
.setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
.setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
@@ -232,7 +232,7 @@ class SafetyEventTest {
SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
.setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
.setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID)
- .build()
+ .build(),
)
.addEqualityGroup(SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build())
.addEqualityGroup(SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED).build())
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
index ba2ab3d76..287aa15a3 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
@@ -350,11 +350,11 @@ class SafetySourceDataTest {
EqualsHashCodeToStringTester.ofParcelable(parcelableCreator = SafetySourceData.CREATOR)
.addEqualityGroup(
SafetySourceData.Builder().setStatus(firstStatus).build(),
- SafetySourceData.Builder().setStatus(firstStatus).build()
+ SafetySourceData.Builder().setStatus(firstStatus).build(),
)
.addEqualityGroup(
SafetySourceData.Builder().addIssue(firstIssue).addIssue(secondIssue).build(),
- SafetySourceData.Builder().addIssue(firstIssue).addIssue(secondIssue).build()
+ SafetySourceData.Builder().addIssue(firstIssue).addIssue(secondIssue).build(),
)
.addEqualityGroup(
SafetySourceData.Builder()
@@ -366,7 +366,7 @@ class SafetySourceDataTest {
.setStatus(firstStatus)
.addIssue(firstIssue)
.addIssue(secondIssue)
- .build()
+ .build(),
)
.addEqualityGroup(SafetySourceData.Builder().setStatus(secondStatus).build())
.addEqualityGroup(
@@ -394,11 +394,11 @@ class SafetySourceDataTest {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetySourceData.CREATOR,
ignoreToString = true,
- createCopy = { SafetySourceData.Builder(it).build() }
+ createCopy = { SafetySourceData.Builder(it).build() },
)
.addEqualityGroup(
SafetySourceData.Builder().setStatus(firstStatus).build(),
- SafetySourceData.Builder().setStatus(firstStatus).setExtras(filledExtras).build()
+ SafetySourceData.Builder().setStatus(firstStatus).setExtras(filledExtras).build(),
)
.addEqualityGroup(
SafetySourceData.Builder().addIssue(firstIssue).addIssue(secondIssue).build(),
@@ -406,7 +406,7 @@ class SafetySourceDataTest {
.addIssue(firstIssue)
.addIssue(secondIssue)
.setExtras(filledExtras)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetySourceData.Builder()
@@ -419,11 +419,11 @@ class SafetySourceDataTest {
.addIssue(firstIssue)
.addIssue(secondIssue)
.setExtras(filledExtras)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetySourceData.Builder().setStatus(secondStatus).build(),
- SafetySourceData.Builder().setStatus(secondStatus).setExtras(filledExtras).build()
+ SafetySourceData.Builder().setStatus(secondStatus).setExtras(filledExtras).build(),
)
.addEqualityGroup(
SafetySourceData.Builder().addIssue(secondIssue).addIssue(firstIssue).build(),
@@ -431,11 +431,11 @@ class SafetySourceDataTest {
.addIssue(secondIssue)
.addIssue(firstIssue)
.setExtras(filledExtras)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetySourceData.Builder().addIssue(firstIssue).build(),
- SafetySourceData.Builder().addIssue(firstIssue).setExtras(filledExtras).build()
+ SafetySourceData.Builder().addIssue(firstIssue).setExtras(filledExtras).build(),
)
.addEqualityGroup(
SafetySourceData.Builder()
@@ -448,7 +448,7 @@ class SafetySourceDataTest {
.addIssue(firstIssue)
.addIssue(secondIssue)
.setExtras(filledExtras)
- .build()
+ .build(),
)
.test()
}
@@ -485,7 +485,7 @@ class SafetySourceDataTest {
context,
/* requestCode = */ 0,
Intent("Status PendingIntent $id"),
- FLAG_IMMUTABLE
+ FLAG_IMMUTABLE,
)
)
.build()
@@ -496,7 +496,7 @@ class SafetySourceDataTest {
"Issue summary $id",
"Issue summary $id",
severityLevel,
- "Issue type id $id"
+ "Issue type id $id",
)
.addAction(
SafetySourceIssue.Action.Builder(
@@ -506,8 +506,8 @@ class SafetySourceDataTest {
context,
/* requestCode = */ 0,
Intent("Issue PendingIntent $id"),
- FLAG_IMMUTABLE
- )
+ FLAG_IMMUTABLE,
+ ),
)
.build()
)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt
index 336462491..4133f6f17 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt
@@ -51,7 +51,7 @@ class SafetySourceErrorDetailsTest {
SafetySourceErrorDetails(SAFETY_EVENT),
SafetySourceErrorDetails(
SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
- )
+ ),
)
.addEqualityGroup(
SafetySourceErrorDetails(
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
index 2d19a3175..a7149e4d7 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
@@ -238,7 +238,7 @@ class SafetySourceIssueTest {
Action.Builder("action_id", "Action label", pendingIntent1)
.setConfirmationDialogDetails(confirmationDialogDetails)
.setWillResolve(false)
- .build()
+ .build(),
)
.addEqualityGroup(
Action.Builder("action_id", "Action label", pendingIntent1)
@@ -270,8 +270,8 @@ class SafetySourceIssueTest {
context,
0,
Intent("Other action PendingIntent"),
- FLAG_IMMUTABLE
- )
+ FLAG_IMMUTABLE,
+ ),
)
.setConfirmationDialogDetails(confirmationDialogDetails)
.build()
@@ -465,7 +465,7 @@ class SafetySourceIssueTest {
fun notification_equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = Notification.CREATOR,
- createCopy = { Notification.Builder(it).build() }
+ createCopy = { Notification.Builder(it).build() },
)
.addEqualityGroup(
Notification.Builder("Title", "Text").build(),
@@ -477,7 +477,7 @@ class SafetySourceIssueTest {
.addEqualityGroup(Notification.Builder("Title", "Text").addAction(action2).build())
.addEqualityGroup(
Notification.Builder("Title", "Text").addAction(action1).addAction(action2).build(),
- Notification.Builder("Title", "Text").addActions(listOf(action1, action2)).build()
+ Notification.Builder("Title", "Text").addActions(listOf(action1, action2)).build(),
)
.test()
}
@@ -538,7 +538,7 @@ class SafetySourceIssueTest {
)
.addEqualityGroup(
ConfirmationDialogDetails("Title", "Text", "Accept", "Deny"),
- ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
+ ConfirmationDialogDetails("Title", "Text", "Accept", "Deny"),
)
.addEqualityGroup(ConfirmationDialogDetails("Other title", "Text", "Accept", "Deny"))
.addEqualityGroup(ConfirmationDialogDetails("Title", "Other text", "Accept", "Deny"))
@@ -555,7 +555,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -571,7 +571,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -587,7 +587,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -603,7 +603,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.addAction(action1)
@@ -620,7 +620,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -637,7 +637,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -654,7 +654,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.setAttributionTitle("attribution title")
@@ -672,7 +672,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -689,7 +689,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
assertFails { safetySourceIssueBuilder.setAttributionTitle("title") }
@@ -703,7 +703,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -719,7 +719,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -735,7 +735,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.setIssueCategory(ISSUE_CATEGORY_DEVICE)
@@ -753,7 +753,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS)
@@ -771,7 +771,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.addAction(action2)
@@ -788,7 +788,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.addAction(action2)
@@ -806,7 +806,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
val actions = safetySourceIssueBuilder.build().actions
@@ -824,7 +824,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.addAction(action2)
@@ -843,7 +843,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -859,7 +859,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.setOnDismissPendingIntent(pendingIntentService)
@@ -877,7 +877,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -894,7 +894,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.setDeduplicationId("deduplication_id")
@@ -912,7 +912,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -929,7 +929,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
assertFails { safetySourceIssueBuilder.setDeduplicationId("id") }
@@ -943,7 +943,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -960,7 +960,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -977,7 +977,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.setCustomNotification(
@@ -1004,7 +1004,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1021,7 +1021,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
assertFails { safetySourceIssueBuilder.setCustomNotification(null) }
@@ -1036,7 +1036,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1054,7 +1054,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER)
@@ -1073,7 +1073,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1090,7 +1090,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
val exception =
@@ -1110,7 +1110,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
assertFails { safetySourceIssueBuilder.setNotificationBehavior(0) }
@@ -1125,7 +1125,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1143,7 +1143,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
@@ -1162,7 +1162,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1179,7 +1179,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
val exception =
@@ -1199,7 +1199,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
assertFails { safetySourceIssueBuilder.setIssueActionability(0) }
@@ -1213,7 +1213,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
}
}
@@ -1226,7 +1226,7 @@ class SafetySourceIssueTest {
Generic.asNull(),
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
}
}
@@ -1239,7 +1239,7 @@ class SafetySourceIssueTest {
"Issue title",
Generic.asNull(),
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
}
}
@@ -1253,7 +1253,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_UNSPECIFIED,
- "issue_type_id"
+ "issue_type_id",
)
}
assertThat(exception)
@@ -1270,7 +1270,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
-1,
- "issue_type_id"
+ "issue_type_id",
)
}
assertThat(exception)
@@ -1286,7 +1286,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- Generic.asNull()
+ Generic.asNull(),
)
}
}
@@ -1299,7 +1299,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
val exception =
@@ -1319,7 +1319,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
@@ -1339,7 +1339,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
val exception =
assertFailsWith(IllegalArgumentException::class) {
@@ -1348,7 +1348,7 @@ class SafetySourceIssueTest {
context,
/* requestCode = */ 0,
Intent("PendingIntent activity"),
- FLAG_IMMUTABLE
+ FLAG_IMMUTABLE,
)
)
}
@@ -1365,7 +1365,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.addAction(action1)
@@ -1385,7 +1385,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
val exception =
@@ -1410,7 +1410,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.addAction(action2)
@@ -1432,7 +1432,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
val exception =
@@ -1452,7 +1452,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP)
.build()
@@ -1469,7 +1469,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
.build()
@@ -1485,7 +1485,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1505,7 +1505,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1526,7 +1526,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1553,7 +1553,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
@@ -1601,7 +1601,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1622,7 +1622,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1641,7 +1641,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1655,7 +1655,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1663,7 +1663,7 @@ class SafetySourceIssueTest {
.addAction(action2)
.setOnDismissPendingIntent(pendingIntentService)
.setAttributionTitle("attribution title")
- .build()
+ .build(),
)
.addEqualityGroup(
SafetySourceIssue.Builder(
@@ -1671,7 +1671,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_CRITICAL_WARNING,
- "issue_type_id"
+ "issue_type_id",
)
.setAttributionTitle("Other issue attribution title")
.addAction(action1)
@@ -1683,7 +1683,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1698,7 +1698,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1707,7 +1707,7 @@ class SafetySourceIssueTest {
.setOnDismissPendingIntent(pendingIntentService)
.setAttributionTitle("attribution title")
.setDeduplicationId("deduplication_id")
- .build()
+ .build(),
)
.addEqualityGroup(
SafetySourceIssue.Builder(
@@ -1715,7 +1715,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1732,7 +1732,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Other issue subtitle")
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
@@ -1745,7 +1745,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Other issue subtitle")
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS)
@@ -1758,7 +1758,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Other issue subtitle")
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY)
@@ -1771,7 +1771,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL)
.addAction(action1)
@@ -1786,7 +1786,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP)
.addAction(action1)
@@ -1796,11 +1796,11 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP)
.addAction(action1)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetySourceIssue.Builder(
@@ -1808,7 +1808,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
.addAction(action1)
@@ -1820,7 +1820,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
.build()
@@ -1831,7 +1831,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED)
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
@@ -1843,7 +1843,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1864,7 +1864,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1889,7 +1889,7 @@ class SafetySourceIssueTest {
) =
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetySourceIssue.CREATOR,
- createCopy = createCopyFromBuilder
+ createCopy = createCopyFromBuilder,
)
.addEqualityGroup(
SafetySourceIssue.Builder(
@@ -1897,7 +1897,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -1910,14 +1910,14 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
.addAction(action1)
.addAction(action2)
.setOnDismissPendingIntent(pendingIntentService)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetySourceIssue.Builder(
@@ -1925,7 +1925,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1936,7 +1936,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1947,7 +1947,7 @@ class SafetySourceIssueTest {
"Other issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1958,7 +1958,7 @@ class SafetySourceIssueTest {
"Issue title",
"Different issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1969,7 +1969,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_CRITICAL_WARNING,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action1)
.build()
@@ -1980,7 +1980,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "other_issue_type_id"
+ "other_issue_type_id",
)
.addAction(action1)
.build()
@@ -1991,7 +1991,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.addAction(action2)
.build()
@@ -2002,7 +2002,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Other issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
@@ -2017,7 +2017,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Other issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_DEVICE)
@@ -2032,7 +2032,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Other issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_DEVICE)
@@ -2047,7 +2047,7 @@ class SafetySourceIssueTest {
"Issue title",
"Issue summary",
SEVERITY_LEVEL_INFORMATION,
- "issue_type_id"
+ "issue_type_id",
)
.setSubtitle("Other issue subtitle")
.setIssueCategory(ISSUE_CATEGORY_DEVICE)
@@ -2058,7 +2058,7 @@ class SafetySourceIssueTest {
context,
0,
Intent("Other PendingIntent service"),
- FLAG_IMMUTABLE
+ FLAG_IMMUTABLE,
)
)
.build()
@@ -2069,14 +2069,14 @@ class SafetySourceIssueTest {
) =
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = Action.CREATOR,
- createCopy = createCopyFromBuilder
+ createCopy = createCopyFromBuilder,
)
.addEqualityGroup(
Action.Builder("action_id", "Action label", pendingIntent1).build(),
Action.Builder("action_id", "Action label", pendingIntent1).build(),
Action.Builder("action_id", "Action label", pendingIntent1)
.setWillResolve(false)
- .build()
+ .build(),
)
.addEqualityGroup(
Action.Builder("action_id", "Action label", pendingIntent1)
@@ -2102,8 +2102,8 @@ class SafetySourceIssueTest {
context,
0,
Intent("Other action PendingIntent"),
- FLAG_IMMUTABLE
- )
+ FLAG_IMMUTABLE,
+ ),
)
.build()
)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
index 2a20cd45d..04a91b268 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
@@ -98,7 +98,7 @@ class SafetySourceStatusTest {
EqualsHashCodeToStringTester.ofParcelable(parcelableCreator = IconAction.CREATOR)
.addEqualityGroup(
IconAction(ICON_TYPE_GEAR, pendingIntent1),
- IconAction(ICON_TYPE_GEAR, pendingIntent1)
+ IconAction(ICON_TYPE_GEAR, pendingIntent1),
)
.addEqualityGroup(IconAction(ICON_TYPE_INFO, pendingIntent1))
.addEqualityGroup(IconAction(ICON_TYPE_GEAR, pendingIntent2))
@@ -195,7 +195,7 @@ class SafetySourceStatusTest {
SafetySourceStatus.Builder(
Generic.asNull(),
"Status summary",
- SEVERITY_LEVEL_INFORMATION
+ SEVERITY_LEVEL_INFORMATION,
)
}
}
@@ -229,7 +229,7 @@ class SafetySourceStatusTest {
context,
/* requestCode = */ 0,
Intent("PendingIntent service"),
- FLAG_IMMUTABLE
+ FLAG_IMMUTABLE,
)
)
}
@@ -298,13 +298,13 @@ class SafetySourceStatusTest {
) =
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetySourceStatus.CREATOR,
- createCopy = createCopyFromBuilder
+ createCopy = createCopyFromBuilder,
)
.addEqualityGroup(
SafetySourceStatus.Builder(
"Status title",
"Status summary",
- SEVERITY_LEVEL_INFORMATION
+ SEVERITY_LEVEL_INFORMATION,
)
.setPendingIntent(pendingIntent1)
.setIconAction(iconAction1)
@@ -313,18 +313,18 @@ class SafetySourceStatusTest {
SafetySourceStatus.Builder(
"Status title",
"Status summary",
- SEVERITY_LEVEL_INFORMATION
+ SEVERITY_LEVEL_INFORMATION,
)
.setPendingIntent(pendingIntent1)
.setIconAction(iconAction1)
.setEnabled(true)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetySourceStatus.Builder(
"Status title",
"Status summary",
- SEVERITY_LEVEL_INFORMATION
+ SEVERITY_LEVEL_INFORMATION,
)
.build()
)
@@ -332,7 +332,7 @@ class SafetySourceStatusTest {
SafetySourceStatus.Builder(
"Other status title",
"Status summary",
- SEVERITY_LEVEL_INFORMATION
+ SEVERITY_LEVEL_INFORMATION,
)
.build()
)
@@ -340,7 +340,7 @@ class SafetySourceStatusTest {
SafetySourceStatus.Builder(
"Status title",
"Other status summary",
- SEVERITY_LEVEL_INFORMATION
+ SEVERITY_LEVEL_INFORMATION,
)
.build()
)
@@ -348,7 +348,7 @@ class SafetySourceStatusTest {
SafetySourceStatus.Builder(
"Status title",
"Status summary",
- SEVERITY_LEVEL_CRITICAL_WARNING
+ SEVERITY_LEVEL_CRITICAL_WARNING,
)
.build()
)
@@ -356,7 +356,7 @@ class SafetySourceStatusTest {
SafetySourceStatus.Builder(
"Status title",
"Status summary",
- SEVERITY_LEVEL_CRITICAL_WARNING
+ SEVERITY_LEVEL_CRITICAL_WARNING,
)
.setPendingIntent(pendingIntent2)
.build()
@@ -365,7 +365,7 @@ class SafetySourceStatusTest {
SafetySourceStatus.Builder(
"Status title",
"Status summary",
- SEVERITY_LEVEL_CRITICAL_WARNING
+ SEVERITY_LEVEL_CRITICAL_WARNING,
)
.setIconAction(iconAction2)
.build()
@@ -374,7 +374,7 @@ class SafetySourceStatusTest {
SafetySourceStatus.Builder(
"Status title",
"Status summary",
- SEVERITY_LEVEL_UNSPECIFIED
+ SEVERITY_LEVEL_UNSPECIFIED,
)
.setEnabled(false)
.build()
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt
index 68dcd4a11..c3b6ef1b3 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt
@@ -36,7 +36,7 @@ class SafetyCenterConfigTest {
assertThat(BASE.safetySourcesGroups)
.containsExactly(
SafetySourcesGroupTest.STATELESS_INFERRED,
- SafetySourcesGroupTest.HIDDEN_INFERRED
+ SafetySourcesGroupTest.HIDDEN_INFERRED,
)
.inOrder()
}
@@ -65,7 +65,7 @@ class SafetyCenterConfigTest {
assertThat(sourceGroups)
.containsExactly(
SafetySourcesGroupTest.STATELESS_INFERRED,
- SafetySourcesGroupTest.HIDDEN_INFERRED
+ SafetySourcesGroupTest.HIDDEN_INFERRED,
)
.inOrder()
}
@@ -99,14 +99,14 @@ class SafetyCenterConfigTest {
) =
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyCenterConfig.CREATOR,
- createCopy = createCopyFromBuilder
+ createCopy = createCopyFromBuilder,
)
.addEqualityGroup(
BASE,
SafetyCenterConfig.Builder()
.addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED)
.addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED)
- .build()
+ .build(),
)
.addEqualityGroup(
SafetyCenterConfig.Builder()
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
index 4b6f0f6f9..210a5b5e7 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
@@ -368,7 +368,7 @@ class SafetySourceTest {
{ SafetySource.Builder(it).build() }
} else {
null
- }
+ },
)
.addEqualityGroup(DYNAMIC_BAREBONE)
.addEqualityGroup(
@@ -396,7 +396,7 @@ class SafetySourceTest {
setTitleForPrivateProfileResId(REFERENCE_RES_ID)
}
}
- .build()
+ .build(),
)
.addEqualityGroup(DYNAMIC_HIDDEN)
.addEqualityGroup(DYNAMIC_HIDDEN_WITH_SEARCH)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt
index f741369eb..06aad5d40 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt
@@ -155,7 +155,7 @@ class SafetySourcesGroupTest {
.containsExactly(
SafetySourceTest.DYNAMIC_BAREBONE,
SafetySourceTest.STATIC_BAREBONE,
- SafetySourceTest.ISSUE_ONLY_BAREBONE
+ SafetySourceTest.ISSUE_ONLY_BAREBONE,
)
.inOrder()
assertThat(STATELESS_INFERRED.safetySources)
@@ -169,7 +169,7 @@ class SafetySourcesGroupTest {
.containsExactly(
SafetySourceTest.DYNAMIC_BAREBONE,
SafetySourceTest.STATIC_BAREBONE,
- SafetySourceTest.ISSUE_ONLY_BAREBONE
+ SafetySourceTest.ISSUE_ONLY_BAREBONE,
)
assertThat(STATELESS_BAREBONE.safetySources)
.containsExactly(SafetySourceTest.STATIC_BAREBONE)
@@ -177,7 +177,7 @@ class SafetySourcesGroupTest {
.containsExactly(
SafetySourceTest.DYNAMIC_BAREBONE,
SafetySourceTest.STATIC_BAREBONE,
- SafetySourceTest.ISSUE_ONLY_BAREBONE
+ SafetySourceTest.ISSUE_ONLY_BAREBONE,
)
assertThat(HIDDEN_BAREBONE.safetySources)
.containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE)
@@ -325,7 +325,7 @@ class SafetySourcesGroupTest {
{ SafetySourcesGroup.Builder(it).build() }
} else {
null
- }
+ },
)
.addEqualityGroup(STATEFUL_INFERRED_WITH_SUMMARY)
.addEqualityGroup(STATEFUL_INFERRED_WITH_ICON)
@@ -340,7 +340,7 @@ class SafetySourcesGroupTest {
.addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
.addSafetySource(SafetySourceTest.STATIC_BAREBONE)
.addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
- .build()
+ .build(),
)
.apply { if (SdkLevel.isAtLeastU()) add(STATEFUL_ALL_OPTIONAL) }
.toTypedArray()
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt
index 7c9e2cffc..34f5c1f69 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt
@@ -88,7 +88,7 @@ class XmlConfigTest {
private fun parseXmlConfig() =
SafetyCenterConfigParser.parseXmlResource(
safetyCenterResourcesApk.safetyCenterConfig!!,
- safetyCenterResourcesApk.resources
+ safetyCenterResourcesApk.resources,
)
companion object {
diff --git a/tests/functional/safetycenter/multiusers/Android.bp b/tests/functional/safetycenter/multiusers/Android.bp
index 30024221b..745e763f0 100644
--- a/tests/functional/safetycenter/multiusers/Android.bp
+++ b/tests/functional/safetycenter/multiusers/Android.bp
@@ -36,6 +36,7 @@ android_test {
"Harrier",
"Nene",
"TestApp",
+ "bedstead-enterprise",
"com.android.permission.flags-aconfig-java-export",
],
test_suites: [
diff --git a/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt b/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt
index ff5dcc1f6..041cc652f 100644
--- a/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt
+++ b/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt
@@ -41,16 +41,20 @@ import android.safetycenter.SafetyEvent
import android.safetycenter.SafetySourceData
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
-import com.android.bedstead.harrier.BedsteadJUnit4
-import com.android.bedstead.harrier.DeviceState
-import com.android.bedstead.harrier.annotations.EnsureHasAdditionalUser
-import com.android.bedstead.harrier.annotations.EnsureHasCloneProfile
-import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile
-import com.android.bedstead.harrier.annotations.EnsureHasPrivateProfile
-import com.android.bedstead.harrier.annotations.EnsureHasNoPrivateProfile
-import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile
import com.android.bedstead.enterprise.annotations.EnsureHasDeviceOwner
import com.android.bedstead.enterprise.annotations.EnsureHasNoDeviceOwner
+import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile
+import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile
+import com.android.bedstead.enterprise.workProfile
+import com.android.bedstead.harrier.BedsteadJUnit4
+import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.multiuser.additionalUser
+import com.android.bedstead.multiuser.annotations.EnsureHasAdditionalUser
+import com.android.bedstead.multiuser.annotations.EnsureHasCloneProfile
+import com.android.bedstead.multiuser.annotations.EnsureHasNoPrivateProfile
+import com.android.bedstead.multiuser.annotations.EnsureHasPrivateProfile
+import com.android.bedstead.multiuser.cloneProfile
+import com.android.bedstead.multiuser.privateProfile
import com.android.bedstead.nene.TestApis
import com.android.bedstead.nene.types.OptionalBoolean.TRUE
import com.android.compatibility.common.util.DisableAnimationRule
@@ -132,30 +136,30 @@ class SafetyCenterMultiUsersTest {
listOf(
safetyCenterTestData.safetyCenterIssueCritical(
DYNAMIC_BAREBONE_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueCritical(
ISSUE_ONLY_BAREBONE_ID,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
DYNAMIC_DISABLED_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
- )
+ groupId = MIXED_STATELESS_GROUP_ID,
+ ),
)
private val dynamicBareboneDefault: SafetyCenterEntry
@@ -181,7 +185,7 @@ class SafetyCenterMultiUsersTest {
.safetyCenterEntryDefaultBuilder(
DYNAMIC_DISABLED_ID,
userId = deviceState.workProfile().id(),
- title = "Paste"
+ title = "Paste",
)
.setPendingIntent(null)
.setEnabled(false)
@@ -196,11 +200,9 @@ class SafetyCenterMultiUsersTest {
DYNAMIC_DISABLED_ID,
deviceState.workProfile().id(),
title = "Ok title for Work",
- pendingIntent = null
- )
- .setSummary(
- safetyCenterResourcesApk.getStringByName("work_profile_paused"),
+ pendingIntent = null,
)
+ .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused"))
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
.setEnabled(false)
.build()
@@ -212,7 +214,7 @@ class SafetyCenterMultiUsersTest {
get() =
safetyCenterTestData.safetyCenterEntryUnspecified(
DYNAMIC_HIDDEN_ID,
- pendingIntent = null
+ pendingIntent = null,
)
private val dynamicHiddenForWorkUpdated: SafetyCenterEntry
@@ -225,11 +227,9 @@ class SafetyCenterMultiUsersTest {
DYNAMIC_HIDDEN_ID,
deviceState.workProfile().id(),
title = "Ok title for Work",
- pendingIntent = null
- )
- .setSummary(
- safetyCenterResourcesApk.getStringByName("work_profile_paused"),
+ pendingIntent = null,
)
+ .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused"))
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
.setEnabled(false)
.build()
@@ -262,12 +262,12 @@ class SafetyCenterMultiUsersTest {
.safetyCenterEntryDefaultStaticBuilder(
STATIC_ALL_OPTIONAL_ID,
userId = deviceState.workProfile().id(),
- title = "Paste"
+ title = "Paste",
)
.setPendingIntent(
createTestActivityRedirectPendingIntentForUser(
deviceState.workProfile().userHandle(),
- explicit = false
+ explicit = false,
)
)
@@ -287,12 +287,12 @@ class SafetyCenterMultiUsersTest {
.safetyCenterEntryDefaultStaticBuilder(
STATIC_ALL_OPTIONAL_ID,
userId = deviceState.privateProfile().id(),
- title = "Unknown"
+ title = "Unknown",
)
.setPendingIntent(
createTestActivityRedirectPendingIntentForUser(
deviceState.privateProfile().userHandle(),
- explicit = false
+ explicit = false,
)
)
@@ -320,20 +320,20 @@ class SafetyCenterMultiUsersTest {
.setPendingIntent(
createTestActivityRedirectPendingIntentForUser(
deviceState.workProfile().userHandle(),
- explicit
+ explicit,
)
)
private fun staticEntryForPrivateBuilder(
title: CharSequence = "Unknown",
- explicit: Boolean = true
+ explicit: Boolean = true,
) =
SafetyCenterStaticEntry.Builder(title)
.setSummary("OK")
.setPendingIntent(
createTestActivityRedirectPendingIntentForUser(
deviceState.privateProfile().userHandle(),
- explicit
+ explicit,
)
)
@@ -374,18 +374,20 @@ class SafetyCenterMultiUsersTest {
safetyCenterTestData.safetyCenterStatusUnknown,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
safetyCenterTestData.safetyCenterEntryDefault(
SINGLE_SOURCE_ALL_PROFILE_ID,
deviceState.additionalUser().id(),
pendingIntent =
createTestActivityRedirectPendingIntentForUser(
deviceState.additionalUser().userHandle()
- )
- )
+ ),
+ ),
+ "No info yet",
)
),
- emptyList()
+ emptyList(),
)
@get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
@@ -449,7 +451,7 @@ class SafetyCenterMultiUsersTest {
getSafetyCenterManagerForUser(deviceState.workProfile().userHandle())
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForWork
+ dataForWork,
)
setQuietMode(true)
@@ -470,7 +472,7 @@ class SafetyCenterMultiUsersTest {
val dataForAdditionalUser = safetySourceTestData.information
additionalUserSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForAdditionalUser
+ dataForAdditionalUser,
)
checkState(
additionalUserSafetyCenterManager.getSafetySourceDataWithInteractAcrossUsersPermission(
@@ -510,7 +512,7 @@ class SafetyCenterMultiUsersTest {
val dataForWork = safetySourceTestData.informationWithIssueForWork
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForWork
+ dataForWork,
)
TestApis.packages().find(context.packageName).uninstall(deviceState.workProfile())
@@ -531,7 +533,7 @@ class SafetyCenterMultiUsersTest {
val dataForWork = safetySourceTestData.informationWithIssueForWork
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForWork
+ dataForWork,
)
deviceState.workProfile().remove()
@@ -552,7 +554,7 @@ class SafetyCenterMultiUsersTest {
val dataForWork = safetySourceTestData.informationWithIssueForWork
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForWork
+ dataForWork,
)
setQuietMode(true)
@@ -593,14 +595,14 @@ class SafetyCenterMultiUsersTest {
staticGroupBuilder
.setEntries(listOf(staticBarebone, staticAllOptional))
.build()
- )
+ ),
),
listOf(
SafetyCenterStaticEntryGroup(
"OK",
- listOf(createStaticEntry(), createStaticEntry(explicit = false))
+ listOf(createStaticEntry(), createStaticEntry(explicit = false)),
)
- )
+ ),
)
assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
}
@@ -628,7 +630,7 @@ class SafetyCenterMultiUsersTest {
listOf(
dynamicBareboneDefault,
dynamicDisabledDefault,
- dynamicDisabledForWorkDefault
+ dynamicDisabledForWorkDefault,
)
)
.setSeverityUnspecifiedIconType(
@@ -642,7 +644,7 @@ class SafetyCenterMultiUsersTest {
listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork)
)
.build()
- )
+ ),
),
listOf(
SafetyCenterStaticEntryGroup(
@@ -651,10 +653,10 @@ class SafetyCenterMultiUsersTest {
createStaticEntry(),
createStaticEntryForWork(),
createStaticEntry(explicit = false),
- createStaticEntryForWork(explicit = false)
- )
+ createStaticEntryForWork(explicit = false),
+ ),
)
- )
+ ),
)
assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
}
@@ -682,7 +684,7 @@ class SafetyCenterMultiUsersTest {
dynamicBareboneUpdated,
dynamicDisabledUpdated,
dynamicDisabledForWorkDefault,
- dynamicHiddenUpdated
+ dynamicHiddenUpdated,
)
)
.setSeverityUnspecifiedIconType(
@@ -696,7 +698,7 @@ class SafetyCenterMultiUsersTest {
listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork)
)
.build()
- )
+ ),
),
listOf(
SafetyCenterStaticEntryGroup(
@@ -705,10 +707,10 @@ class SafetyCenterMultiUsersTest {
staticEntryUpdated,
createStaticEntryForWork(),
createStaticEntry(explicit = false),
- createStaticEntryForWork(explicit = false)
- )
+ createStaticEntryForWork(explicit = false),
+ ),
)
- )
+ ),
)
assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
}
@@ -730,56 +732,56 @@ class SafetyCenterMultiUsersTest {
listOf(
safetyCenterTestData.safetyCenterIssueCritical(
DYNAMIC_BAREBONE_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueCritical(
ISSUE_ONLY_BAREBONE_ID,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
DYNAMIC_DISABLED_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_DISABLED_ID,
managedUserId,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_HIDDEN_ID,
managedUserId,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
managedUserId,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_IN_STATELESS_ID,
managedUserId,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_IN_STATELESS_ID,
managedUserId,
- groupId = MIXED_STATELESS_GROUP_ID
- )
+ groupId = MIXED_STATELESS_GROUP_ID,
+ ),
),
listOf(
SafetyCenterEntryOrGroup(
@@ -792,7 +794,7 @@ class SafetyCenterMultiUsersTest {
dynamicDisabledUpdated,
dynamicDisabledForWorkUpdated,
dynamicHiddenUpdated,
- dynamicHiddenForWorkUpdated
+ dynamicHiddenForWorkUpdated,
)
)
.setSeverityUnspecifiedIconType(
@@ -806,7 +808,7 @@ class SafetyCenterMultiUsersTest {
listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork)
)
.build()
- )
+ ),
),
listOf(
SafetyCenterStaticEntryGroup(
@@ -815,10 +817,10 @@ class SafetyCenterMultiUsersTest {
staticEntryUpdated,
staticEntryForWorkUpdated,
createStaticEntry(explicit = false),
- createStaticEntryForWork(explicit = false)
- )
+ createStaticEntryForWork(explicit = false),
+ ),
)
- )
+ ),
)
assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
}
@@ -842,56 +844,56 @@ class SafetyCenterMultiUsersTest {
listOf(
safetyCenterTestData.safetyCenterIssueCritical(
DYNAMIC_BAREBONE_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueCritical(
ISSUE_ONLY_BAREBONE_ID,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
DYNAMIC_DISABLED_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_DISABLED_ID,
managedUserId,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_HIDDEN_ID,
managedUserId,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
managedUserId,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_IN_STATELESS_ID,
managedUserId,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_IN_STATELESS_ID,
managedUserId,
- groupId = MIXED_STATELESS_GROUP_ID
- )
+ groupId = MIXED_STATELESS_GROUP_ID,
+ ),
),
listOf(
SafetyCenterEntryOrGroup(
@@ -904,7 +906,7 @@ class SafetyCenterMultiUsersTest {
dynamicDisabledUpdated,
dynamicDisabledForWorkUpdated,
dynamicHiddenUpdated,
- dynamicHiddenForWorkUpdated
+ dynamicHiddenForWorkUpdated,
)
)
.setSeverityUnspecifiedIconType(
@@ -918,7 +920,7 @@ class SafetyCenterMultiUsersTest {
listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork)
)
.build()
- )
+ ),
),
listOf(
SafetyCenterStaticEntryGroup(
@@ -927,10 +929,10 @@ class SafetyCenterMultiUsersTest {
staticEntryUpdated,
staticEntryForWorkUpdated,
createStaticEntry(explicit = false),
- createStaticEntryForWork(explicit = false)
- )
+ createStaticEntryForWork(explicit = false),
+ ),
)
- )
+ ),
)
assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
}
@@ -956,82 +958,82 @@ class SafetyCenterMultiUsersTest {
listOf(
safetyCenterTestData.safetyCenterIssueCritical(
DYNAMIC_BAREBONE_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueCritical(
ISSUE_ONLY_BAREBONE_ID,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
DYNAMIC_DISABLED_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_DISABLED_ID,
managedUserId,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_HIDDEN_ID,
managedUserId,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
managedUserId,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_IN_STATELESS_ID,
managedUserId,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_IN_STATELESS_ID,
managedUserId,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_DISABLED_ID,
privateProfileId,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_HIDDEN_ID,
privateProfileId,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
privateProfileId,
attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_IN_STATELESS_ID,
privateProfileId,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_IN_STATELESS_ID,
privateProfileId,
- groupId = MIXED_STATELESS_GROUP_ID
- )
+ groupId = MIXED_STATELESS_GROUP_ID,
+ ),
),
listOf(
SafetyCenterEntryOrGroup(
@@ -1046,7 +1048,7 @@ class SafetyCenterMultiUsersTest {
dynamicDisabledForPrivateUpdated,
dynamicHiddenUpdated,
dynamicHiddenForWorkUpdated,
- dynamicHiddenForPrivateUpdated
+ dynamicHiddenForPrivateUpdated,
)
)
.setSeverityUnspecifiedIconType(
@@ -1061,11 +1063,11 @@ class SafetyCenterMultiUsersTest {
staticBarebone,
staticAllOptional,
staticAllOptionalForWork,
- staticAllOptionalForPrivate
+ staticAllOptionalForPrivate,
)
)
.build()
- )
+ ),
),
listOf(
SafetyCenterStaticEntryGroup(
@@ -1076,10 +1078,10 @@ class SafetyCenterMultiUsersTest {
staticEntryForPrivateUpdated,
createStaticEntry(explicit = false),
createStaticEntryForWork(explicit = false),
- createStaticEntryForPrivate(explicit = false)
- )
+ createStaticEntryForPrivate(explicit = false),
+ ),
)
- )
+ ),
)
assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
}
@@ -1124,11 +1126,11 @@ class SafetyCenterMultiUsersTest {
listOf(
staticBarebone,
staticAllOptional,
- staticAllOptionalForWorkPaused
+ staticAllOptionalForWorkPaused,
)
)
.build()
- )
+ ),
),
listOf(
SafetyCenterStaticEntryGroup(
@@ -1137,10 +1139,10 @@ class SafetyCenterMultiUsersTest {
staticEntryUpdated,
staticEntryForWorkPausedUpdated,
createStaticEntry(explicit = false),
- createStaticEntryForWorkPaused()
- )
+ createStaticEntryForWorkPaused(),
+ ),
)
- )
+ ),
)
assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
}
@@ -1157,7 +1159,7 @@ class SafetyCenterMultiUsersTest {
getSafetyCenterManagerForUser(deviceState.workProfile().userHandle())
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForPrimaryUserWorkProfile
+ dataForPrimaryUserWorkProfile,
)
val additionalUserSafetyCenterManager =
@@ -1199,8 +1201,8 @@ class SafetyCenterMultiUsersTest {
pendingIntent =
createTestActivityRedirectPendingIntentForUser(
deviceState.workProfile().userHandle()
- )
- )
+ ),
+ ),
)
)
.setSeverityUnspecifiedIconType(
@@ -1209,7 +1211,7 @@ class SafetyCenterMultiUsersTest {
.build()
)
),
- emptyList()
+ emptyList(),
)
checkState(
safetyCenterManager.getSafetyCenterDataWithPermission() ==
@@ -1227,11 +1229,13 @@ class SafetyCenterMultiUsersTest {
safetyCenterTestData.safetyCenterStatusUnknown,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ALL_PROFILE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ALL_PROFILE_ID),
+ "No info yet",
)
),
- emptyList()
+ emptyList(),
)
assertThat(safetyCenterManager.getSafetyCenterDataWithPermission())
.isEqualTo(safetyCenterDataForPrimaryUser)
@@ -1271,8 +1275,8 @@ class SafetyCenterMultiUsersTest {
pendingIntent =
createTestActivityRedirectPendingIntentForUser(
deviceState.privateProfile().userHandle()
- )
- )
+ ),
+ ),
)
)
.setSeverityUnspecifiedIconType(
@@ -1281,7 +1285,7 @@ class SafetyCenterMultiUsersTest {
.build()
)
),
- emptyList()
+ emptyList(),
)
assertThat(safetyCenterManager.getSafetyCenterDataWithPermission())
@@ -1298,11 +1302,13 @@ class SafetyCenterMultiUsersTest {
safetyCenterTestData.safetyCenterStatusUnknown,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ALL_PROFILE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ALL_PROFILE_ID),
+ "No info yet",
)
),
- emptyList()
+ emptyList(),
)
assertThat(safetyCenterManager.getSafetyCenterDataWithPermission())
.isEqualTo(safetyCenterDataForPrimaryUser)
@@ -1345,7 +1351,7 @@ class SafetyCenterMultiUsersTest {
assertFailsWith(IllegalArgumentException::class) {
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- dataForWork
+ dataForWork,
)
}
}
@@ -1362,7 +1368,7 @@ class SafetyCenterMultiUsersTest {
managedSafetyCenterManager.setSafetySourceData(
SINGLE_SOURCE_ALL_PROFILE_ID,
dataForWork,
- EVENT_SOURCE_STATE_CHANGED
+ EVENT_SOURCE_STATE_CHANGED,
)
}
}
@@ -1378,7 +1384,7 @@ class SafetyCenterMultiUsersTest {
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForWork
+ dataForWork,
)
val safetySourceData =
@@ -1399,7 +1405,7 @@ class SafetyCenterMultiUsersTest {
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForWork
+ dataForWork,
)
val safetySourceData =
@@ -1419,7 +1425,7 @@ class SafetyCenterMultiUsersTest {
clonedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForClone
+ dataForClone,
)
val safetySourceData =
@@ -1440,7 +1446,7 @@ class SafetyCenterMultiUsersTest {
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForWork
+ dataForWork,
)
val safetySourceData =
@@ -1464,7 +1470,7 @@ class SafetyCenterMultiUsersTest {
SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue)
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
ISSUE_ONLY_ALL_PROFILE_SOURCE_ID,
- dataForWorkProfile
+ dataForWorkProfile,
)
val apiSafetySourceData =
@@ -1488,7 +1494,7 @@ class SafetyCenterMultiUsersTest {
assertFailsWith(IllegalArgumentException::class) {
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ID,
- dataForWork
+ dataForWork,
)
}
}
@@ -1505,7 +1511,7 @@ class SafetyCenterMultiUsersTest {
getSafetyCenterManagerForUser(deviceState.workProfile().userHandle())
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForWork
+ dataForWork,
)
val apiSafetySourceData =
@@ -1532,14 +1538,14 @@ class SafetyCenterMultiUsersTest {
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ALL_PROFILE_ID,
- dataForWork
+ dataForWork,
)
TestNotificationListener.waitForNotificationsMatching(
NotificationCharacteristics(
title = "Information issue title",
text = "Information issue summary",
- actions = listOf("Review")
+ actions = listOf("Review"),
)
)
}
@@ -1555,7 +1561,7 @@ class SafetyCenterMultiUsersTest {
getSafetyCenterManagerForUser(deviceState.additionalUser().userHandle())
additionalUserSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ID,
- dataForPrimaryUser
+ dataForPrimaryUser,
)
val apiSafetySourceData =
@@ -1577,7 +1583,7 @@ class SafetyCenterMultiUsersTest {
getSafetyCenterManagerForUser(deviceState.additionalUser().userHandle())
additionalUserSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ID,
- dataForAdditionalUser
+ dataForAdditionalUser,
)
val apiSafetySourceDataForPrimaryUser =
@@ -1600,7 +1606,7 @@ class SafetyCenterMultiUsersTest {
getSafetyCenterManagerForUser(deviceState.additionalUser().userHandle())
additionalUserSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
SINGLE_SOURCE_ID,
- dataForAdditionalUser
+ dataForAdditionalUser,
)
val apiSafetySourceDataForPrimaryUser =
@@ -1627,12 +1633,12 @@ class SafetyCenterMultiUsersTest {
private fun createTestActivityRedirectPendingIntentForUser(
user: UserHandle,
- explicit: Boolean = true
+ explicit: Boolean = true,
): PendingIntent {
return callWithShellPermissionIdentity(INTERACT_ACROSS_USERS) {
SafetySourceTestData.createRedirectPendingIntent(
getContextForUser(user),
- SafetySourceTestData.createTestActivityIntent(getContextForUser(user), explicit)
+ SafetySourceTestData.createTestActivityIntent(getContextForUser(user), explicit),
)
}
}
@@ -1647,7 +1653,7 @@ class SafetyCenterMultiUsersTest {
private fun SafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
id: String,
dataToSet: SafetySourceData,
- safetyEvent: SafetyEvent = EVENT_SOURCE_STATE_CHANGED
+ safetyEvent: SafetyEvent = EVENT_SOURCE_STATE_CHANGED,
) =
callWithShellPermissionIdentity(INTERACT_ACROSS_USERS_FULL) {
setSafetySourceDataWithPermission(id, dataToSet, safetyEvent)
@@ -1691,28 +1697,28 @@ class SafetyCenterMultiUsersTest {
private fun updatePrimaryProfileSources() {
safetyCenterTestHelper.setData(
DYNAMIC_BAREBONE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
DYNAMIC_DISABLED_ID,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(DYNAMIC_HIDDEN_ID, safetySourceTestData.unspecified)
safetyCenterTestHelper.setData(
ISSUE_ONLY_BAREBONE_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue),
)
safetyCenterTestHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue),
)
safetyCenterTestHelper.setData(
DYNAMIC_IN_STATELESS_ID,
- safetySourceTestData.unspecifiedWithIssue
+ safetySourceTestData.unspecifiedWithIssue,
)
safetyCenterTestHelper.setData(
ISSUE_ONLY_IN_STATELESS_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue),
)
}
@@ -1721,23 +1727,23 @@ class SafetyCenterMultiUsersTest {
getSafetyCenterManagerForUser(deviceState.workProfile().userHandle())
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
DYNAMIC_DISABLED_ID,
- safetySourceTestData.informationWithIssueForWork
+ safetySourceTestData.informationWithIssueForWork,
)
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
DYNAMIC_HIDDEN_ID,
- safetySourceTestData.informationWithIssueForWork
+ safetySourceTestData.informationWithIssueForWork,
)
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue),
)
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
DYNAMIC_IN_STATELESS_ID,
- safetySourceTestData.unspecifiedWithIssueForWork
+ safetySourceTestData.unspecifiedWithIssueForWork,
)
managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
ISSUE_ONLY_IN_STATELESS_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue),
)
}
@@ -1746,23 +1752,23 @@ class SafetyCenterMultiUsersTest {
getSafetyCenterManagerForUser(deviceState.privateProfile().userHandle())
privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
DYNAMIC_DISABLED_ID,
- safetySourceTestData.informationWithIssueForPrivate
+ safetySourceTestData.informationWithIssueForPrivate,
)
privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
DYNAMIC_HIDDEN_ID,
- safetySourceTestData.informationWithIssueForPrivate
+ safetySourceTestData.informationWithIssueForPrivate,
)
privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue),
)
privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
DYNAMIC_IN_STATELESS_ID,
- safetySourceTestData.unspecifiedWithIssueForPrivate
+ safetySourceTestData.unspecifiedWithIssueForPrivate,
)
privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
ISSUE_ONLY_IN_STATELESS_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue),
)
}
}
diff --git a/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt b/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt
index 73f435615..ee0326bd3 100644
--- a/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt
+++ b/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt
@@ -73,7 +73,9 @@ import com.android.safetycenter.testing.UiTestHelper.waitPageTitleDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitSourceDataDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueNotDisplayed
+import java.util.regex.Pattern
import org.junit.After
+import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
import org.junit.Rule
import org.junit.Test
@@ -99,13 +101,13 @@ class SafetyCenterActivityTest {
}
@Test
- fun launchActivity_allowingSettingsTrampoline() {
+ fun launchActivity_allowingSettingsTrampoline_showsSafetyCenter() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
val dataToDisplay = safetySourceTestData.criticalWithResolvingGeneralIssue
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, dataToDisplay)
context.launchSafetyCenterActivity(preventTrampolineToSettings = false) {
- waitSourceDataDisplayed(dataToDisplay)
+ waitSourceIssueDisplayed(dataToDisplay.issues[0])
}
}
@@ -120,7 +122,7 @@ class SafetyCenterActivityTest {
context.getString(safetyCenterTestConfigs.staticSource1.summaryResId),
context.getString(safetyCenterTestConfigs.staticSourceGroup2.titleResId),
context.getString(safetyCenterTestConfigs.staticSource2.titleResId),
- context.getString(safetyCenterTestConfigs.staticSource2.summaryResId)
+ context.getString(safetyCenterTestConfigs.staticSource2.summaryResId),
)
}
}
@@ -131,7 +133,13 @@ class SafetyCenterActivityTest {
val dataToDisplay = safetySourceTestData.criticalWithResolvingGeneralIssue
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, dataToDisplay)
- context.launchSafetyCenterActivity { waitSourceDataDisplayed(dataToDisplay) }
+ context.launchSafetyCenterActivity {
+ if (SafetyCenterFlags.showSubpages) {
+ waitSourceIssueDisplayed(dataToDisplay.issues[0])
+ } else {
+ waitSourceDataDisplayed(dataToDisplay)
+ }
+ }
}
@Test
@@ -144,8 +152,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_1_TITLE,
entrySummary = SAFETY_SOURCE_1_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_2,
@@ -153,8 +161,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_2_TITLE,
entrySummary = SAFETY_SOURCE_2_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_3,
@@ -162,8 +170,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_3_TITLE,
entrySummary = SAFETY_SOURCE_3_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_4,
@@ -171,8 +179,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_4_TITLE,
entrySummary = SAFETY_SOURCE_4_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_5,
@@ -180,8 +188,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_5_TITLE,
entrySummary = SAFETY_SOURCE_5_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
}
@@ -190,7 +198,7 @@ class SafetyCenterActivityTest {
context.getString(safetyCenterTestConfigs.dynamicSourceGroup1.titleResId),
context.getString(safetyCenterTestConfigs.dynamicSourceGroup1.summaryResId),
context.getString(safetyCenterTestConfigs.dynamicSourceGroup3.titleResId),
- context.getString(safetyCenterTestConfigs.dynamicSourceGroup3.summaryResId)
+ context.getString(safetyCenterTestConfigs.dynamicSourceGroup3.summaryResId),
)
waitAllTextNotDisplayed(
SAFETY_SOURCE_1_TITLE,
@@ -200,7 +208,7 @@ class SafetyCenterActivityTest {
SAFETY_SOURCE_4_TITLE,
SAFETY_SOURCE_4_SUMMARY,
SAFETY_SOURCE_5_TITLE,
- SAFETY_SOURCE_5_SUMMARY
+ SAFETY_SOURCE_5_SUMMARY,
)
}
}
@@ -215,8 +223,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_1_TITLE,
entrySummary = SAFETY_SOURCE_1_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_2,
@@ -224,8 +232,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_RECOMMENDATION,
entryTitle = SAFETY_SOURCE_2_TITLE,
entrySummary = SAFETY_SOURCE_2_SUMMARY,
- withIssue = true
- )
+ withIssue = true,
+ ),
)
setData(
SOURCE_ID_3,
@@ -233,8 +241,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_3_TITLE,
entrySummary = SAFETY_SOURCE_3_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_4,
@@ -242,8 +250,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_RECOMMENDATION,
entryTitle = SAFETY_SOURCE_4_TITLE,
entrySummary = SAFETY_SOURCE_4_SUMMARY,
- withIssue = true
- )
+ withIssue = true,
+ ),
)
setData(
SOURCE_ID_5,
@@ -251,8 +259,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_CRITICAL_WARNING,
entryTitle = SAFETY_SOURCE_5_TITLE,
entrySummary = SAFETY_SOURCE_5_SUMMARY,
- withIssue = true
- )
+ withIssue = true,
+ ),
)
}
@@ -261,7 +269,7 @@ class SafetyCenterActivityTest {
context.getString(safetyCenterTestConfigs.dynamicSourceGroup1.titleResId),
SAFETY_SOURCE_2_SUMMARY,
context.getString(safetyCenterTestConfigs.dynamicSourceGroup3.titleResId),
- SAFETY_SOURCE_5_SUMMARY
+ SAFETY_SOURCE_5_SUMMARY,
)
waitAllTextNotDisplayed(
SAFETY_SOURCE_1_TITLE,
@@ -269,13 +277,15 @@ class SafetyCenterActivityTest {
SAFETY_SOURCE_1_SUMMARY,
SAFETY_SOURCE_4_TITLE,
SAFETY_SOURCE_5_TITLE,
- SAFETY_SOURCE_4_SUMMARY
+ SAFETY_SOURCE_4_SUMMARY,
)
}
}
@Test
fun launchActivity_displaysGroupsOfSingleSourceAsEntity() {
+ // Single source groups are displayed in the subpage when subpages enabled
+ assumeFalse(SafetyCenterFlags.showSubpages)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig)
context.launchSafetyCenterActivity {
@@ -297,7 +307,11 @@ class SafetyCenterActivityTest {
val dataToDisplay = safetySourceTestData.recommendationWithGeneralIssue
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, dataToDisplay)
- waitSourceDataDisplayed(dataToDisplay)
+ if (SafetyCenterFlags.showSubpages) {
+ waitSourceIssueDisplayed(dataToDisplay.issues[0])
+ } else {
+ waitSourceDataDisplayed(dataToDisplay)
+ }
}
}
@@ -338,6 +352,8 @@ class SafetyCenterActivityTest {
@Test
fun entryListWithEntryGroup_informationState_hasContentDescription() {
+ // No custom content descriptions when using subpages
+ assumeFalse(SafetyCenterFlags.showSubpages)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(SOURCE_ID_1, safetySourceTestData.information)
safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information)
@@ -355,10 +371,12 @@ class SafetyCenterActivityTest {
@Test
fun entryListWithEntryGroup_recommendationState_hasActionsNeededContentDescription() {
+ // No custom content descriptions when using subpages
+ assumeFalse(SafetyCenterFlags.showSubpages)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.information)
@@ -394,6 +412,8 @@ class SafetyCenterActivityTest {
@Test
fun entryListWithEntryGroup_unclickableDisabledEntry_hasContentDescription() {
+ // No custom content descriptions when using subpages
+ assumeFalse(SafetyCenterFlags.showSubpages)
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesConfigWithSourceWithInvalidIntent
)
@@ -412,7 +432,7 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.unspecifiedDisabledWithTestActivityRedirect
+ safetySourceTestData.unspecifiedDisabledWithTestActivityRedirect,
)
context.launchSafetyCenterActivity {
@@ -424,10 +444,12 @@ class SafetyCenterActivityTest {
@Test
fun entryListWithEntryGroup_clickableDisabledEntry_hasContentDescription() {
+ // No custom content descriptions when using subpages
+ assumeFalse(SafetyCenterFlags.showSubpages)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.unspecifiedDisabledWithTestActivityRedirect
+ safetySourceTestData.unspecifiedDisabledWithTestActivityRedirect,
)
context.launchSafetyCenterActivity {
@@ -442,6 +464,8 @@ class SafetyCenterActivityTest {
@Test
fun entryListWithSingleSource_informationState_hasContentDescription() {
+ // No custom content descriptions when using subpages
+ assumeFalse(SafetyCenterFlags.showSubpages)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
@@ -456,6 +480,9 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
context.launchSafetyCenterActivity {
+ if (SafetyCenterFlags.showSubpages) {
+ waitDisplayed(By.text("OK")) { it.click() } // Open subpage
+ }
waitDisplayed(By.text("OK")) { it.click() }
waitButtonDisplayed("Exit test activity") { it.click() }
waitDisplayed(By.text("OK"))
@@ -467,6 +494,9 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.implicitIntentSingleSourceConfig)
context.launchSafetyCenterActivity {
+ if (SafetyCenterFlags.showSubpages) {
+ waitDisplayed(By.text("OK")) { it.click() } // Open subpage
+ }
waitDisplayed(By.text("OK")) { it.click() }
waitButtonDisplayed("Exit test activity") { it.click() }
}
@@ -478,6 +508,9 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
context.launchSafetyCenterActivity {
+ if (SafetyCenterFlags.showSubpages) {
+ waitDisplayed(By.text("OK")) { it.click() } // Open subpage
+ }
waitDisplayed(By.text("Ok title")) { it.click() }
waitButtonDisplayed("Exit test activity") { it.click() }
waitDisplayed(By.text("Ok title"))
@@ -489,10 +522,13 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.informationWithIconAction
+ safetySourceTestData.informationWithIconAction,
)
context.launchSafetyCenterActivity {
+ if (SafetyCenterFlags.showSubpages) {
+ waitDisplayed(By.text("OK")) { it.click() } // Open subpage
+ }
waitDisplayed(By.desc("Information")) { it.click() }
waitButtonDisplayed("Exit test activity") { it.click() }
waitDisplayed(By.text("Ok title"))
@@ -523,7 +559,7 @@ class SafetyCenterActivityTest {
val issue = safetySourceTestData.recommendationGeneralIssue
safetyCenterTestHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(issue)
+ SafetySourceTestData.issuesOnly(issue),
)
context.launchSafetyCenterActivity { waitDisplayed(By.desc("Alert. ${issue.title}")) }
@@ -553,7 +589,6 @@ class SafetyCenterActivityTest {
clickDismissIssueCard()
waitSourceIssueNotDisplayed(safetySourceTestData.informationIssue)
- waitSourceDataDisplayed(safetySourceTestData.information)
waitButtonDisplayed(RESCAN_BUTTON_LABEL)
}
}
@@ -563,7 +598,7 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
context.launchSafetyCenterActivity {
@@ -581,21 +616,21 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
context.launchSafetyCenterActivity {
clickDismissIssueCard()
waitAllTextDisplayed(
"Dismiss this alert?",
- "Review your security and privacy settings any time to add more protection"
+ "Review your security and privacy settings any time to add more protection",
)
getUiDevice().rotate()
waitAllTextDisplayed(
"Dismiss this alert?",
- "Review your security and privacy settings any time to add more protection"
+ "Review your security and privacy settings any time to add more protection",
)
clickConfirmDismissal()
@@ -609,7 +644,7 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
context.launchSafetyCenterActivity {
@@ -626,7 +661,7 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
context.launchSafetyCenterActivity {
@@ -650,13 +685,13 @@ class SafetyCenterActivityTest {
// Set the initial data for the source
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage
+ safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage,
)
// Clear the data when action is triggered to simulate resolution.
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.ClearData
+ Response.ClearData,
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -680,13 +715,13 @@ class SafetyCenterActivityTest {
// Set the initial data for the source
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage
+ safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage,
)
// Clear the data when action is triggered to simulate resolution.
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.ClearData
+ Response.ClearData,
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -708,13 +743,13 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation
+ safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation,
)
// Clear the data when action is triggered to simulate resolution.
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.ClearData
+ Response.ClearData,
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -734,13 +769,13 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation
+ safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation,
)
// Clear the data when action is triggered to simulate resolution.
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.ClearData
+ Response.ClearData,
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -764,7 +799,7 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation
+ safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation,
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -786,13 +821,13 @@ class SafetyCenterActivityTest {
// Set the initial data for the source
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
// Clear the data when action is triggered to simulate resolution.
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.ClearData
+ Response.ClearData,
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -909,11 +944,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -923,7 +958,7 @@ class SafetyCenterActivityTest {
waitExpandedIssuesDisplayed(
safetySourceTestData.criticalResolvingGeneralIssue,
safetySourceTestData.recommendationGeneralIssue,
- safetySourceTestData.informationIssue
+ safetySourceTestData.informationIssue,
)
}
}
@@ -933,11 +968,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -948,7 +983,7 @@ class SafetyCenterActivityTest {
waitCollapsedIssuesDisplayed(
safetySourceTestData.criticalResolvingGeneralIssue,
safetySourceTestData.recommendationGeneralIssue,
- safetySourceTestData.informationIssue
+ safetySourceTestData.informationIssue,
)
}
}
@@ -958,11 +993,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.criticalWithRedirectingIssue
+ safetySourceTestData.criticalWithRedirectingIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -973,7 +1008,7 @@ class SafetyCenterActivityTest {
waitCollapsedIssuesDisplayed(
safetySourceTestData.criticalRedirectingIssue,
safetySourceTestData.criticalResolvingGeneralIssue,
- safetySourceTestData.informationIssue
+ safetySourceTestData.informationIssue,
)
}
}
@@ -983,11 +1018,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -1007,11 +1042,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -1022,7 +1057,7 @@ class SafetyCenterActivityTest {
waitCollapsedIssuesDisplayed(
safetySourceTestData.criticalResolvingGeneralIssue,
safetySourceTestData.recommendationGeneralIssue,
- safetySourceTestData.informationIssue
+ safetySourceTestData.informationIssue,
)
}
}
@@ -1032,7 +1067,7 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
context.launchSafetyCenterActivity {
@@ -1046,11 +1081,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -1058,7 +1093,7 @@ class SafetyCenterActivityTest {
waitCollapsedIssuesDisplayed(
safetySourceTestData.criticalResolvingGeneralIssue,
safetySourceTestData.recommendationGeneralIssue,
- safetySourceTestData.informationIssue
+ safetySourceTestData.informationIssue,
)
}
}
@@ -1068,11 +1103,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -1084,7 +1119,7 @@ class SafetyCenterActivityTest {
waitExpandedIssuesDisplayed(
safetySourceTestData.criticalResolvingGeneralIssue,
safetySourceTestData.recommendationGeneralIssue,
- safetySourceTestData.informationIssue
+ safetySourceTestData.informationIssue,
)
}
}
@@ -1094,11 +1129,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -1118,7 +1153,7 @@ class SafetyCenterActivityTest {
waitExpandedIssuesDisplayed(
safetySourceTestData.criticalResolvingGeneralIssue,
safetySourceTestData.recommendationGeneralIssue,
- safetySourceTestData.informationIssue
+ safetySourceTestData.informationIssue,
)
}
}
@@ -1128,11 +1163,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -1152,11 +1187,11 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue)
@@ -1173,7 +1208,7 @@ class SafetyCenterActivityTest {
}
@Test
- fun collapsedEntryGroup_expandsWhenClicked() {
+ fun entryGroup_showsEntriesWhenClicked() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig)
with(safetyCenterTestHelper) {
setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig)
@@ -1183,8 +1218,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_1_TITLE,
entrySummary = SAFETY_SOURCE_1_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_2,
@@ -1192,8 +1227,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_2_TITLE,
entrySummary = SAFETY_SOURCE_2_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_3,
@@ -1201,8 +1236,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_3_TITLE,
entrySummary = SAFETY_SOURCE_3_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_4,
@@ -1210,8 +1245,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_4_TITLE,
entrySummary = SAFETY_SOURCE_4_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_5,
@@ -1219,8 +1254,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_5_TITLE,
entrySummary = SAFETY_SOURCE_5_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
}
@@ -1238,13 +1273,14 @@ class SafetyCenterActivityTest {
SAFETY_SOURCE_1_TITLE,
SAFETY_SOURCE_1_SUMMARY,
SAFETY_SOURCE_2_TITLE,
- SAFETY_SOURCE_2_SUMMARY
+ SAFETY_SOURCE_2_SUMMARY,
)
}
}
@Test
fun expandedEntryGroup_collapsesWhenClicked() {
+ assumeFalse(SafetyCenterFlags.showSubpages) // No collapsible groups when using subpages
with(safetyCenterTestHelper) {
setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig)
setData(
@@ -1253,8 +1289,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_1_TITLE,
entrySummary = SAFETY_SOURCE_1_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_2,
@@ -1262,8 +1298,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_2_TITLE,
entrySummary = SAFETY_SOURCE_2_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_3,
@@ -1271,8 +1307,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_3_TITLE,
entrySummary = SAFETY_SOURCE_3_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_4,
@@ -1280,8 +1316,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_4_TITLE,
entrySummary = SAFETY_SOURCE_4_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_5,
@@ -1289,8 +1325,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_5_TITLE,
entrySummary = SAFETY_SOURCE_5_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
}
@@ -1332,13 +1368,14 @@ class SafetyCenterActivityTest {
)
waitAllTextDisplayed(
context.getString(safetyCenterTestConfigs.dynamicSource1.titleResId),
- context.getString(safetyCenterTestConfigs.dynamicSource2.titleResId)
+ context.getString(safetyCenterTestConfigs.dynamicSource2.titleResId),
)
}
}
@Test
fun expandedEntryGroup_otherGroupRemainsCollapsed() {
+ assumeFalse(SafetyCenterFlags.showSubpages) // No collapsible groups when using subpages
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig)
with(safetyCenterTestHelper) {
setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig)
@@ -1348,8 +1385,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_1_TITLE,
entrySummary = SAFETY_SOURCE_1_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_2,
@@ -1357,8 +1394,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_2_TITLE,
entrySummary = SAFETY_SOURCE_2_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_3,
@@ -1366,8 +1403,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_3_TITLE,
entrySummary = SAFETY_SOURCE_3_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_4,
@@ -1375,8 +1412,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_4_TITLE,
entrySummary = SAFETY_SOURCE_4_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
setData(
SOURCE_ID_5,
@@ -1384,8 +1421,8 @@ class SafetyCenterActivityTest {
severityLevel = SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_5_TITLE,
entrySummary = SAFETY_SOURCE_5_SUMMARY,
- withIssue = false
- )
+ withIssue = false,
+ ),
)
}
@@ -1425,7 +1462,7 @@ class SafetyCenterActivityTest {
context.launchSafetyCenterActivity {
waitAllTextDisplayed(
context.getString(lastGroup.titleResId),
- context.getString(lastGroup.summaryResId)
+ context.getString(lastGroup.summaryResId),
)
waitDisplayed(By.text(context.getString(firstGroup.titleResId))) { it.click() }
@@ -1444,6 +1481,9 @@ class SafetyCenterActivityTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.implicitIntentSingleSourceConfig)
context.launchSafetyCenterActivity {
+ if (SafetyCenterFlags.showSubpages) {
+ waitDisplayed(By.text("OK")) { it.click() } // Open subpage
+ }
waitDisplayed(By.text("OK")) { it.click() }
waitDisplayed(By.text("is_from_settings_homepage false"))
waitButtonDisplayed("Exit test activity") { it.click() }
@@ -1477,9 +1517,10 @@ class SafetyCenterActivityTest {
)
safetyCenterTestHelper.setEnabled(false)
+ val containsPrivacyPattern = Pattern.compile(".*[Pp]rivacy|[Pp]ermission.*") // NOTYPO
context.launchSafetyCenterActivity(intentAction = PRIVACY_CONTROLS_ACTION) {
waitDisplayed(By.pkg(context.getSettingsPackageName()))
- waitPageTitleDisplayed("Privacy")
+ waitDisplayed(By.text(containsPrivacyPattern))
}
}
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/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt
index cb3935ec5..64db7d47a 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt
+++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt
@@ -104,6 +104,7 @@ import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.MIXED_
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.MIXED_STATELESS_GROUP_ID
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.MULTIPLE_SOURCES_GROUP_ID_1
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.MULTIPLE_SOURCES_GROUP_ID_2
+import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_GROUP_ID
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_1
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_2
@@ -160,7 +161,7 @@ class SafetyCenterManagerTest {
get() =
SafetyCenterStatus.Builder(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary")
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary"),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
.build()
@@ -169,7 +170,7 @@ class SafetyCenterManagerTest {
get() =
SafetyCenterStatus.Builder(
safetyCenterResourcesApk.getStringByName("scanning_title"),
- safetyCenterResourcesApk.getStringByName("loading_summary")
+ safetyCenterResourcesApk.getStringByName("loading_summary"),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN)
.setRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS)
@@ -179,7 +180,7 @@ class SafetyCenterManagerTest {
get() =
SafetyCenterStatus.Builder(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
.build()
@@ -190,7 +191,7 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_ok_review_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
.build()
@@ -203,7 +204,7 @@ class SafetyCenterManagerTest {
),
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_ok_review_summary"
- )
+ ),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
.build()
@@ -214,7 +215,7 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_safety_recommendation_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
.build()
@@ -225,7 +226,7 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_account_recommendation_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
.build()
@@ -236,7 +237,7 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_device_recommendation_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
.build()
@@ -247,7 +248,7 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_safety_warning_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
@@ -258,7 +259,7 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_safety_warning_title"
),
- safetyCenterTestData.getAlertString(2)
+ safetyCenterTestData.getAlertString(2),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
@@ -269,7 +270,7 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_account_warning_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
@@ -280,7 +281,7 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_account_warning_title"
),
- safetyCenterTestData.getAlertString(2)
+ safetyCenterTestData.getAlertString(2),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
@@ -291,7 +292,7 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_device_warning_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
@@ -302,21 +303,23 @@ class SafetyCenterManagerTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_device_warning_title"
),
- safetyCenterTestData.getAlertString(2)
+ safetyCenterTestData.getAlertString(2),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
private val safetyCenterEntryOrGroupRecommendation: SafetyCenterEntryOrGroup
get() =
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID),
)
private val safetyCenterEntryOrGroupCritical: SafetyCenterEntryOrGroup
get() =
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryCritical(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryCritical(SINGLE_SOURCE_ID),
)
private val safetyCenterEntryGroupMixedFromComplexConfig: SafetyCenterEntryOrGroup
@@ -330,7 +333,7 @@ class SafetyCenterManagerTest {
safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_IN_STATEFUL_ID),
SafetyCenterEntry.Builder(
SafetyCenterTestData.entryId(STATIC_IN_STATEFUL_ID),
- "OK"
+ "OK",
)
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
.setSummary("OK")
@@ -342,7 +345,7 @@ class SafetyCenterManagerTest {
.setSeverityUnspecifiedIconType(
SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON
)
- .build()
+ .build(),
)
)
.setSeverityUnspecifiedIconType(
@@ -370,8 +373,8 @@ class SafetyCenterManagerTest {
explicit = false
)
)
- .build()
- )
+ .build(),
+ ),
)
private val safetyCenterStaticEntryGroupMixedFromComplexConfig: SafetyCenterStaticEntryGroup
@@ -392,8 +395,8 @@ class SafetyCenterManagerTest {
explicit = false
)
)
- .build()
- )
+ .build(),
+ ),
)
private val safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig:
@@ -415,8 +418,8 @@ class SafetyCenterManagerTest {
explicit = false
)
)
- .build()
- )
+ .build(),
+ ),
)
private val safetyCenterDataFromConfigScanning: SafetyCenterData
@@ -425,11 +428,13 @@ class SafetyCenterManagerTest {
safetyCenterStatusUnknownScanning,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID),
+ "No info yet",
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataFromConfig: SafetyCenterData
@@ -438,11 +443,13 @@ class SafetyCenterManagerTest {
safetyCenterTestData.safetyCenterStatusUnknown,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID),
+ "No info yet",
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataUnspecified: SafetyCenterData
@@ -451,11 +458,12 @@ class SafetyCenterManagerTest {
safetyCenterStatusOk,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryUnspecified(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryUnspecified(SINGLE_SOURCE_ID),
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataOk: SafetyCenterData
@@ -464,11 +472,12 @@ class SafetyCenterManagerTest {
safetyCenterStatusOk,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID),
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataOkWithIconAction: SafetyCenterData
@@ -477,17 +486,18 @@ class SafetyCenterManagerTest {
safetyCenterStatusOk,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
safetyCenterTestData
.safetyCenterEntryOkBuilder(SINGLE_SOURCE_ID)
.setIconAction(
ICON_ACTION_TYPE_INFO,
- safetySourceTestData.createTestActivityRedirectPendingIntent()
+ safetySourceTestData.createTestActivityRedirectPendingIntent(),
)
- .build()
+ .build(),
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataUnknownScanningWithError: SafetyCenterData
@@ -496,11 +506,13 @@ class SafetyCenterManagerTest {
safetyCenterStatusUnknownScanning,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID),
+ "Couldn’t check setting",
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataUnknownReviewError: SafetyCenterData
@@ -509,11 +521,13 @@ class SafetyCenterManagerTest {
safetyCenterTestData.safetyCenterStatusUnknown,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID),
+ "Couldn’t check setting",
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataOkOneAlert: SafetyCenterData
@@ -522,20 +536,40 @@ class SafetyCenterManagerTest {
safetyCenterStatusOkOneAlert,
listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID),
+ "Ok summary", // When an issue is present, entry summary is used
)
),
- emptyList()
+ emptyList(),
)
+ private val safetyCenterDataOkOneDismissedAlert: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusOk,
+ emptyList(),
+ listOf(
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID),
+ "Ok summary", // When an issue is present, entry summary is used
+ )
+ ),
+ emptyList(),
+ )
+ .withDismissedIssuesIfAtLeastU(
+ listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID))
+ )
+
private val safetyCenterDataOkReviewCriticalEntry: SafetyCenterData
get() =
SafetyCenterData(
safetyCenterStatusOkReview,
emptyList(),
listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataOkReviewRecommendationEntry: SafetyCenterData
@@ -544,7 +578,7 @@ class SafetyCenterManagerTest {
safetyCenterStatusOkReview,
emptyList(),
listOf(safetyCenterEntryOrGroupRecommendation),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataOkReviewOneAlert: SafetyCenterData
@@ -553,7 +587,7 @@ class SafetyCenterManagerTest {
safetyCenterStatusOkReviewOneAlert,
listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)),
listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataGeneralRecommendationOneAlert: SafetyCenterData
@@ -562,11 +596,12 @@ class SafetyCenterManagerTest {
safetyCenterStatusGeneralRecommendationOneAlert,
listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID),
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataGeneralRecommendationAlertWithConfirmation: SafetyCenterData
@@ -576,15 +611,16 @@ class SafetyCenterManagerTest {
listOf(
safetyCenterTestData.safetyCenterIssueRecommendation(
SINGLE_SOURCE_ID,
- confirmationDialog = true
+ confirmationDialog = true,
)
),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID),
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataAccountRecommendationOneAlert: SafetyCenterData
@@ -593,11 +629,12 @@ class SafetyCenterManagerTest {
safetyCenterStatusAccountRecommendationOneAlert,
listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID),
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataDeviceRecommendationOneAlert: SafetyCenterData
@@ -606,11 +643,12 @@ class SafetyCenterManagerTest {
safetyCenterStatusDeviceRecommendationOneAlert,
listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID),
)
),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataGeneralCriticalOneAlert: SafetyCenterData
@@ -619,7 +657,7 @@ class SafetyCenterManagerTest {
safetyCenterStatusGeneralCriticalOneAlert,
listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataAccountCriticalOneAlert: SafetyCenterData
@@ -628,7 +666,7 @@ class SafetyCenterManagerTest {
safetyCenterStatusAccountCriticalOneAlert,
listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataDeviceCriticalOneAlert: SafetyCenterData
@@ -637,7 +675,7 @@ class SafetyCenterManagerTest {
safetyCenterStatusDeviceCriticalOneAlert,
listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataCriticalOneAlertInFlight: SafetyCenterData
@@ -647,11 +685,11 @@ class SafetyCenterManagerTest {
listOf(
safetyCenterTestData.safetyCenterIssueCritical(
SINGLE_SOURCE_ID,
- isActionInFlight = true
+ isActionInFlight = true,
)
),
listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
+ emptyList(),
)
private val safetyCenterDataOkReviewOneDismissedAlertInFlight: SafetyCenterData
@@ -660,13 +698,13 @@ class SafetyCenterManagerTest {
safetyCenterStatusOkReview,
emptyList(),
listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
+ emptyList(),
)
.withDismissedIssuesIfAtLeastU(
listOf(
safetyCenterTestData.safetyCenterIssueCritical(
SINGLE_SOURCE_ID,
- isActionInFlight = true
+ isActionInFlight = true,
)
)
)
@@ -702,17 +740,17 @@ class SafetyCenterManagerTest {
.safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID)
.setPendingIntent(null)
.setEnabled(false)
- .build()
+ .build(),
)
)
.build()
),
- safetyCenterEntryGroupMixedFromComplexConfig
+ safetyCenterEntryGroupMixedFromComplexConfig,
),
listOf(
safetyCenterStaticEntryGroupFromComplexConfig,
- safetyCenterStaticEntryGroupMixedFromComplexConfig
- )
+ safetyCenterStaticEntryGroupMixedFromComplexConfig,
+ ),
)
private val safetyCenterDataFromComplexConfigUpdated: SafetyCenterData
@@ -722,28 +760,28 @@ class SafetyCenterManagerTest {
listOf(
safetyCenterTestData.safetyCenterIssueCritical(
DYNAMIC_BAREBONE_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueCritical(
ISSUE_ONLY_BAREBONE_ID,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
DYNAMIC_DISABLED_ID,
- groupId = DYNAMIC_GROUP_ID
+ groupId = DYNAMIC_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- groupId = ISSUE_ONLY_GROUP_ID
+ groupId = ISSUE_ONLY_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
DYNAMIC_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ groupId = MIXED_STATELESS_GROUP_ID,
),
safetyCenterTestData.safetyCenterIssueInformation(
ISSUE_ONLY_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
- )
+ groupId = MIXED_STATELESS_GROUP_ID,
+ ),
),
listOf(
SafetyCenterEntryOrGroup(
@@ -765,7 +803,7 @@ class SafetyCenterManagerTest {
),
safetyCenterTestData.safetyCenterEntryUnspecified(
DYNAMIC_HIDDEN_ID,
- pendingIntent = null
+ pendingIntent = null,
),
safetyCenterTestData.safetyCenterEntryOk(
DYNAMIC_HIDDEN_WITH_SEARCH_ID
@@ -774,17 +812,17 @@ class SafetyCenterManagerTest {
.safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID)
.setPendingIntent(null)
.setEnabled(false)
- .build()
+ .build(),
)
)
.build()
),
- safetyCenterEntryGroupMixedFromComplexConfig
+ safetyCenterEntryGroupMixedFromComplexConfig,
),
listOf(
safetyCenterStaticEntryGroupFromComplexConfig,
- safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig
- )
+ safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig,
+ ),
)
@get:Rule(order = 1) val flagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
@@ -838,7 +876,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_LONG)
SafetySourceReceiver.setResponse(
Request.Rescan(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_RESCAN_BUTTON_CLICK
@@ -869,7 +907,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Rescan(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
val listener = safetyCenterTestHelper.addListener()
@@ -894,7 +932,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
val listener = safetyCenterTestHelper.addListener()
@@ -918,7 +956,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
val listener = safetyCenterTestHelper.addListener()
@@ -944,7 +982,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.informationWithIssue)
+ Response.SetData(safetySourceTestData.informationWithIssue),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -962,13 +1000,13 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.informationWithIssue)
+ Response.SetData(safetySourceTestData.informationWithIssue),
)
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- timeout = TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT,
)
}
val apiSafetySourceDataBeforeSettingFlag =
@@ -990,7 +1028,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1020,10 +1058,16 @@ class SafetyCenterManagerTest {
val expectedExplicitPendingIntent =
SafetySourceTestData.createRedirectPendingIntent(
context,
- Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName)
+ Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName),
)
val defaultEntryPendingIntent =
apiSafetyCenterData.entriesOrGroups.firstOrNull()?.entry?.pendingIntent
+ ?: apiSafetyCenterData.entriesOrGroups
+ .firstOrNull()
+ ?.entryGroup
+ ?.entries
+ ?.firstOrNull()
+ ?.pendingIntent
val defaultEntryIntentFilterEqualsToExplicitIntent =
callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") {
expectedExplicitPendingIntent.intentFilterEquals(defaultEntryPendingIntent)
@@ -1040,10 +1084,16 @@ class SafetyCenterManagerTest {
val expectedImplicitPendingIntent =
SafetySourceTestData.createRedirectPendingIntent(
context,
- Intent(ACTION_TEST_ACTIVITY_EXPORTED)
+ Intent(ACTION_TEST_ACTIVITY_EXPORTED),
)
val defaultEntryPendingIntent =
apiSafetyCenterData.entriesOrGroups.firstOrNull()?.entry?.pendingIntent
+ ?: apiSafetyCenterData.entriesOrGroups
+ .firstOrNull()
+ ?.entryGroup
+ ?.entries
+ ?.firstOrNull()
+ ?.pendingIntent
val defaultEntryIntentFilterEqualsToImplicitIntent =
callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") {
expectedImplicitPendingIntent.intentFilterEquals(defaultEntryPendingIntent)
@@ -1060,7 +1110,7 @@ class SafetyCenterManagerTest {
val expectedImplicitPendingIntent =
SafetySourceTestData.createRedirectPendingIntent(
context,
- Intent(ACTION_TEST_ACTIVITY_EXPORTED)
+ Intent(ACTION_TEST_ACTIVITY_EXPORTED),
)
val staticEntryPendingIntent =
apiSafetyCenterData.staticEntryGroups
@@ -1086,7 +1136,7 @@ class SafetyCenterManagerTest {
val expectedExplicitPendingIntent =
SafetySourceTestData.createRedirectPendingIntent(
context,
- Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName)
+ Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName),
)
val staticEntryPendingIntent =
apiSafetyCenterData.staticEntryGroups
@@ -1146,7 +1196,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.informationWithIconAction
+ safetySourceTestData.informationWithIconAction,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1160,7 +1210,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.informationWithIssueWithAttributionTitle
+ safetySourceTestData.informationWithIssueWithAttributionTitle,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1189,7 +1239,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceNoGroupTitleConfig)
safetyCenterTestHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1200,11 +1250,11 @@ class SafetyCenterManagerTest {
listOf(
safetyCenterTestData.safetyCenterIssueRecommendation(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- attributionTitle = null
+ attributionTitle = null,
)
),
emptyList(),
- emptyList()
+ emptyList(),
)
assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData)
}
@@ -1227,7 +1277,7 @@ class SafetyCenterManagerTest {
val previousApiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1241,7 +1291,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterManager.reportSafetySourceErrorWithPermission(
SINGLE_SOURCE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1264,7 +1314,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithInformationIssue
+ safetySourceTestData.criticalWithInformationIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1277,7 +1327,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1291,7 +1341,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationWithIssueWithActionConfirmation
+ safetySourceTestData.recommendationWithIssueWithActionConfirmation,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1305,7 +1355,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationWithAccountIssue
+ safetySourceTestData.recommendationWithAccountIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1318,7 +1368,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationWithDeviceIssue
+ safetySourceTestData.recommendationWithDeviceIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1331,7 +1381,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1344,7 +1394,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingAccountIssue
+ safetySourceTestData.criticalWithResolvingAccountIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1357,7 +1407,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingDeviceIssue
+ safetySourceTestData.criticalWithResolvingDeviceIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1376,7 +1426,7 @@ class SafetyCenterManagerTest {
.defaultRecommendationIssueBuilder()
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
.build()
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1385,7 +1435,7 @@ class SafetyCenterManagerTest {
.isEqualTo(
safetyCenterTestData.safetyCenterStatusOneAlert(
"overall_severity_level_data_recommendation_title",
- OVERALL_SEVERITY_LEVEL_RECOMMENDATION
+ OVERALL_SEVERITY_LEVEL_RECOMMENDATION,
)
)
}
@@ -1401,7 +1451,7 @@ class SafetyCenterManagerTest {
.defaultCriticalResolvingIssueBuilder()
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
.build()
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1410,7 +1460,7 @@ class SafetyCenterManagerTest {
.isEqualTo(
safetyCenterTestData.safetyCenterStatusOneAlert(
"overall_severity_level_critical_data_warning_title",
- OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING
+ OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING,
)
)
}
@@ -1426,7 +1476,7 @@ class SafetyCenterManagerTest {
.defaultRecommendationIssueBuilder()
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS)
.build()
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1435,7 +1485,7 @@ class SafetyCenterManagerTest {
.isEqualTo(
safetyCenterTestData.safetyCenterStatusOneAlert(
"overall_severity_level_passwords_recommendation_title",
- OVERALL_SEVERITY_LEVEL_RECOMMENDATION
+ OVERALL_SEVERITY_LEVEL_RECOMMENDATION,
)
)
}
@@ -1451,7 +1501,7 @@ class SafetyCenterManagerTest {
.defaultCriticalResolvingIssueBuilder()
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS)
.build()
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1460,7 +1510,7 @@ class SafetyCenterManagerTest {
.isEqualTo(
safetyCenterTestData.safetyCenterStatusOneAlert(
"overall_severity_level_critical_passwords_warning_title",
- OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING
+ OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING,
)
)
}
@@ -1476,7 +1526,7 @@ class SafetyCenterManagerTest {
.defaultRecommendationIssueBuilder()
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY)
.build()
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1485,7 +1535,7 @@ class SafetyCenterManagerTest {
.isEqualTo(
safetyCenterTestData.safetyCenterStatusOneAlert(
"overall_severity_level_personal_recommendation_title",
- OVERALL_SEVERITY_LEVEL_RECOMMENDATION
+ OVERALL_SEVERITY_LEVEL_RECOMMENDATION,
)
)
}
@@ -1501,7 +1551,7 @@ class SafetyCenterManagerTest {
.defaultCriticalResolvingIssueBuilder()
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY)
.build()
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1510,7 +1560,7 @@ class SafetyCenterManagerTest {
.isEqualTo(
safetyCenterTestData.safetyCenterStatusOneAlert(
"overall_severity_level_critical_personal_warning_title",
- OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING
+ OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING,
)
)
}
@@ -1526,7 +1576,7 @@ class SafetyCenterManagerTest {
.defaultInformationIssueBuilder()
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP)
.build()
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1562,7 +1612,7 @@ class SafetyCenterManagerTest {
.defaultInformationIssueBuilder("id_5")
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP)
.build(),
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1582,7 +1632,7 @@ class SafetyCenterManagerTest {
.defaultInformationIssueBuilder()
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
.build()
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1618,7 +1668,7 @@ class SafetyCenterManagerTest {
.defaultInformationIssueBuilder("id_5")
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL)
.build(),
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1637,8 +1687,8 @@ class SafetyCenterManagerTest {
safetySourceTestData
.defaultInformationIssueBuilder()
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL)
- .build(),
- )
+ .build()
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1648,7 +1698,7 @@ class SafetyCenterManagerTest {
safetyCenterTestData.safetyCenterStatusNAlerts(
"overall_severity_level_ok_title",
OVERALL_SEVERITY_LEVEL_OK,
- numAlerts = 1
+ numAlerts = 1,
)
)
}
@@ -1676,7 +1726,7 @@ class SafetyCenterManagerTest {
.defaultInformationIssueBuilder("id_4")
.setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
.build(),
- )
+ ),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1686,7 +1736,7 @@ class SafetyCenterManagerTest {
safetyCenterTestData.safetyCenterStatusNAlerts(
"overall_severity_level_ok_title",
OVERALL_SEVERITY_LEVEL_OK,
- numAlerts = 2
+ numAlerts = 2,
)
)
}
@@ -1701,7 +1751,7 @@ class SafetyCenterManagerTest {
assertThat(
SafetyCenterBundles.getStaticEntryId(
apiSafetyCenterData,
- apiSafetyCenterData.staticEntryGroups[0].staticEntries[0]
+ apiSafetyCenterData.staticEntryGroups[0].staticEntries[0],
)
)
.isEqualTo(
@@ -1713,7 +1763,7 @@ class SafetyCenterManagerTest {
assertThat(
SafetyCenterBundles.getStaticEntryId(
apiSafetyCenterData,
- apiSafetyCenterData.staticEntryGroups[1].staticEntries[0]
+ apiSafetyCenterData.staticEntryGroups[1].staticEntries[0],
)
)
.isEqualTo(
@@ -1729,7 +1779,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingDeviceIssueAndRecommendationIssue
+ safetySourceTestData.criticalWithResolvingDeviceIssueAndRecommendationIssue,
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1742,11 +1792,11 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.recommendationWithAccountIssue
+ safetySourceTestData.recommendationWithAccountIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_3,
- safetySourceTestData.criticalWithResolvingDeviceIssue
+ safetySourceTestData.criticalWithResolvingDeviceIssue,
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1759,11 +1809,11 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_3,
- safetySourceTestData.criticalWithResolvingDeviceIssue
+ safetySourceTestData.criticalWithResolvingDeviceIssue,
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -1782,14 +1832,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
@@ -1798,7 +1848,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
)
)
}
@@ -1814,14 +1864,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1 and source group MULTIPLE_SOURCES_GROUP_ID_2
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1844,14 +1894,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1 and source group MULTIPLE_SOURCES_GROUP_ID_1
safetyCenterTestHelper.setData(
SOURCE_ID_2,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1874,14 +1924,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_3 and source group MULTIPLE_SOURCES_GROUP_ID_2
safetyCenterTestHelper.setData(
SOURCE_ID_6,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -1902,14 +1952,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("different")
- )
+ ),
)
val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
@@ -1918,12 +1968,12 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
),
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_5,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
- )
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
+ ),
)
.inOrder()
}
@@ -1939,14 +1989,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_3
safetyCenterTestHelper.setData(
SOURCE_ID_6,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
@@ -1955,12 +2005,12 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
),
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_6,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
- )
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
+ ),
)
.inOrder()
}
@@ -1976,21 +2026,21 @@ class SafetyCenterManagerTest {
SOURCE_ID_4,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_3
safetyCenterTestHelper.setData(
SOURCE_ID_6,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_3
safetyCenterTestHelper.setData(
SOURCE_ID_7,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
@@ -1999,7 +2049,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_4,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
)
)
}
@@ -2015,14 +2065,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_2,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
@@ -2031,7 +2081,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_5,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
)
)
}
@@ -2047,49 +2097,49 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("A")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_2,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("A")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_2
safetyCenterTestHelper.setData(
SOURCE_ID_3,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("B")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_3
safetyCenterTestHelper.setData(
SOURCE_ID_4,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("B")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("A")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_3
safetyCenterTestHelper.setData(
SOURCE_ID_6,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("B")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_3
safetyCenterTestHelper.setData(
SOURCE_ID_7,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("B")
- )
+ ),
)
val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
@@ -2098,16 +2148,16 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_5,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
SOURCE_ID_3,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
),
safetyCenterTestData.safetyCenterIssueRecommendation(
SOURCE_ID_4,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
- )
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
+ ),
)
.inOrder()
}
@@ -2123,14 +2173,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)
@@ -2148,7 +2198,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
)
)
}
@@ -2164,14 +2214,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SOURCE_ID_5, RECOMMENDATION_ISSUE_ID)
@@ -2185,7 +2235,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
)
)
assertThat(apiSafetyCenterDismissedIssues).isEmpty()
@@ -2202,14 +2252,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)
@@ -2224,7 +2274,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
)
)
}
@@ -2240,14 +2290,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SOURCE_ID_5, CRITICAL_ISSUE_ID)
@@ -2262,7 +2312,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
)
)
}
@@ -2282,14 +2332,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)
@@ -2303,7 +2353,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_5,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
)
)
waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) {
@@ -2314,7 +2364,7 @@ class SafetyCenterManagerTest {
.contains(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_5,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
)
)
hasResurfaced
@@ -2329,13 +2379,13 @@ class SafetyCenterManagerTest {
mapOf(
SEVERITY_LEVEL_INFORMATION to 0L,
SEVERITY_LEVEL_RECOMMENDATION to 99L,
- SEVERITY_LEVEL_CRITICAL_WARNING to 0L
+ SEVERITY_LEVEL_CRITICAL_WARNING to 0L,
)
SafetyCenterFlags.resurfaceIssueDelays =
mapOf(
SEVERITY_LEVEL_INFORMATION to Duration.ZERO,
SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY,
- SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO
+ SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO,
)
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -2345,14 +2395,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)
@@ -2366,7 +2416,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueRecommendation(
SOURCE_ID_5,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
)
)
waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) {
@@ -2377,7 +2427,7 @@ class SafetyCenterManagerTest {
.contains(
safetyCenterTestData.safetyCenterIssueRecommendation(
SOURCE_ID_5,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
)
)
hasResurfaced
@@ -2391,13 +2441,13 @@ class SafetyCenterManagerTest {
mapOf(
SEVERITY_LEVEL_INFORMATION to 0L,
SEVERITY_LEVEL_RECOMMENDATION to 99L,
- SEVERITY_LEVEL_CRITICAL_WARNING to 99L
+ SEVERITY_LEVEL_CRITICAL_WARNING to 99L,
)
SafetyCenterFlags.resurfaceIssueDelays =
mapOf(
SEVERITY_LEVEL_INFORMATION to Duration.ZERO,
SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY,
- SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY.multipliedBy(100)
+ SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY.multipliedBy(100),
)
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -2407,14 +2457,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)
@@ -2427,7 +2477,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
)
)
assertFailsWith(TimeoutCancellationException::class) {
@@ -2439,7 +2489,7 @@ class SafetyCenterManagerTest {
.contains(
safetyCenterTestData.safetyCenterIssueRecommendation(
SOURCE_ID_5,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
)
)
hasResurfaced
@@ -2454,13 +2504,13 @@ class SafetyCenterManagerTest {
mapOf(
SEVERITY_LEVEL_INFORMATION to 0L,
SEVERITY_LEVEL_RECOMMENDATION to 99L,
- SEVERITY_LEVEL_CRITICAL_WARNING to 99L
+ SEVERITY_LEVEL_CRITICAL_WARNING to 99L,
)
SafetyCenterFlags.resurfaceIssueDelays =
mapOf(
SEVERITY_LEVEL_INFORMATION to Duration.ZERO,
SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY.multipliedBy(100),
- SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY
+ SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY,
)
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -2470,14 +2520,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)
@@ -2490,7 +2540,7 @@ class SafetyCenterManagerTest {
.containsExactly(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
)
)
waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) {
@@ -2501,7 +2551,7 @@ class SafetyCenterManagerTest {
.contains(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_1,
- groupId = MULTIPLE_SOURCES_GROUP_ID_1
+ groupId = MULTIPLE_SOURCES_GROUP_ID_1,
)
)
hasResurfaced
@@ -2520,14 +2570,14 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
// Belongs to DEDUPLICATION_GROUP_1
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
val listener = safetyCenterTestHelper.addListener()
@@ -2545,7 +2595,7 @@ class SafetyCenterManagerTest {
.contains(
safetyCenterTestData.safetyCenterIssueCritical(
SOURCE_ID_5,
- groupId = MULTIPLE_SOURCES_GROUP_ID_2
+ groupId = MULTIPLE_SOURCES_GROUP_ID_2,
)
)
hasResurfaced
@@ -2571,7 +2621,7 @@ class SafetyCenterManagerTest {
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
.build()
)
- .build()
+ .build(),
)
val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
@@ -2584,32 +2634,32 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexConfig)
safetyCenterTestHelper.setData(
DYNAMIC_BAREBONE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterTestHelper.setData(
DYNAMIC_DISABLED_ID,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(DYNAMIC_HIDDEN_ID, safetySourceTestData.unspecified)
safetyCenterTestHelper.setData(
DYNAMIC_HIDDEN_WITH_SEARCH_ID,
- safetySourceTestData.information
+ safetySourceTestData.information,
)
safetyCenterTestHelper.setData(
ISSUE_ONLY_BAREBONE_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue),
)
safetyCenterTestHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue),
)
safetyCenterTestHelper.setData(
DYNAMIC_IN_STATELESS_ID,
- safetySourceTestData.unspecifiedWithIssue
+ safetySourceTestData.unspecifiedWithIssue,
)
safetyCenterTestHelper.setData(
ISSUE_ONLY_IN_STATELESS_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -2623,50 +2673,50 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.summaryTestConfig)
safetyCenterManager.reportSafetySourceErrorWithPermission(
SOURCE_ID_1,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified"
- )
+ entrySummary = "unspecified",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_3,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information without issues"
- )
+ entrySummary = "information without issues",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_4,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
entrySummary = "information with issue",
- withIssue = true
- )
+ withIssue = true,
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_5,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_RECOMMENDATION,
- entrySummary = "recommendation"
- )
+ entrySummary = "recommendation",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_6,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_CRITICAL_WARNING,
- entrySummary = "critical 1"
- )
+ entrySummary = "critical 1",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_7,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_CRITICAL_WARNING,
- entrySummary = "critical 2"
- )
+ entrySummary = "critical 2",
+ ),
)
// STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
@@ -2682,43 +2732,43 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.summaryTestConfig)
safetyCenterManager.reportSafetySourceErrorWithPermission(
SOURCE_ID_1,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified"
- )
+ entrySummary = "unspecified",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_3,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information without issues"
- )
+ entrySummary = "information without issues",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_4,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
entrySummary = "information with issue",
- withIssue = true
- )
+ withIssue = true,
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_5,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_RECOMMENDATION,
- entrySummary = "recommendation 1"
- )
+ entrySummary = "recommendation 1",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_6,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_RECOMMENDATION,
- entrySummary = "recommendation 2"
- )
+ entrySummary = "recommendation 2",
+ ),
)
// SOURCE_ID_7 leave as an UNKNOWN dynamic entry
// STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
@@ -2737,52 +2787,52 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 1"
- )
+ entrySummary = "unspecified 1",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 2"
- )
+ entrySummary = "unspecified 2",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_3,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information without issues 1"
- )
+ entrySummary = "information without issues 1",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_4,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
entrySummary = "information with issue 1",
- withIssue = true
- )
+ withIssue = true,
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_5,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
entrySummary = "information with issue 2",
- withIssue = true
- )
+ withIssue = true,
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_6,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information without issues 2"
- )
+ entrySummary = "information without issues 2",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_7,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 3"
- )
+ entrySummary = "unspecified 3",
+ ),
)
// STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
@@ -2800,50 +2850,50 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 1"
- )
+ entrySummary = "unspecified 1",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 2"
- )
+ entrySummary = "unspecified 2",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_3,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information without issues 1"
- )
+ entrySummary = "information without issues 1",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_4,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information without issues 2"
- )
+ entrySummary = "information without issues 2",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_5,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 3"
- )
+ entrySummary = "unspecified 3",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_6,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information without issues 3"
- )
+ entrySummary = "information without issues 3",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_7,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 4"
- )
+ entrySummary = "unspecified 4",
+ ),
)
// STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
@@ -2861,51 +2911,51 @@ class SafetyCenterManagerTest {
SOURCE_ID_1,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 1"
- )
+ entrySummary = "unspecified 1",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 2"
- )
+ entrySummary = "unspecified 2",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_3,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 3"
- )
+ entrySummary = "unspecified 3",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_4,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 4"
- )
+ entrySummary = "unspecified 4",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_5,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
entrySummary = "unspecified with issue",
- withIssue = true
- )
+ withIssue = true,
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_6,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 6"
- )
+ entrySummary = "unspecified 6",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_7,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified 7"
- )
+ entrySummary = "unspecified 7",
+ ),
)
// STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
@@ -2921,34 +2971,34 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.summaryTestConfig)
safetyCenterManager.reportSafetySourceErrorWithPermission(
SOURCE_ID_1,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
safetyCenterManager.reportSafetySourceErrorWithPermission(
SOURCE_ID_2,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
safetyCenterTestHelper.setData(
SOURCE_ID_3,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
entrySummary = "information with issue",
- withIssue = true
- )
+ withIssue = true,
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_4,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified"
- )
+ entrySummary = "unspecified",
+ ),
)
// SOURCE_ID_5 leave as an UNKNOWN dynamic entry
safetyCenterTestHelper.setData(
SOURCE_ID_6,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information"
- )
+ entrySummary = "information",
+ ),
)
// SOURCE_ID_7 leave as an UNKNOWN dynamic entry
// STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
@@ -2965,7 +3015,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.summaryTestConfig)
safetyCenterManager.reportSafetySourceErrorWithPermission(
SOURCE_ID_1,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
// SOURCE_ID_2 leave as an UNKNOWN dynamic entry
safetyCenterTestHelper.setData(
@@ -2973,15 +3023,15 @@ class SafetyCenterManagerTest {
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
entrySummary = "information with issue",
- withIssue = true
- )
+ withIssue = true,
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_4,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified"
- )
+ entrySummary = "unspecified",
+ ),
)
// SOURCE_ID_5 leave as an UNKNOWN dynamic entry
// SOURCE_ID_6 leave as an UNKNOWN dynamic entry
@@ -2989,8 +3039,8 @@ class SafetyCenterManagerTest {
SOURCE_ID_7,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information"
- )
+ entrySummary = "information",
+ ),
)
// STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
@@ -3011,22 +3061,22 @@ class SafetyCenterManagerTest {
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
entrySummary = "information with issue",
- withIssue = true
- )
+ withIssue = true,
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_4,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
- entrySummary = "unspecified"
- )
+ entrySummary = "unspecified",
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_5,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_INFORMATION,
- entrySummary = "information"
- )
+ entrySummary = "information",
+ ),
)
// SOURCE_ID_6 leave as an UNKNOWN dynamic entry
safetyCenterTestHelper.setData(
@@ -3034,8 +3084,8 @@ class SafetyCenterManagerTest {
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
entrySummary = "unspecified with issue",
- withIssue = true
- )
+ withIssue = true,
+ ),
)
// STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
@@ -3083,7 +3133,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(
DYNAMIC_DISABLED_ID,
- safetySourceTestData.informationWithIssue
+ safetySourceTestData.informationWithIssue,
)
val informationWithIssueGroup =
@@ -3115,15 +3165,17 @@ class SafetyCenterManagerTest {
safetyCenterTestData.safetyCenterStatusUnknown,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
safetyCenterTestData
.safetyCenterEntryDefaultBuilder(SINGLE_SOURCE_ID)
.setPendingIntent(null)
.setEnabled(false)
- .build()
+ .build(),
+ "No info yet",
)
),
- emptyList()
+ emptyList(),
)
assertThat(safetyCenterData).isEqualTo(expectedSafetyCenterData)
}
@@ -3133,7 +3185,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceInvalidIntentConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.informationWithNullIntent
+ safetySourceTestData.informationWithNullIntent,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -3143,16 +3195,17 @@ class SafetyCenterManagerTest {
safetyCenterStatusOk,
emptyList(),
listOf(
- SafetyCenterEntryOrGroup(
+ safetyCenterTestData.singletonSafetyCenterEntryOrGroup(
+ SINGLE_SOURCE_GROUP_ID,
safetyCenterTestData
.safetyCenterEntryOkBuilder(SINGLE_SOURCE_ID)
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
.setPendingIntent(null)
.setEnabled(false)
- .build()
+ .build(),
)
),
- emptyList()
+ emptyList(),
)
assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData)
}
@@ -3165,7 +3218,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information)
safetyCenterTestHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -3184,7 +3237,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information)
safetyCenterTestHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -3200,12 +3253,12 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.entryGroupWithIssueOnlyConfig)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information)
safetyCenterTestHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -3223,7 +3276,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information)
safetyCenterTestHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue),
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(ISSUE_ONLY_ALL_OPTIONAL_ID, RECOMMENDATION_ISSUE_ID)
@@ -3245,7 +3298,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information)
safetyCenterTestHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue)
+ SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue),
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -3287,7 +3340,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
@@ -3313,7 +3366,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
@@ -3334,7 +3387,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID)
@@ -3350,7 +3403,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val safetyCenterDataAfterSourcePushesDismissedIssueAgain =
@@ -3364,14 +3417,14 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- issueTypeId = "some_other_issue_type_id"
+ issueTypeId = "some_other_issue_type_id",
)
)
@@ -3388,11 +3441,11 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationDismissPendingIntentIssue
+ safetySourceTestData.recommendationDismissPendingIntentIssue,
)
SafetySourceReceiver.setResponse(
Request.DismissIssue(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
val listener = safetyCenterTestHelper.addListener()
@@ -3446,15 +3499,11 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, INFORMATION_ISSUE_ID)
)
- val expectedSafetyCenterData =
- safetyCenterDataOk.withDismissedIssuesIfAtLeastU(
- listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID))
- )
assertFailsWith(TimeoutCancellationException::class) {
waitForWithTimeout(timeout = TIMEOUT_SHORT) {
val hasResurfaced =
safetyCenterManager.getSafetyCenterDataWithPermission() !=
- expectedSafetyCenterData
+ safetyCenterDataOkOneDismissedAlert
hasResurfaced
}
}
@@ -3466,13 +3515,13 @@ class SafetyCenterManagerTest {
mapOf(
SEVERITY_LEVEL_INFORMATION to 0L,
SEVERITY_LEVEL_RECOMMENDATION to 99L,
- SEVERITY_LEVEL_CRITICAL_WARNING to 99L
+ SEVERITY_LEVEL_CRITICAL_WARNING to 99L,
)
SafetyCenterFlags.resurfaceIssueDelays =
mapOf(
SEVERITY_LEVEL_INFORMATION to Duration.ZERO,
SEVERITY_LEVEL_RECOMMENDATION to Duration.ZERO,
- SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO
+ SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO,
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue)
@@ -3483,15 +3532,11 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, INFORMATION_ISSUE_ID)
)
- val expectedSafetyCenterData =
- safetyCenterDataOk.withDismissedIssuesIfAtLeastU(
- listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID))
- )
assertFailsWith(TimeoutCancellationException::class) {
waitForWithTimeout(timeout = TIMEOUT_SHORT) {
val hasResurfaced =
safetyCenterManager.getSafetyCenterDataWithPermission() !=
- expectedSafetyCenterData
+ safetyCenterDataOkOneDismissedAlert
hasResurfaced
}
}
@@ -3503,18 +3548,18 @@ class SafetyCenterManagerTest {
mapOf(
SEVERITY_LEVEL_INFORMATION to 0L,
SEVERITY_LEVEL_RECOMMENDATION to 0L,
- SEVERITY_LEVEL_CRITICAL_WARNING to 2L
+ SEVERITY_LEVEL_CRITICAL_WARNING to 2L,
)
SafetyCenterFlags.resurfaceIssueDelays =
mapOf(
SEVERITY_LEVEL_INFORMATION to RESURFACE_DELAY,
SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY,
- SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO
+ SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO,
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingDeviceIssue
+ safetySourceTestData.criticalWithResolvingDeviceIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
checkState(apiSafetyCenterData == safetyCenterDataDeviceCriticalOneAlert)
@@ -3559,18 +3604,18 @@ class SafetyCenterManagerTest {
mapOf(
SEVERITY_LEVEL_INFORMATION to 0L,
SEVERITY_LEVEL_RECOMMENDATION to 99L,
- SEVERITY_LEVEL_CRITICAL_WARNING to 0L
+ SEVERITY_LEVEL_CRITICAL_WARNING to 0L,
)
SafetyCenterFlags.resurfaceIssueDelays =
mapOf(
SEVERITY_LEVEL_INFORMATION to Duration.ZERO,
SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY,
- SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO
+ SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO,
)
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationWithDeviceIssue
+ safetySourceTestData.recommendationWithDeviceIssue,
)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
checkState(apiSafetyCenterData == safetyCenterDataDeviceRecommendationOneAlert)
@@ -3598,12 +3643,12 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
@@ -3611,8 +3656,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData()
@@ -3627,7 +3672,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
SafetySourceReceiver.setResponse(Request.ResolveAction(SINGLE_SOURCE_ID), Response.Error)
@@ -3637,8 +3682,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData()
@@ -3662,7 +3707,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
@@ -3671,8 +3716,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData()
@@ -3696,7 +3741,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
@@ -3704,8 +3749,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
listener.receiveSafetyCenterData()
listener.receiveSafetyCenterData()
@@ -3713,7 +3758,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.resolveActionTimeout = TIMEOUT_LONG
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
@@ -3721,8 +3766,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData()
@@ -3737,11 +3782,11 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
val issueId = SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID)
val listener = safetyCenterTestHelper.addListener()
@@ -3754,8 +3799,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
if (SdkLevel.isAtLeastU()) {
@@ -3775,12 +3820,12 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
val listener = safetyCenterTestHelper.addListener()
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
assertFailsWith(IllegalArgumentException::class) {
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
@@ -3788,9 +3833,9 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID + "invalid",
- CRITICAL_ISSUE_ACTION_ID
+ CRITICAL_ISSUE_ACTION_ID,
),
- TIMEOUT_SHORT
+ TIMEOUT_SHORT,
)
}
@@ -3799,8 +3844,8 @@ class SafetyCenterManagerTest {
SafetyCenterTestData.issueActionId(
SINGLE_SOURCE_ID,
CRITICAL_ISSUE_ID,
- CRITICAL_ISSUE_ACTION_ID
- )
+ CRITICAL_ISSUE_ACTION_ID,
+ ),
)
val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData()
@@ -3891,7 +3936,7 @@ class SafetyCenterManagerTest {
safetyCenterManager.reportSafetySourceErrorWithPermission(
SINGLE_SOURCE_ID,
- SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED)
+ SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED),
)
val lastUpdated = dumpLastUpdated()
diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt
index 1678ccced..ce75b00f5 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt
+++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt
@@ -116,7 +116,7 @@ class SafetyCenterNotificationTest {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.recommendationWithAccountIssue
+ safetySourceTestData.recommendationWithAccountIssue,
)
TestNotificationListener.waitForZeroNotifications()
@@ -128,7 +128,7 @@ class SafetyCenterNotificationTest {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.recommendationWithAccountIssue
+ safetySourceTestData.recommendationWithAccountIssue,
)
TestNotificationListener.waitForZeroNotifications()
@@ -140,7 +140,7 @@ class SafetyCenterNotificationTest {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.recommendationWithAccountIssue
+ safetySourceTestData.recommendationWithAccountIssue,
)
TestNotificationListener.waitForZeroNotifications()
@@ -401,7 +401,7 @@ class SafetyCenterNotificationTest {
SafetySourceIssue.Action.Builder(
"default_action",
"Default action",
- safetySourceTestData.createTestActivityRedirectPendingIntent()
+ safetySourceTestData.createTestActivityRedirectPendingIntent(),
)
.build()
)
@@ -440,7 +440,7 @@ class SafetyCenterNotificationTest {
SafetySourceIssue.Action.Builder(
"default_action",
"Default action",
- safetySourceTestData.createTestActivityRedirectPendingIntent()
+ safetySourceTestData.createTestActivityRedirectPendingIntent(),
)
.build()
)
@@ -497,7 +497,7 @@ class SafetyCenterNotificationTest {
"New action",
safetySourceTestData.createTestActivityRedirectPendingIntent(
identifier = "new_action"
- )
+ ),
)
.build()
)
@@ -697,13 +697,13 @@ class SafetyCenterNotificationTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
TestNotificationListener.waitForNotificationsMatching(
@@ -728,7 +728,7 @@ class SafetyCenterNotificationTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
val notificationWithChannel =
@@ -745,7 +745,7 @@ class SafetyCenterNotificationTest {
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
TestNotificationListener.waitForNotificationsMatching(
@@ -762,7 +762,7 @@ class SafetyCenterNotificationTest {
fun setSafetySourceData_withInformationIssue_lowImportanceBlockableNotification() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.informationWithIssue
+ safetySourceTestData.informationWithIssue,
)
TestNotificationListener.waitForNotificationsMatching(
@@ -781,7 +781,7 @@ class SafetyCenterNotificationTest {
fun setSafetySourceData_withRecommendationIssue_defaultImportanceUnblockableNotification() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.recommendationWithAccountIssue
+ safetySourceTestData.recommendationWithAccountIssue,
)
TestNotificationListener.waitForNotificationsMatching(
@@ -800,7 +800,7 @@ class SafetyCenterNotificationTest {
fun setSafetySourceData_withCriticalIssue_highImportanceUnblockableNotification() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.criticalWithResolvingDeviceIssue(sourceId = uniqueSafetySourceId)
+ safetySourceTestData.criticalWithResolvingDeviceIssue(sourceId = uniqueSafetySourceId),
)
TestNotificationListener.waitForNotificationsMatching(
@@ -819,7 +819,7 @@ class SafetyCenterNotificationTest {
fun dismissSafetyCenterIssue_dismissesNotification() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.recommendationWithAccountIssue
+ safetySourceTestData.recommendationWithAccountIssue,
)
TestNotificationListener.waitForSingleNotificationMatching(
@@ -832,7 +832,7 @@ class SafetyCenterNotificationTest {
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(
uniqueSafetySourceId,
- SafetySourceTestData.RECOMMENDATION_ISSUE_ID
+ SafetySourceTestData.RECOMMENDATION_ISSUE_ID,
)
)
@@ -843,7 +843,7 @@ class SafetyCenterNotificationTest {
fun dismissingNotification_doesNotUpdateSafetyCenterData() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId)
+ safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId),
)
// Add the listener after setting the initial data so that we don't need to consume/receive
// an update for that
@@ -876,13 +876,13 @@ class SafetyCenterNotificationTest {
SOURCE_ID_1,
SafetySourceTestData.issuesOnly(
safetySourceTestData.criticalIssueWithDeduplicationId("same")
- )
+ ),
)
safetyCenterTestHelper.setData(
SOURCE_ID_5,
SafetySourceTestData.issuesOnly(
safetySourceTestData.recommendationIssueWithDeduplicationId("same")
- )
+ ),
)
val notificationWithChannel =
@@ -926,7 +926,7 @@ class SafetyCenterNotificationTest {
fun sendActionPendingIntent_successful_updatesListener() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId)
+ safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId),
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -940,7 +940,7 @@ class SafetyCenterNotificationTest {
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
Request.ResolveAction(uniqueSafetySourceId),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
val listener = safetyCenterTestHelper.addListener()
@@ -958,7 +958,7 @@ class SafetyCenterNotificationTest {
fun sendActionPendingIntent_successfulNoSuccessMessage_removesNotification() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId)
+ safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId),
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -972,7 +972,7 @@ class SafetyCenterNotificationTest {
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
Request.ResolveAction(uniqueSafetySourceId),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
sendActionPendingIntentAndWaitWithPermission(action)
@@ -986,7 +986,7 @@ class SafetyCenterNotificationTest {
uniqueSafetySourceId,
safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage(
sourceId = uniqueSafetySourceId
- )
+ ),
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -1000,7 +1000,7 @@ class SafetyCenterNotificationTest {
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
Request.ResolveAction(uniqueSafetySourceId),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
sendActionPendingIntentAndWaitWithPermission(action)
@@ -1017,7 +1017,7 @@ class SafetyCenterNotificationTest {
uniqueSafetySourceId,
safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage(
sourceId = uniqueSafetySourceId
- )
+ ),
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -1031,7 +1031,7 @@ class SafetyCenterNotificationTest {
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
Request.ResolveAction(uniqueSafetySourceId),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
sendActionPendingIntentAndWaitWithPermission(action)
@@ -1049,7 +1049,7 @@ class SafetyCenterNotificationTest {
uniqueSafetySourceId,
safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage(
sourceId = uniqueSafetySourceId
- )
+ ),
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -1063,7 +1063,7 @@ class SafetyCenterNotificationTest {
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
Request.ResolveAction(uniqueSafetySourceId),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
SafetyCenterFlags.notificationsEnabled = false
@@ -1078,7 +1078,7 @@ class SafetyCenterNotificationTest {
uniqueSafetySourceId,
safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage(
sourceId = uniqueSafetySourceId
- )
+ ),
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -1095,8 +1095,8 @@ class SafetyCenterNotificationTest {
Response.SetData(
safetySourceTestData.information,
overrideSafetyEvent =
- SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
- )
+ SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build(),
+ ),
)
sendActionPendingIntentAndWaitWithPermission(action)
@@ -1118,8 +1118,8 @@ class SafetyCenterNotificationTest {
"Solve now!",
safetySourceTestData.resolvingActionPendingIntent(
sourceId = uniqueSafetySourceId,
- sourceIssueActionId = "notification_action_id"
- )
+ sourceIssueActionId = "notification_action_id",
+ ),
)
.setWillResolve(true)
.setSuccessMessage("Solved via notification action :)")
@@ -1140,8 +1140,8 @@ class SafetyCenterNotificationTest {
"Default action",
safetySourceTestData.resolvingActionPendingIntent(
sourceId = uniqueSafetySourceId,
- sourceIssueActionId = "issue_action_id"
- )
+ sourceIssueActionId = "issue_action_id",
+ ),
)
.setWillResolve(true)
.setSuccessMessage("Solved via issue action :(")
@@ -1168,7 +1168,7 @@ class SafetyCenterNotificationTest {
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
Request.ResolveAction(uniqueSafetySourceId),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
sendActionPendingIntentAndWaitWithPermission(action)
@@ -1185,7 +1185,7 @@ class SafetyCenterNotificationTest {
// to resolve that action successfully.
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId)
+ safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId),
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -1199,7 +1199,7 @@ class SafetyCenterNotificationTest {
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
Request.ResolveAction(uniqueSafetySourceId),
- Response.Error
+ Response.Error,
)
val listener = safetyCenterTestHelper.addListener()
@@ -1224,7 +1224,7 @@ class SafetyCenterNotificationTest {
fun sendContentPendingIntent_singleIssue_opensSafetyCenterWithIssueVisible() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.recommendationWithDeviceIssue
+ safetySourceTestData.recommendationWithDeviceIssue,
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -1245,11 +1245,11 @@ class SafetyCenterNotificationTest {
setFlagsForImmediateNotifications(SOURCE_ID_1)
safetyCenterTestHelper.setData(
SOURCE_ID_1,
- safetySourceTestData.recommendationWithDeviceIssue
+ safetySourceTestData.recommendationWithDeviceIssue,
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = SOURCE_ID_2)
+ safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = SOURCE_ID_2),
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -1269,7 +1269,7 @@ class SafetyCenterNotificationTest {
fun whenGreenIssue_notificationHasAutoCancel() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.informationWithIssue
+ safetySourceTestData.informationWithIssue,
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -1286,7 +1286,7 @@ class SafetyCenterNotificationTest {
fun whenNotGreenIssue_notificationDoesntHaveAutoCancel() {
safetyCenterTestHelper.setData(
uniqueSafetySourceId,
- safetySourceTestData.recommendationWithDeviceIssue
+ safetySourceTestData.recommendationWithDeviceIssue,
)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
@@ -1305,7 +1305,7 @@ class SafetyCenterNotificationTest {
fun sendActionPendingIntentAndWaitWithPermission(
action: Notification.Action,
- timeout: Duration = Coroutines.TIMEOUT_LONG
+ timeout: Duration = Coroutines.TIMEOUT_LONG,
) {
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
PendingIntentSender.send(action.actionIntent)
@@ -1328,13 +1328,13 @@ class SafetyCenterNotificationTest {
fun sendContentPendingIntent(
statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
- andExecuteBlock: () -> Unit = {}
+ andExecuteBlock: () -> Unit = {},
) {
val contentIntent =
statusBarNotificationWithChannel.statusBarNotification.notification.contentIntent
executeBlockAndExit(
launchActivity = { PendingIntentSender.send(contentIntent) },
- block = andExecuteBlock
+ block = andExecuteBlock,
)
}
}
diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt
index 4ba293eb9..5cafe3d48 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt
+++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt
@@ -105,7 +105,7 @@ class SafetySourceDataFixesTest {
.addAction(
safetySourceTestData.action(
id = targetActionId,
- pendingIntent = originalPendingIntent
+ pendingIntent = originalPendingIntent,
)
)
.build()
@@ -126,6 +126,7 @@ class SafetySourceDataFixesTest {
)
assertThat(intentsFilterEqual(overriddenPendingIntent, expectedPendingIntent)).isTrue()
}
+
@Test
@SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun defaultActionOverride_notification_overridesMatchingActions() {
@@ -143,7 +144,7 @@ class SafetySourceDataFixesTest {
notification(
safetySourceTestData.action(
id = targetActionId,
- pendingIntent = originalPendingIntent
+ pendingIntent = originalPendingIntent,
)
)
)
@@ -183,7 +184,7 @@ class SafetySourceDataFixesTest {
.addAction(
safetySourceTestData.action(
id = targetActionId,
- pendingIntent = originalPendingIntent
+ pendingIntent = originalPendingIntent,
)
)
.build()
@@ -192,7 +193,7 @@ class SafetySourceDataFixesTest {
safetyCenterTestHelper.setData(
SOURCE_ID_2, // Different source ID
- dataWithoutActionToOverride
+ dataWithoutActionToOverride,
)
val actualPendingIntent =
@@ -219,7 +220,7 @@ class SafetySourceDataFixesTest {
.addAction(
safetySourceTestData.action(
id = "DifferentActionId",
- pendingIntent = originalPendingIntent
+ pendingIntent = originalPendingIntent,
)
)
.build()
@@ -253,7 +254,7 @@ class SafetySourceDataFixesTest {
.addAction(
safetySourceTestData.action(
id = targetActionId,
- pendingIntent = originalPendingIntent
+ pendingIntent = originalPendingIntent,
)
)
.build()
@@ -289,7 +290,7 @@ class SafetySourceDataFixesTest {
private fun intentsFilterEqual(
actualPendingIntent: PendingIntent,
- expectedPendingIntent: PendingIntent?
+ expectedPendingIntent: PendingIntent?,
) =
callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") {
actualPendingIntent.intentFilterEquals(expectedPendingIntent)
diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt
index f76a52256..6f147dfbf 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt
+++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt
@@ -69,7 +69,7 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity {
waitAllTextDisplayed(
safetyCenterResourcesApk.getStringByName("scanning_title"),
- safetyCenterResourcesApk.getStringByName("loading_summary")
+ safetyCenterResourcesApk.getStringByName("loading_summary"),
)
}
}
@@ -79,13 +79,13 @@ class SafetyCenterStatusCardTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.informationWithIconAction
+ safetySourceTestData.informationWithIconAction,
)
context.launchSafetyCenterActivity {
waitAllTextDisplayed(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesApk.getStringByName("loading_summary")
+ safetyCenterResourcesApk.getStringByName("loading_summary"),
)
}
}
@@ -98,7 +98,7 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_title"),
- safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_summary")
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_summary"),
)
waitButtonDisplayed(RESCAN_BUTTON_LABEL)
}
@@ -110,13 +110,13 @@ class SafetyCenterStatusCardTest {
preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary")
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary"),
)
waitButtonDisplayed(RESCAN_BUTTON_LABEL)
}
@@ -128,7 +128,7 @@ class SafetyCenterStatusCardTest {
preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -143,13 +143,13 @@ class SafetyCenterStatusCardTest {
preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.informationWithIssue)
+ Response.SetData(safetySourceTestData.informationWithIssue),
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
waitButtonNotDisplayed(RESCAN_BUTTON_LABEL)
}
@@ -160,11 +160,11 @@ class SafetyCenterStatusCardTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.recommendationWithGeneralIssue
+ safetySourceTestData.recommendationWithGeneralIssue,
)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.recommendationWithGeneralIssue)
+ Response.SetData(safetySourceTestData.recommendationWithGeneralIssue),
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -172,7 +172,7 @@ class SafetyCenterStatusCardTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_safety_recommendation_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
waitButtonNotDisplayed(RESCAN_BUTTON_LABEL)
}
@@ -183,11 +183,11 @@ class SafetyCenterStatusCardTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue,
)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
+ Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue),
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -195,7 +195,7 @@ class SafetyCenterStatusCardTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_safety_warning_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
waitButtonNotDisplayed(RESCAN_BUTTON_LABEL)
}
@@ -207,20 +207,20 @@ class SafetyCenterStatusCardTest {
preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary")
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary"),
)
waitButtonDisplayed(RESCAN_BUTTON_LABEL) { it.click() }
waitAllTextDisplayed(
safetyCenterResourcesApk.getStringByName("scanning_title"),
- safetyCenterResourcesApk.getStringByName("loading_summary")
+ safetyCenterResourcesApk.getStringByName("loading_summary"),
)
}
}
@@ -231,17 +231,17 @@ class SafetyCenterStatusCardTest {
preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
+ Response.SetData(safetySourceTestData.information),
)
SafetySourceReceiver.setResponse(
Request.Rescan(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.recommendationWithGeneralIssue)
+ Response.SetData(safetySourceTestData.recommendationWithGeneralIssue),
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary")
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary"),
)
waitButtonDisplayed(RESCAN_BUTTON_LABEL) { it.click() }
@@ -250,7 +250,7 @@ class SafetyCenterStatusCardTest {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_safety_recommendation_title"
),
- safetyCenterTestData.getAlertString(1)
+ safetyCenterTestData.getAlertString(1),
)
}
}
diff --git a/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt
index 7ad83b949..c3e104504 100644
--- a/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt
+++ b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt
@@ -47,9 +47,9 @@ import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitAllTextNotDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitButtonDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitDisplayed
-import com.android.safetycenter.testing.UiTestHelper.waitPageTitleDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueNotDisplayed
+import java.util.regex.Pattern
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -98,7 +98,7 @@ class PrivacySubpageTest {
context.getString(firstSource.summaryResId),
"Controls",
context.getString(lastSource.titleResId),
- context.getString(lastSource.summaryResId)
+ context.getString(lastSource.summaryResId),
)
}
}
@@ -117,7 +117,7 @@ class PrivacySubpageTest {
waitButtonDisplayed("Exit test activity") { it.click() }
waitAllTextDisplayed(
context.getString(source.titleResId),
- context.getString(source.summaryResId)
+ context.getString(source.summaryResId),
)
}
}
@@ -156,16 +156,16 @@ class PrivacySubpageTest {
context.launchSafetyCenterActivity(extras) {
waitAllText(
displayed = sensorPrivacyManager.supportsSensorToggle(CAMERA),
- text = "Camera access"
+ text = "Camera access",
)
waitAllText(
displayed = sensorPrivacyManager.supportsSensorToggle(MICROPHONE),
- text = "Microphone access"
+ text = "Microphone access",
)
waitAllTextDisplayed("Show clipboard access")
waitAllText(
displayed = getPermissionControllerBool("config_display_show_password_toggle"),
- text = "Show passwords"
+ text = "Show passwords",
)
waitAllTextDisplayed("Location access")
}
@@ -179,16 +179,14 @@ class PrivacySubpageTest {
val source: SafetySource = sourcesGroup.safetySources.first()
val extras = Bundle()
extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, sourcesGroup.id)
+ val containsLocationPattern = Pattern.compile(".*[Ll]ocation.*") // NOTYPO
context.launchSafetyCenterActivity(extras) {
- openPageAndExit("Location access") {
- waitPageTitleDisplayed("Location")
- waitAllTextDisplayed("Use location")
- }
+ openPageAndExit("Location access") { waitDisplayed(By.text(containsLocationPattern)) }
waitAllTextDisplayed(
context.getString(source.titleResId),
- context.getString(source.summaryResId)
+ context.getString(source.summaryResId),
)
}
}
@@ -225,7 +223,7 @@ class PrivacySubpageTest {
permissionControllerContext.resources.getIdentifier(
resourceName,
"bool",
- "com.android.permissioncontroller"
+ "com.android.permissioncontroller",
)
return permissionControllerContext.resources.getBoolean(resourceId)
}
diff --git a/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt
index 236beb34e..5c770cc92 100644
--- a/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt
+++ b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt
@@ -160,7 +160,7 @@ class SafetyCenterSubpagesTest {
context.getString(firstGroup.titleResId),
context.getString(firstGroup.summaryResId),
context.getString(lastGroup.titleResId),
- context.getString(lastGroup.summaryResId)
+ context.getString(lastGroup.summaryResId),
)
openPageAndExit(context.getString(lastGroup.titleResId)) {
@@ -191,7 +191,7 @@ class SafetyCenterSubpagesTest {
waitAllTextDisplayed(
context.getString(firstGroup.titleResId),
context.getString(firstGroup.summaryResId),
- context.getString(lastGroup.titleResId)
+ context.getString(lastGroup.titleResId),
)
waitDisplayed(By.text(context.getString(lastGroup.summaryResId))) { it.click() }
@@ -232,24 +232,24 @@ class SafetyCenterSubpagesTest {
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_1_TITLE,
- entrySummary = SAFETY_SOURCE_1_SUMMARY
- )
+ entrySummary = SAFETY_SOURCE_1_SUMMARY,
+ ),
)
setData(
SOURCE_ID_2,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_2_TITLE,
- entrySummary = SAFETY_SOURCE_2_SUMMARY
- )
+ entrySummary = SAFETY_SOURCE_2_SUMMARY,
+ ),
)
setData(
SOURCE_ID_3,
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION,
entryTitle = SAFETY_SOURCE_3_TITLE,
- entrySummary = SAFETY_SOURCE_3_SUMMARY
- )
+ entrySummary = SAFETY_SOURCE_3_SUMMARY,
+ ),
)
}
val firstGroup = safetyCenterTestConfigs.multipleSourcesConfig.safetySourcesGroups[0]
@@ -263,7 +263,7 @@ class SafetyCenterSubpagesTest {
SAFETY_SOURCE_1_TITLE,
SAFETY_SOURCE_1_SUMMARY,
SAFETY_SOURCE_2_TITLE,
- SAFETY_SOURCE_2_SUMMARY
+ SAFETY_SOURCE_2_SUMMARY,
)
}
@@ -287,7 +287,7 @@ class SafetyCenterSubpagesTest {
waitButtonDisplayed("Exit test activity") { it.click() }
waitAllTextDisplayed(
context.getString(source.titleResId),
- context.getString(source.summaryResId)
+ context.getString(source.summaryResId),
)
}
}
@@ -370,7 +370,7 @@ class SafetyCenterSubpagesTest {
openPageAndExit(context.getString(sourcesGroup.titleResId)) {
waitAllTextDisplayed(
context.getString(source.titleResId),
- context.getString(source.summaryResId)
+ context.getString(source.summaryResId),
)
}
@@ -380,15 +380,15 @@ class SafetyCenterSubpagesTest {
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION,
entryTitle = "Updated title",
- entrySummary = "Updated summary"
+ entrySummary = "Updated summary",
)
- )
+ ),
)
openPageAndExit(context.getString(sourcesGroup.titleResId)) {
waitAllTextNotDisplayed(
context.getString(source.titleResId),
- context.getString(source.summaryResId)
+ context.getString(source.summaryResId),
)
waitAllTextDisplayed("Updated title", "Updated summary")
}
@@ -405,7 +405,7 @@ class SafetyCenterSubpagesTest {
openPageAndExit(context.getString(sourcesGroup.titleResId)) {
waitAllTextDisplayed(
context.getString(source.titleResId),
- context.getString(source.summaryResId)
+ context.getString(source.summaryResId),
)
SafetySourceReceiver.setResponse(
@@ -414,15 +414,15 @@ class SafetyCenterSubpagesTest {
safetySourceTestData.buildSafetySourceDataWithSummary(
severityLevel = SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION,
entryTitle = "Updated title",
- entrySummary = "Updated summary"
+ entrySummary = "Updated summary",
)
- )
+ ),
)
UiAutomatorUtils2.getUiDevice().rotate()
waitAllTextDisplayed(
context.getString(source.titleResId),
- context.getString(source.summaryResId)
+ context.getString(source.summaryResId),
)
waitAllTextNotDisplayed("Updated title", "Updated summary")
}
@@ -489,7 +489,7 @@ class SafetyCenterSubpagesTest {
// Clear the data when action is triggered to simulate resolution.
SafetySourceReceiver.setResponse(
Request.ResolveAction(SINGLE_SOURCE_ID),
- Response.ClearData
+ Response.ClearData,
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
@@ -921,7 +921,7 @@ class SafetyCenterSubpagesTest {
waitPageTitleDisplayed(context.getString(sourcesGroup.titleResId))
waitAllTextDisplayed(
context.getString(source.titleResId),
- context.getString(source.summaryResId)
+ context.getString(source.summaryResId),
)
}
}
@@ -952,7 +952,7 @@ class SafetyCenterSubpagesTest {
waitAllTextDisplayed(
context.getString(source.titleResId),
context.getString(source.summaryResId),
- safetyCenterResourcesApk.getStringByName("test_single_source_group_id_footer")
+ safetyCenterResourcesApk.getStringByName("test_single_source_group_id_footer"),
)
}
}
@@ -975,7 +975,7 @@ class SafetyCenterSubpagesTest {
private fun checkOnDismissedIssue(
sourcesGroup: SafetySourcesGroup,
issue: SafetySourceIssue,
- block: () -> Unit
+ block: () -> Unit,
) {
val safetyCenterIssueId = SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, issue.id)
safetyCenterTestHelper.dismissSafetyCenterIssue(safetyCenterIssueId)
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt
index a7009b19e..cc8c53d5e 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt
@@ -56,14 +56,14 @@ object Coroutines {
/** Shorthand for [runBlocking] combined with [withTimeoutOrNull] */
fun <T> runBlockingWithTimeoutOrNull(
timeout: Duration = TIMEOUT_LONG,
- block: suspend () -> T
+ block: suspend () -> T,
): T? = runBlocking { withTimeoutOrNull(timeout.toMillis()) { block() } }
/** Check a condition using coroutines with a timeout. */
fun waitForWithTimeout(
timeout: Duration = TIMEOUT_LONG,
checkPeriod: Duration = CHECK_PERIOD,
- condition: () -> Boolean
+ condition: () -> Boolean,
) {
runBlockingWithTimeout(timeout) { waitFor(checkPeriod, condition) }
}
@@ -72,7 +72,7 @@ object Coroutines {
fun waitForSuccessWithTimeout(
timeout: Duration = TIMEOUT_LONG,
checkPeriod: Duration = CHECK_PERIOD,
- fallibleAction: () -> Unit
+ fallibleAction: () -> Unit,
) {
waitForWithTimeout(timeout, checkPeriod) {
try {
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt
index 1ed0ecbc3..7303d631b 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt
@@ -42,7 +42,7 @@ class EnableSensorRule(context: Context, val sensor: Int) : TestRule {
override fun evaluate() {
assumeTrue(
"Test device does not support toggling sensor $sensor",
- supportsSensorToggle()
+ supportsSensorToggle(),
)
val oldSensorPrivacy = isSensorPrivacyEnabled()
setSensorPrivacy(false)
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/EqualsHashCodeToStringTester.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EqualsHashCodeToStringTester.kt
index 2dedfc853..77577d504 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/EqualsHashCodeToStringTester.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EqualsHashCodeToStringTester.kt
@@ -45,7 +45,7 @@ private constructor(
private val ignoreHashCode: Boolean = false,
private val ignoreToString: Boolean = false,
private val parcelRoundTripEqualsEquivalence: Equivalence<T>? = null,
- private val createCopy: ((T) -> T)? = null
+ private val createCopy: ((T) -> T)? = null,
) {
private val equalsTester = EqualsTester()
@@ -85,13 +85,13 @@ private constructor(
parcelableCreator: Parcelable.Creator<T>,
ignoreHashCode: Boolean = false,
ignoreToString: Boolean = false,
- createCopy: ((T) -> T)? = null
+ createCopy: ((T) -> T)? = null,
): EqualsHashCodeToStringTester<T> =
EqualsHashCodeToStringTester(
ignoreHashCode,
ignoreToString,
parcelRoundTripEqualsEquivalence(parcelableCreator),
- createCopy
+ createCopy,
)
/**
@@ -103,13 +103,13 @@ private constructor(
fun <T> of(
ignoreHashCode: Boolean = false,
ignoreToString: Boolean = false,
- createCopy: ((T) -> T)? = null
+ createCopy: ((T) -> T)? = null,
): EqualsHashCodeToStringTester<T> =
EqualsHashCodeToStringTester(
ignoreHashCode,
ignoreToString,
parcelRoundTripEqualsEquivalence = null,
- createCopy
+ createCopy,
)
/**
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt
index 81b752bca..11827a5f4 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt
@@ -38,7 +38,7 @@ data class NotificationCharacteristics(
private fun importanceMatches(
statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
- characteristicImportance: Int
+ characteristicImportance: Int,
): Boolean {
return characteristicImportance == IMPORTANCE_ANY ||
statusBarNotificationWithChannel.channel.importance == characteristicImportance
@@ -46,7 +46,7 @@ data class NotificationCharacteristics(
private fun blockableMatches(
statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
- characteristicBlockable: Boolean?
+ characteristicBlockable: Boolean?,
): Boolean {
return characteristicBlockable == null ||
statusBarNotificationWithChannel.channel.isBlockable == characteristicBlockable
@@ -54,7 +54,7 @@ data class NotificationCharacteristics(
fun safetySourceIdMatches(
statusBarNotification: StatusBarNotification,
- safetySourceId: String?
+ safetySourceId: String?,
): Boolean {
return safetySourceId == null ||
SafetyCenterIds.issueKeyFromString(statusBarNotification.tag).safetySourceId ==
@@ -63,7 +63,7 @@ data class NotificationCharacteristics(
private fun isMatch(
statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
- characteristic: NotificationCharacteristics
+ characteristic: NotificationCharacteristics,
): Boolean {
val notif = statusBarNotificationWithChannel.statusBarNotification.notification
val extras = notif.extras
@@ -75,13 +75,13 @@ data class NotificationCharacteristics(
blockableMatches(statusBarNotificationWithChannel, characteristic.blockable) &&
safetySourceIdMatches(
statusBarNotificationWithChannel.statusBarNotification,
- characteristic.safetySourceId
+ characteristic.safetySourceId,
)
}
fun areMatching(
statusBarNotifications: List<StatusBarNotificationWithChannel>,
- characteristics: List<NotificationCharacteristics>
+ characteristics: List<NotificationCharacteristics>,
): Boolean {
if (statusBarNotifications.size != characteristics.size) {
return false
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt
index 40515fa33..62f324d89 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt
@@ -48,13 +48,13 @@ object SafetyCenterActivityLauncher {
intentAction: String = ACTION_SAFETY_CENTER,
withReceiverPermission: Boolean = false,
preventTrampolineToSettings: Boolean = true,
- block: () -> Unit
+ block: () -> Unit,
) {
val launchSafetyCenterIntent =
createIntent(
intentAction,
intentExtras,
- preventTrampolineToSettings = preventTrampolineToSettings
+ preventTrampolineToSettings = preventTrampolineToSettings,
)
if (withReceiverPermission) {
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
@@ -83,7 +83,7 @@ object SafetyCenterActivityLauncher {
private fun createIntent(
intentAction: String,
intentExtras: Bundle?,
- preventTrampolineToSettings: Boolean = false
+ preventTrampolineToSettings: Boolean = false,
): Intent {
val launchIntent =
Intent(intentAction).addFlags(FLAG_ACTIVITY_NEW_TASK).addFlags(FLAG_ACTIVITY_CLEAR_TASK)
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt
index 961d03c47..b5f732bad 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt
@@ -52,7 +52,7 @@ object SafetyCenterApisWithShellPermissions {
fun SafetyCenterManager.setSafetySourceDataWithPermission(
safetySourceId: String,
safetySourceData: SafetySourceData?,
- safetyEvent: SafetyEvent
+ safetyEvent: SafetyEvent,
) {
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
setSafetySourceData(safetySourceId, safetySourceData, safetyEvent)
@@ -72,7 +72,7 @@ object SafetyCenterApisWithShellPermissions {
*/
fun SafetyCenterManager.reportSafetySourceErrorWithPermission(
safetySourceId: String,
- safetySourceErrorDetails: SafetySourceErrorDetails
+ safetySourceErrorDetails: SafetySourceErrorDetails,
) {
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
reportSafetySourceError(safetySourceId, safetySourceErrorDetails)
@@ -85,7 +85,7 @@ object SafetyCenterApisWithShellPermissions {
*/
fun SafetyCenterManager.refreshSafetySourcesWithPermission(
refreshReason: Int,
- safetySourceIds: List<String>? = null
+ safetySourceIds: List<String>? = null,
) {
callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) {
if (safetySourceIds != null) {
@@ -116,7 +116,7 @@ object SafetyCenterApisWithShellPermissions {
*/
fun SafetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
executor: Executor,
- listener: OnSafetyCenterDataChangedListener
+ listener: OnSafetyCenterDataChangedListener,
) {
callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) {
addOnSafetyCenterDataChangedListener(executor, listener)
@@ -151,7 +151,7 @@ object SafetyCenterApisWithShellPermissions {
*/
fun SafetyCenterManager.executeSafetyCenterIssueActionWithPermission(
safetyCenterIssueId: String,
- safetyCenterIssueActionId: String
+ safetyCenterIssueActionId: String,
) {
callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) {
executeSafetyCenterIssueAction(safetyCenterIssueId, safetyCenterIssueActionId)
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt
index f8926caac..e454b6e25 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt
@@ -54,16 +54,14 @@ class SafetyCenterEnabledChangedReceiver(private val context: Context) : Broadca
fun setSafetyCenterEnabledWithReceiverPermissionAndWait(
value: Boolean,
- timeout: Duration = TIMEOUT_LONG
+ timeout: Duration = TIMEOUT_LONG,
): Boolean =
callWithShellPermissionIdentity(READ_SAFETY_CENTER_STATUS) {
SafetyCenterFlags.isEnabled = value
receiveSafetyCenterEnabledChanged(timeout)
}
- fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait(
- value: Boolean,
- ) {
+ fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait(value: Boolean) {
SafetyCenterFlags.isEnabled = value
WaitForBroadcasts.waitForBroadcasts()
receiveSafetyCenterEnabledChanged(TIMEOUT_SHORT)
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..7efbba7a0 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
@@ -61,7 +62,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_notifications_min_delay",
defaultValue = Duration.ofHours(2),
- DurationParser()
+ DurationParser(),
)
/**
@@ -72,7 +73,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_notifications_allowed_sources",
defaultValue = emptySet(),
- SetParser(StringParser())
+ SetParser(StringParser()),
)
/**
@@ -83,7 +84,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_notifications_immediate_behavior_issues",
defaultValue = emptySet(),
- SetParser(StringParser())
+ SetParser(StringParser()),
)
/**
@@ -98,7 +99,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_notification_resurface_interval",
defaultValue = Duration.ofDays(-1),
- DurationParser()
+ DurationParser(),
)
/** Flag that determines whether we should replace the IconAction of the lock screen source. */
@@ -119,7 +120,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_refresh_sources_timeouts_millis",
defaultValue = getAllRefreshTimeoutsMap(TEST_TIMEOUT),
- MapParser(IntParser(), DurationParser())
+ MapParser(IntParser(), DurationParser()),
)
/**
@@ -130,7 +131,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_resolve_action_timeout_millis",
defaultValue = TIMEOUT_LONG,
- DurationParser()
+ DurationParser(),
)
/** Flag that determines a duration after which a temporarily hidden issue will resurface. */
@@ -138,7 +139,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_temp_hidden_issue_resurface_delay_millis",
defaultValue = Duration.ofDays(2),
- DurationParser()
+ DurationParser(),
)
/**
@@ -149,7 +150,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_hide_resolved_ui_transition_delay_millis",
defaultValue = Duration.ofMillis(400),
- DurationParser()
+ DurationParser(),
)
/**
@@ -161,7 +162,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_untracked_sources",
defaultValue = emptySet(),
- SetParser(StringParser())
+ SetParser(StringParser()),
)
/**
@@ -173,7 +174,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_resurface_issue_max_counts",
defaultValue = emptyMap(),
- MapParser(IntParser(), LongParser())
+ MapParser(IntParser(), LongParser()),
)
/**
@@ -187,7 +188,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_resurface_issue_delays_millis",
defaultValue = emptyMap(),
- MapParser(IntParser(), DurationParser())
+ MapParser(IntParser(), DurationParser()),
)
/**
@@ -199,7 +200,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_issue_category_allowlists",
defaultValue = emptyMap(),
- MapParser(IntParser(), SetParser(StringParser(), delimiter = "|"))
+ MapParser(IntParser(), SetParser(StringParser(), delimiter = "|")),
)
/**
@@ -211,7 +212,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_actions_to_override_with_default_intent",
defaultValue = emptyMap(),
- MapParser(StringParser(), SetParser(StringParser(), delimiter = "|"))
+ MapParser(StringParser(), SetParser(StringParser(), delimiter = "|")),
)
/**
@@ -223,7 +224,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_background_refresh_denied_sources",
defaultValue = emptySet(),
- SetParser(StringParser())
+ SetParser(StringParser()),
)
/**
@@ -245,7 +246,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_qs_tile_component_setting_flags",
defaultValue = PackageManager.DONT_KILL_APP,
- IntParser()
+ IntParser(),
)
/**
@@ -253,13 +254,13 @@ object SafetyCenterFlags {
* expand-and-collapse list.
*/
private val showSubpagesFlag =
- Flag("safety_center_show_subpages", defaultValue = false, BooleanParser())
+ Flag("safety_center_show_subpages", defaultValue = SdkLevel.isAtLeastU(), BooleanParser())
private val overrideRefreshOnPageOpenSourcesFlag =
Flag(
"safety_center_override_refresh_on_page_open_sources",
defaultValue = setOf(),
- SetParser(StringParser())
+ SetParser(StringParser()),
)
/**
@@ -272,7 +273,7 @@ object SafetyCenterFlags {
// do not set defaultValue to true, do not want background refreshes running
// during other tests
defaultValue = false,
- BooleanParser()
+ BooleanParser(),
)
/**
@@ -286,7 +287,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_periodic_background_interval_millis",
defaultValue = Duration.ofDays(1),
- DurationParser()
+ DurationParser(),
)
/** Flag for allowlisting additional certificates for a given package. */
@@ -294,7 +295,7 @@ object SafetyCenterFlags {
Flag(
"safety_center_additional_allow_package_certs",
defaultValue = emptyMap(),
- MapParser(StringParser(), SetParser(StringParser(), delimiter = "|"))
+ MapParser(StringParser(), SetParser(StringParser(), delimiter = "|")),
)
/** Every Safety Center flag. */
@@ -323,7 +324,7 @@ object SafetyCenterFlags {
showSubpagesFlag,
overrideRefreshOnPageOpenSourcesFlag,
backgroundRefreshIsEnabledFlag,
- periodicBackgroundRefreshIntervalFlag
+ periodicBackgroundRefreshIntervalFlag,
)
/** A property that allows getting and setting the [isEnabledFlag]. */
@@ -453,7 +454,7 @@ object SafetyCenterFlags {
REFRESH_REASON_DEVICE_LOCALE_CHANGE to refreshTimeout,
REFRESH_REASON_SAFETY_CENTER_ENABLED to refreshTimeout,
REFRESH_REASON_OTHER to refreshTimeout,
- REFRESH_REASON_PERIODIC to refreshTimeout
+ REFRESH_REASON_PERIODIC to refreshTimeout,
)
private interface Parser<T> {
@@ -486,7 +487,7 @@ object SafetyCenterFlags {
private class SetParser<T>(
private val elementParser: Parser<T>,
- private val delimiter: String = ","
+ private val delimiter: String = ",",
) : Parser<Set<T>> {
override fun parseFromString(stringValue: String) =
stringValue.split(delimiter).map(elementParser::parseFromString).toSet()
@@ -499,7 +500,7 @@ object SafetyCenterFlags {
private val keyParser: Parser<K>,
private val valueParser: Parser<V>,
private val entriesDelimiter: String = ",",
- private val pairDelimiter: String = ":"
+ private val pairDelimiter: String = ":",
) : Parser<Map<K, V>> {
override fun parseFromString(stringValue: String) =
stringValue.split(entriesDelimiter).associate { pair ->
@@ -532,13 +533,13 @@ 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,
name,
stringValue, /* makeDefault */
- false
+ false,
)
require(valueWasSet) { "Could not set $name to: $stringValue" }
}
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt
index 261e179dd..231cec821 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt
@@ -48,7 +48,7 @@ class SafetyCenterTestConfigs(private val context: Context) {
context.packageManager
.getPackageInfo(
context.packageName,
- PackageInfoFlags.of(GET_SIGNING_CERTIFICATES.toLong())
+ PackageInfoFlags.of(GET_SIGNING_CERTIFICATES.toLong()),
)
.signingInfo!!
.apkContentsSigners[0]
@@ -220,25 +220,25 @@ class SafetyCenterTestConfigs(private val context: Context) {
.addSafetySource(
issueOnlySafetySourceWithDuplicationInfo(
SOURCE_ID_1,
- DEDUPLICATION_GROUP_1
+ DEDUPLICATION_GROUP_1,
)
)
.addSafetySource(
issueOnlySafetySourceWithDuplicationInfo(
SOURCE_ID_2,
- DEDUPLICATION_GROUP_1
+ DEDUPLICATION_GROUP_1,
)
)
.addSafetySource(
issueOnlySafetySourceWithDuplicationInfo(
SOURCE_ID_3,
- DEDUPLICATION_GROUP_2
+ DEDUPLICATION_GROUP_2,
)
)
.addSafetySource(
issueOnlySafetySourceWithDuplicationInfo(
SOURCE_ID_4,
- DEDUPLICATION_GROUP_3
+ DEDUPLICATION_GROUP_3,
)
)
.build()
@@ -248,13 +248,13 @@ class SafetyCenterTestConfigs(private val context: Context) {
.addSafetySource(
issueOnlySafetySourceWithDuplicationInfo(
SOURCE_ID_5,
- DEDUPLICATION_GROUP_1
+ DEDUPLICATION_GROUP_1,
)
)
.addSafetySource(
issueOnlySafetySourceWithDuplicationInfo(
SOURCE_ID_6,
- DEDUPLICATION_GROUP_3
+ DEDUPLICATION_GROUP_3,
)
)
.build()
@@ -264,7 +264,7 @@ class SafetyCenterTestConfigs(private val context: Context) {
.addSafetySource(
issueOnlySafetySourceWithDuplicationInfo(
SOURCE_ID_7,
- DEDUPLICATION_GROUP_3
+ DEDUPLICATION_GROUP_3,
)
)
.build()
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt
index 289bc32a8..15f8d02ae 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt
@@ -32,6 +32,8 @@ import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNKNOWN
import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED
import android.safetycenter.SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON
import android.safetycenter.SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION
+import android.safetycenter.SafetyCenterEntryGroup
+import android.safetycenter.SafetyCenterEntryOrGroup
import android.safetycenter.SafetyCenterIssue
import android.safetycenter.SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING
import android.safetycenter.SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK
@@ -80,7 +82,7 @@ class SafetyCenterTestData(context: Context) {
),
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_ok_review_summary"
- )
+ ),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN)
.build()
@@ -91,7 +93,7 @@ class SafetyCenterTestData(context: Context) {
*/
fun safetyCenterStatusOneAlert(
statusResource: String,
- overallSeverityLevel: Int
+ overallSeverityLevel: Int,
): SafetyCenterStatus = safetyCenterStatusNAlerts(statusResource, overallSeverityLevel, 1)
/**
@@ -105,7 +107,7 @@ class SafetyCenterTestData(context: Context) {
): SafetyCenterStatus =
SafetyCenterStatus.Builder(
safetyCenterResourcesApk.getStringByName(statusResource),
- getAlertString(numAlerts)
+ getAlertString(numAlerts),
)
.setSeverityLevel(overallSeverityLevel)
.build()
@@ -114,12 +116,10 @@ class SafetyCenterTestData(context: Context) {
* Returns an information [SafetyCenterStatus] that has "Tip(s) available" as a summary for the
* given [numTipIssues].
*/
- fun safetyCenterStatusTips(
- numTipIssues: Int,
- ): SafetyCenterStatus =
+ fun safetyCenterStatusTips(numTipIssues: Int): SafetyCenterStatus =
SafetyCenterStatus.Builder(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
- getIcuPluralsString("overall_severity_level_tip_summary", numTipIssues)
+ getIcuPluralsString("overall_severity_level_tip_summary", numTipIssues),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
.build()
@@ -128,15 +128,13 @@ class SafetyCenterTestData(context: Context) {
* Returns an information [SafetyCenterStatus] that has "Action(s) taken" as a summary for the
* given [numAutomaticIssues].
*/
- fun safetyCenterStatusActionsTaken(
- numAutomaticIssues: Int,
- ): SafetyCenterStatus =
+ fun safetyCenterStatusActionsTaken(numAutomaticIssues: Int): SafetyCenterStatus =
SafetyCenterStatus.Builder(
safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
getIcuPluralsString(
"overall_severity_level_action_taken_summary",
- numAutomaticIssues
- )
+ numAutomaticIssues,
+ ),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
.build()
@@ -150,7 +148,7 @@ class SafetyCenterTestData(context: Context) {
safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_safety_warning_title"
),
- getAlertString(numAlerts)
+ getAlertString(numAlerts),
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
@@ -165,7 +163,7 @@ class SafetyCenterTestData(context: Context) {
userId: Int = UserHandle.myUserId(),
title: CharSequence = "OK",
pendingIntent: PendingIntent? =
- safetySourceTestData.createTestActivityRedirectPendingIntent()
+ safetySourceTestData.createTestActivityRedirectPendingIntent(),
) =
SafetyCenterEntry.Builder(entryId(sourceId, userId), title)
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
@@ -183,7 +181,7 @@ class SafetyCenterTestData(context: Context) {
userId: Int = UserHandle.myUserId(),
title: CharSequence = "OK",
pendingIntent: PendingIntent? =
- safetySourceTestData.createTestActivityRedirectPendingIntent()
+ safetySourceTestData.createTestActivityRedirectPendingIntent(),
) = safetyCenterEntryDefaultBuilder(sourceId, userId, title, pendingIntent).build()
/**
@@ -194,7 +192,7 @@ class SafetyCenterTestData(context: Context) {
fun safetyCenterEntryDefaultStaticBuilder(
sourceId: String,
userId: Int = UserHandle.myUserId(),
- title: CharSequence = "OK"
+ title: CharSequence = "OK",
) =
SafetyCenterEntry.Builder(entryId(sourceId, userId), title)
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
@@ -219,7 +217,7 @@ class SafetyCenterTestData(context: Context) {
fun safetyCenterEntryUnspecified(
sourceId: String,
pendingIntent: PendingIntent? =
- safetySourceTestData.createTestActivityRedirectPendingIntent()
+ safetySourceTestData.createTestActivityRedirectPendingIntent(),
) =
SafetyCenterEntry.Builder(entryId(sourceId), "Unspecified title")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
@@ -237,7 +235,7 @@ class SafetyCenterTestData(context: Context) {
fun safetyCenterEntryOkBuilder(
sourceId: String,
userId: Int = UserHandle.myUserId(),
- title: CharSequence = "Ok title"
+ title: CharSequence = "Ok title",
) =
SafetyCenterEntry.Builder(entryId(sourceId, userId), title)
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_OK)
@@ -252,7 +250,7 @@ class SafetyCenterTestData(context: Context) {
fun safetyCenterEntryOk(
sourceId: String,
userId: Int = UserHandle.myUserId(),
- title: CharSequence = "Ok title"
+ title: CharSequence = "Ok title",
) = safetyCenterEntryOkBuilder(sourceId, userId, title).build()
/**
@@ -262,7 +260,7 @@ class SafetyCenterTestData(context: Context) {
*/
fun safetyCenterEntryRecommendation(
sourceId: String,
- summary: String = "Recommendation summary"
+ summary: String = "Recommendation summary",
) =
SafetyCenterEntry.Builder(entryId(sourceId), "Recommendation title")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
@@ -283,6 +281,32 @@ class SafetyCenterTestData(context: Context) {
.setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION)
.build()
+ fun singletonSafetyCenterEntryOrGroup(
+ groupId: String,
+ entry: SafetyCenterEntry,
+ groupSummary: String? = null,
+ ) =
+ // TODO: b/361404288 - Replace with platform version check
+ if (SafetyCenterFlags.showSubpages) {
+ val summary =
+ if (groupSummary == null && entry.severityLevel > ENTRY_SEVERITY_LEVEL_OK) {
+ entry.summary
+ } else groupSummary ?: "OK"
+
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(groupId, "OK")
+ .setSeverityLevel(entry.severityLevel)
+ .setSeverityUnspecifiedIconType(
+ SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION
+ )
+ .setSummary(summary)
+ .setEntries(listOf(entry))
+ .build()
+ )
+ } else {
+ SafetyCenterEntryOrGroup(entry)
+ }
+
/**
* Returns an information [SafetyCenterIssue] for the given source and user id that is
* consistent with information [SafetySourceIssue]s used in [SafetySourceTestData].
@@ -291,12 +315,12 @@ class SafetyCenterTestData(context: Context) {
sourceId: String,
userId: Int = UserHandle.myUserId(),
attributionTitle: String? = "OK",
- groupId: String? = SINGLE_SOURCE_GROUP_ID
+ groupId: String? = SINGLE_SOURCE_GROUP_ID,
) =
SafetyCenterIssue.Builder(
issueId(sourceId, INFORMATION_ISSUE_ID, userId = userId),
"Information issue title",
- "Information issue summary"
+ "Information issue summary",
)
.setSeverityLevel(ISSUE_SEVERITY_LEVEL_OK)
.setShouldConfirmDismissal(false)
@@ -307,10 +331,10 @@ class SafetyCenterTestData(context: Context) {
sourceId,
INFORMATION_ISSUE_ID,
INFORMATION_ISSUE_ACTION_ID,
- userId
+ userId,
),
"Review",
- safetySourceTestData.createTestActivityRedirectPendingIntent()
+ safetySourceTestData.createTestActivityRedirectPendingIntent(),
)
.build()
)
@@ -332,12 +356,12 @@ class SafetyCenterTestData(context: Context) {
userId: Int = UserHandle.myUserId(),
attributionTitle: String? = "OK",
groupId: String? = SINGLE_SOURCE_GROUP_ID,
- confirmationDialog: Boolean = false
+ confirmationDialog: Boolean = false,
) =
SafetyCenterIssue.Builder(
issueId(sourceId, RECOMMENDATION_ISSUE_ID, userId = userId),
"Recommendation issue title",
- "Recommendation issue summary"
+ "Recommendation issue summary",
)
.setSeverityLevel(ISSUE_SEVERITY_LEVEL_RECOMMENDATION)
.setActions(
@@ -347,10 +371,10 @@ class SafetyCenterTestData(context: Context) {
sourceId,
RECOMMENDATION_ISSUE_ID,
RECOMMENDATION_ISSUE_ACTION_ID,
- userId
+ userId,
),
"See issue",
- safetySourceTestData.createTestActivityRedirectPendingIntent()
+ safetySourceTestData.createTestActivityRedirectPendingIntent(),
)
.apply {
if (confirmationDialog && SdkLevel.isAtLeastU()) {
@@ -359,7 +383,7 @@ class SafetyCenterTestData(context: Context) {
"Confirmation title",
"Confirmation text",
"Confirmation yes",
- "Confirmation no"
+ "Confirmation no",
)
)
}
@@ -384,12 +408,12 @@ class SafetyCenterTestData(context: Context) {
isActionInFlight: Boolean = false,
userId: Int = UserHandle.myUserId(),
attributionTitle: String? = "OK",
- groupId: String? = SINGLE_SOURCE_GROUP_ID
+ groupId: String? = SINGLE_SOURCE_GROUP_ID,
) =
SafetyCenterIssue.Builder(
issueId(sourceId, CRITICAL_ISSUE_ID, userId = userId),
"Critical issue title",
- "Critical issue summary"
+ "Critical issue summary",
)
.setSeverityLevel(ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING)
.setActions(
@@ -399,10 +423,10 @@ class SafetyCenterTestData(context: Context) {
sourceId,
CRITICAL_ISSUE_ID,
CRITICAL_ISSUE_ACTION_ID,
- userId
+ userId,
),
"Solve issue",
- safetySourceTestData.criticalIssueActionPendingIntent
+ safetySourceTestData.criticalIssueActionPendingIntent,
)
.setWillResolve(true)
.setIsInFlight(isActionInFlight)
@@ -432,7 +456,7 @@ class SafetyCenterTestData(context: Context) {
val messageFormat =
MessageFormat(
safetyCenterResourcesApk.getStringByName(name, formatArgs),
- Locale.getDefault()
+ Locale.getDefault(),
)
val arguments = ArrayMap<String, Any>()
arguments["count"] = count
@@ -448,7 +472,7 @@ class SafetyCenterTestData(context: Context) {
.build(),
emptyList(),
emptyList(),
- emptyList()
+ emptyList(),
)
/** Creates an ID for a Safety Center entry. */
@@ -465,7 +489,7 @@ class SafetyCenterTestData(context: Context) {
sourceId: String,
sourceIssueId: String,
issueTypeId: String = ISSUE_TYPE_ID,
- userId: Int = UserHandle.myUserId()
+ userId: Int = UserHandle.myUserId(),
) =
SafetyCenterIds.encodeToString(
SafetyCenterIssueId.newBuilder()
@@ -485,7 +509,7 @@ class SafetyCenterTestData(context: Context) {
sourceId: String,
sourceIssueId: String,
sourceIssueActionId: String,
- userId: Int = UserHandle.myUserId()
+ userId: Int = UserHandle.myUserId(),
) =
SafetyCenterIds.encodeToString(
SafetyCenterIssueActionId.newBuilder()
@@ -546,7 +570,7 @@ class SafetyCenterTestData(context: Context) {
private fun SafetyCenterData.copy(
issues: List<SafetyCenterIssue> = this.issues,
dismissedIssues: List<SafetyCenterIssue> = this.dismissedIssues,
- extras: Bundle = this.extras
+ extras: Bundle = this.extras,
): SafetyCenterData =
SafetyCenterData.Builder(status)
.apply {
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt
index a138675d3..ac646648a 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt
@@ -108,7 +108,7 @@ class SafetyCenterTestHelper(val context: Context) {
}
assumeTrue(
"Cannot toggle SafetyCenter using DeviceConfig",
- safetyCenterCanBeToggledUsingDeviceConfig()
+ safetyCenterCanBeToggledUsingDeviceConfig(),
)
setEnabledWaitingForSafetyCenterBroadcastIdle(value, safetyCenterConfig)
}
@@ -132,7 +132,7 @@ class SafetyCenterTestHelper(val context: Context) {
val listener = SafetyCenterTestListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(),
- listener
+ listener,
)
if (skipInitialData) {
listener.receiveSafetyCenterData()
@@ -145,14 +145,14 @@ class SafetyCenterTestHelper(val context: Context) {
fun setData(
safetySourceId: String,
safetySourceData: SafetySourceData?,
- safetyEvent: SafetyEvent = EVENT_SOURCE_STATE_CHANGED
+ safetyEvent: SafetyEvent = EVENT_SOURCE_STATE_CHANGED,
) {
Log.d(TAG, "setData for $safetySourceId")
require(isEnabled())
safetyCenterManager.setSafetySourceDataWithPermission(
safetySourceId,
safetySourceData,
- safetyEvent
+ safetyEvent,
)
}
@@ -173,7 +173,7 @@ class SafetyCenterTestHelper(val context: Context) {
private fun setEnabledWaitingForSafetyCenterBroadcastIdle(
value: Boolean,
- safetyCenterConfig: SafetyCenterConfig
+ safetyCenterConfig: SafetyCenterConfig,
) =
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE, READ_SAFETY_CENTER_STATUS) {
val enabledChangedReceiver = SafetyCenterEnabledChangedReceiver(context)
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt
index 8ce5c25d4..46e9b6593 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt
@@ -58,7 +58,7 @@ class SafetyCenterTestListener : OnSafetyCenterDataChangedListener {
*/
fun receiveSafetyCenterData(
timeout: Duration = TIMEOUT_LONG,
- matching: (SafetyCenterData) -> Boolean = { true }
+ matching: (SafetyCenterData) -> Boolean = { true },
): SafetyCenterData =
runBlockingWithTimeout(timeout) {
var safetyCenterData = dataChannel.receive()
@@ -78,7 +78,7 @@ class SafetyCenterTestListener : OnSafetyCenterDataChangedListener {
*/
fun waitForSafetyCenterRefresh(
timeout: Duration = TIMEOUT_LONG,
- withErrorEntry: Boolean? = null
+ withErrorEntry: Boolean? = null,
): SafetyCenterData {
receiveSafetyCenterData(timeout) {
it.status.refreshStatus == SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS ||
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt
index dcbc4ebe9..c89b29b2a 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt
@@ -23,7 +23,7 @@ import org.junit.runners.model.Statement
/** A JUnit [TestRule] that performs setup and reset steps before and after Safety Center tests. */
class SafetyCenterTestRule(
private val safetyCenterTestHelper: SafetyCenterTestHelper,
- private val withNotifications: Boolean = false
+ private val withNotifications: Boolean = false,
) : TestRule {
override fun apply(base: Statement, description: Description): Statement {
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt
index 8386228b8..77d338f90 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt
@@ -114,7 +114,7 @@ class SafetySourceIntentHandler {
private suspend fun SafetyCenterManager.processRefreshSafetySources(
intent: Intent,
- userId: Int
+ userId: Int,
) {
val broadcastId = intent.getStringExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID)
if (broadcastId.isNullOrEmpty()) {
@@ -190,7 +190,7 @@ class SafetySourceIntentHandler {
private fun createResolveActionSuccessEvent(
sourceIssueId: String,
- sourceIssueActionId: String
+ sourceIssueActionId: String,
) =
SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED)
.setSafetySourceIssueId(sourceIssueId)
@@ -218,7 +218,7 @@ class SafetySourceIntentHandler {
private suspend fun SafetyCenterManager.processRequest(
request: Request,
- safetyEventForResponse: (Response) -> SafetyEvent
+ safetyEventForResponse: (Response) -> SafetyEvent,
) {
val response = mutex.withLock { requestsToResponses[request] } ?: return
val safetyEvent = response.overrideSafetyEvent ?: safetyEventForResponse(response)
@@ -242,25 +242,25 @@ class SafetySourceIntentHandler {
/** Creates a refresh [Request] based on the given [sourceId] and [userId]. */
data class Refresh(
override val sourceId: String,
- override val userId: Int = UserHandle.myUserId()
+ override val userId: Int = UserHandle.myUserId(),
) : Request
/** Creates a rescan [Request] based on the given [sourceId] and [userId]. */
data class Rescan(
override val sourceId: String,
- override val userId: Int = UserHandle.myUserId()
+ override val userId: Int = UserHandle.myUserId(),
) : Request
/** Creates a resolve action [Request] based on the given [sourceId] and [userId]. */
data class ResolveAction(
override val sourceId: String,
- override val userId: Int = UserHandle.myUserId()
+ override val userId: Int = UserHandle.myUserId(),
) : Request
/** Creates an issue dismissal [Request] based on the given [sourceId] and [userId]. */
data class DismissIssue(
override val sourceId: String,
- override val userId: Int = UserHandle.myUserId()
+ override val userId: Int = UserHandle.myUserId(),
) : Request
}
@@ -295,7 +295,7 @@ class SafetySourceIntentHandler {
data class SetData(
val safetySourceData: SafetySourceData,
val overrideBroadcastId: String? = null,
- override val overrideSafetyEvent: SafetyEvent? = null
+ override val overrideSafetyEvent: SafetyEvent? = null,
) : Response
}
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt
index 29072c989..6b86df0dd 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt
@@ -86,7 +86,7 @@ class SafetySourceReceiver : BroadcastReceiver() {
NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_ID,
- IMPORTANCE_DEFAULT
+ IMPORTANCE_DEFAULT,
)
)
startForeground(
@@ -98,7 +98,7 @@ class SafetySourceReceiver : BroadcastReceiver() {
"ForegroundService"
)
.setSmallIcon(android.R.drawable.ic_info)
- .build()
+ .build(),
)
serviceScope.launch {
try {
@@ -150,7 +150,7 @@ class SafetySourceReceiver : BroadcastReceiver() {
componentName,
if (enabled) COMPONENT_ENABLED_STATE_ENABLED
else COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP
+ PackageManager.DONT_KILL_APP,
)
}
@@ -165,7 +165,7 @@ class SafetySourceReceiver : BroadcastReceiver() {
fun SafetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
refreshReason: Int,
safetySourceIds: List<String>? = null,
- timeout: Duration = TIMEOUT_LONG
+ timeout: Duration = TIMEOUT_LONG,
): String =
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
refreshSafetySourcesWithPermission(refreshReason, safetySourceIds)
@@ -174,7 +174,7 @@ class SafetySourceReceiver : BroadcastReceiver() {
fun SafetyCenterManager.refreshSafetySourcesWithoutReceiverPermissionAndWait(
refreshReason: Int,
- safetySourceIds: List<String>? = null
+ safetySourceIds: List<String>? = null,
) {
refreshSafetySourcesWithPermission(refreshReason, safetySourceIds)
WaitForBroadcasts.waitForBroadcasts()
@@ -183,16 +183,14 @@ class SafetySourceReceiver : BroadcastReceiver() {
fun setSafetyCenterEnabledWithReceiverPermissionAndWait(
value: Boolean,
- timeout: Duration = TIMEOUT_LONG
+ timeout: Duration = TIMEOUT_LONG,
): Boolean =
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
SafetyCenterFlags.isEnabled = value
receiveSafetyCenterEnabledChanged(timeout)
}
- fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait(
- value: Boolean,
- ) {
+ fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait(value: Boolean) {
SafetyCenterFlags.isEnabled = value
WaitForBroadcasts.waitForBroadcasts()
receiveSafetyCenterEnabledChanged(TIMEOUT_SHORT)
@@ -201,7 +199,7 @@ class SafetySourceReceiver : BroadcastReceiver() {
fun SafetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
issueId: String,
issueActionId: String,
- timeout: Duration = TIMEOUT_LONG
+ timeout: Duration = TIMEOUT_LONG,
) {
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
executeSafetyCenterIssueActionWithPermission(issueId, issueActionId)
@@ -211,7 +209,7 @@ class SafetySourceReceiver : BroadcastReceiver() {
fun SafetyCenterManager.dismissSafetyCenterIssueWithPermissionAndWait(
issueId: String,
- timeout: Duration = TIMEOUT_LONG
+ timeout: Duration = TIMEOUT_LONG,
) {
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
dismissSafetyCenterIssueWithPermission(issueId)
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt
index 7e77c0827..aad665004 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt
@@ -63,11 +63,11 @@ class SafetySourceTestData(private val context: Context) {
*/
fun createTestActivityRedirectPendingIntent(
explicit: Boolean = true,
- identifier: String? = null
+ identifier: String? = null,
) =
createRedirectPendingIntent(
context,
- createTestActivityIntent(context, explicit).setIdentifier(identifier)
+ createTestActivityIntent(context, explicit).setIdentifier(identifier),
)
/** A [SafetySourceData] with a [SEVERITY_LEVEL_UNSPECIFIED] [SafetySourceStatus]. */
@@ -77,7 +77,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Unspecified title",
"Unspecified summary",
- SEVERITY_LEVEL_UNSPECIFIED
+ SEVERITY_LEVEL_UNSPECIFIED,
)
.setEnabled(false)
.build()
@@ -94,7 +94,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Clickable disabled title",
"Clickable disabled summary",
- SEVERITY_LEVEL_UNSPECIFIED
+ SEVERITY_LEVEL_UNSPECIFIED,
)
.setEnabled(false)
.setPendingIntent(createTestActivityRedirectPendingIntent())
@@ -111,7 +111,7 @@ class SafetySourceTestData(private val context: Context) {
fun defaultInformationIssueBuilder(
id: String = INFORMATION_ISSUE_ID,
title: String = "Information issue title",
- summary: String = "Information issue summary"
+ summary: String = "Information issue summary",
) =
SafetySourceIssue.Builder(id, title, summary, SEVERITY_LEVEL_INFORMATION, ISSUE_TYPE_ID)
.addAction(action())
@@ -120,7 +120,7 @@ class SafetySourceTestData(private val context: Context) {
fun action(
id: String = INFORMATION_ISSUE_ACTION_ID,
label: String = "Review",
- pendingIntent: PendingIntent = createTestActivityRedirectPendingIntent()
+ pendingIntent: PendingIntent = createTestActivityRedirectPendingIntent(),
) = Action.Builder(id, label, pendingIntent).build()
/**
@@ -133,7 +133,7 @@ class SafetySourceTestData(private val context: Context) {
"Information issue title",
"Information issue summary",
SEVERITY_LEVEL_INFORMATION,
- ISSUE_TYPE_ID
+ ISSUE_TYPE_ID,
)
.setSubtitle("Information issue subtitle")
.addAction(action())
@@ -149,7 +149,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Unspecified title",
"Unspecified summary",
- SEVERITY_LEVEL_UNSPECIFIED
+ SEVERITY_LEVEL_UNSPECIFIED,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -167,7 +167,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Unspecified title for Work",
"Unspecified summary",
- SEVERITY_LEVEL_UNSPECIFIED
+ SEVERITY_LEVEL_UNSPECIFIED,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -185,7 +185,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Unspecified title for Private",
"Unspecified summary",
- SEVERITY_LEVEL_UNSPECIFIED
+ SEVERITY_LEVEL_UNSPECIFIED,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -292,7 +292,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Ok title for Work",
"Ok summary",
- SEVERITY_LEVEL_INFORMATION
+ SEVERITY_LEVEL_INFORMATION,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -310,7 +310,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Ok title for Private",
"Ok summary",
- SEVERITY_LEVEL_INFORMATION
+ SEVERITY_LEVEL_INFORMATION,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -339,20 +339,20 @@ class SafetySourceTestData(private val context: Context) {
fun defaultRecommendationIssueBuilder(
title: String = "Recommendation issue title",
summary: String = "Recommendation issue summary",
- confirmationDialog: Boolean = false
+ confirmationDialog: Boolean = false,
) =
SafetySourceIssue.Builder(
RECOMMENDATION_ISSUE_ID,
title,
summary,
SEVERITY_LEVEL_RECOMMENDATION,
- ISSUE_TYPE_ID
+ ISSUE_TYPE_ID,
)
.addAction(
Action.Builder(
RECOMMENDATION_ISSUE_ACTION_ID,
"See issue",
- createTestActivityRedirectPendingIntent()
+ createTestActivityRedirectPendingIntent(),
)
.apply {
if (confirmationDialog && SdkLevel.isAtLeastU()) {
@@ -418,7 +418,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Recommendation title",
"Recommendation summary",
- SEVERITY_LEVEL_RECOMMENDATION
+ SEVERITY_LEVEL_RECOMMENDATION,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -476,7 +476,7 @@ class SafetySourceTestData(private val context: Context) {
fun resolvingActionPendingIntent(
sourceId: String = SINGLE_SOURCE_ID,
sourceIssueId: String = CRITICAL_ISSUE_ID,
- sourceIssueActionId: String = CRITICAL_ISSUE_ACTION_ID
+ sourceIssueActionId: String = CRITICAL_ISSUE_ACTION_ID,
) =
broadcastPendingIntent(
Intent(ACTION_RESOLVE_ACTION)
@@ -498,7 +498,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
CRITICAL_ISSUE_ACTION_ID,
"Solve issue",
- criticalIssueActionPendingIntent(sourceId = sourceId)
+ criticalIssueActionPendingIntent(sourceId = sourceId),
)
.setWillResolve(true)
.build()
@@ -510,7 +510,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
CRITICAL_ISSUE_ACTION_ID,
"Solve issue",
- criticalIssueActionPendingIntent
+ criticalIssueActionPendingIntent,
)
.setWillResolve(true)
.setConfirmationDialogDetails(CONFIRMATION_DETAILS)
@@ -521,7 +521,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
CRITICAL_ISSUE_ACTION_ID,
"Redirect",
- createTestActivityRedirectPendingIntent()
+ createTestActivityRedirectPendingIntent(),
)
.build()
@@ -537,7 +537,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
CRITICAL_ISSUE_ACTION_ID,
"Solve issue",
- criticalIssueActionPendingIntent(sourceId = sourceId)
+ criticalIssueActionPendingIntent(sourceId = sourceId),
)
.setWillResolve(true)
.setSuccessMessage("Issue solved")
@@ -550,7 +550,7 @@ class SafetySourceTestData(private val context: Context) {
"Critical issue title",
"Critical issue summary",
SEVERITY_LEVEL_CRITICAL_WARNING,
- ISSUE_TYPE_ID
+ ISSUE_TYPE_ID,
)
.addAction(criticalResolvingActionWithSuccessMessage)
.build()
@@ -562,7 +562,7 @@ class SafetySourceTestData(private val context: Context) {
"Critical issue title",
"Critical issue summary",
SEVERITY_LEVEL_CRITICAL_WARNING,
- ISSUE_TYPE_ID
+ ISSUE_TYPE_ID,
)
.addAction(criticalResolvingActionWithSuccessMessage(sourceId = sourceId))
.build()
@@ -577,13 +577,13 @@ class SafetySourceTestData(private val context: Context) {
"Critical issue title 2",
"Critical issue summary 2",
SEVERITY_LEVEL_CRITICAL_WARNING,
- ISSUE_TYPE_ID
+ ISSUE_TYPE_ID,
)
.addAction(
Action.Builder(
CRITICAL_ISSUE_ACTION_ID,
"Go solve issue",
- createTestActivityRedirectPendingIntent()
+ createTestActivityRedirectPendingIntent(),
)
.build()
)
@@ -620,7 +620,7 @@ class SafetySourceTestData(private val context: Context) {
"Critical issue title",
"Critical issue summary",
SEVERITY_LEVEL_CRITICAL_WARNING,
- ISSUE_TYPE_ID
+ ISSUE_TYPE_ID,
)
.addAction(criticalResolvingAction(sourceId))
@@ -679,7 +679,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Critical title",
"Critical summary",
- SEVERITY_LEVEL_CRITICAL_WARNING
+ SEVERITY_LEVEL_CRITICAL_WARNING,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -811,7 +811,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Critical title",
"Critical summary",
- SEVERITY_LEVEL_CRITICAL_WARNING
+ SEVERITY_LEVEL_CRITICAL_WARNING,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -829,7 +829,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Critical title",
"Critical summary",
- SEVERITY_LEVEL_CRITICAL_WARNING
+ SEVERITY_LEVEL_CRITICAL_WARNING,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -854,7 +854,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceStatus.Builder(
"Critical title 2",
"Critical summary 2",
- SEVERITY_LEVEL_CRITICAL_WARNING
+ SEVERITY_LEVEL_CRITICAL_WARNING,
)
.setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
@@ -870,7 +870,7 @@ class SafetySourceTestData(private val context: Context) {
severityLevel: Int,
entrySummary: String,
withIssue: Boolean = false,
- entryTitle: String = "Entry title"
+ entryTitle: String = "Entry title",
) =
SafetySourceData.Builder()
.setStatus(
@@ -886,13 +886,13 @@ class SafetySourceTestData(private val context: Context) {
"Issue title",
"Issue summary",
max(severityLevel, SEVERITY_LEVEL_INFORMATION),
- ISSUE_TYPE_ID
+ ISSUE_TYPE_ID,
)
.addAction(
Action.Builder(
"action_id",
"Action",
- createTestActivityRedirectPendingIntent()
+ createTestActivityRedirectPendingIntent(),
)
.build()
)
@@ -907,7 +907,7 @@ class SafetySourceTestData(private val context: Context) {
context,
0,
intent.addFlags(FLAG_RECEIVER_FOREGROUND).setPackage(context.packageName),
- PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_IMMUTABLE,
)
companion object {
@@ -945,7 +945,7 @@ class SafetySourceTestData(private val context: Context) {
CONFIRMATION_TITLE,
CONFIRMATION_TEXT,
CONFIRMATION_YES,
- CONFIRMATION_NO
+ CONFIRMATION_NO,
)
/** A [SafetyEvent] to push arbitrary changes to Safety Center. */
@@ -979,7 +979,7 @@ class SafetySourceTestData(private val context: Context) {
context,
0 /* requestCode */,
intent,
- PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_IMMUTABLE,
)
}
}
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SettingsPackage.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SettingsPackage.kt
index 2ba127f4e..8fef70ce3 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SettingsPackage.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SettingsPackage.kt
@@ -38,7 +38,7 @@ object SettingsPackage {
PackageManager.MATCH_DIRECT_BOOT_AWARE or
PackageManager.MATCH_DIRECT_BOOT_UNAWARE)
.toLong()
- )
+ ),
)!!
.activityInfo
.packageName
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt
index 53ea34362..a4569b72b 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt
@@ -22,5 +22,5 @@ import android.service.notification.StatusBarNotification
/** Tuple of [StatusBarNotification] and the [NotificationChannel] it was posted to. */
data class StatusBarNotificationWithChannel(
val statusBarNotification: StatusBarNotification,
- val channel: NotificationChannel
+ val channel: NotificationChannel,
)
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt
index eceffb74f..12b4f7fd2 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt
@@ -52,10 +52,12 @@ class TestActivity : Activity() {
fun enableHighPriorityAlias() {
setAliasEnabledState(COMPONENT_ENABLED_STATE_ENABLED)
}
+
/** @see [enableHighPriorityAlias] */
fun disableHighPriorityAlias() {
setAliasEnabledState(COMPONENT_ENABLED_STATE_DISABLED)
}
+
private fun setAliasEnabledState(state: Int) {
val name =
ComponentName(getApplicationContext(), TestActivity::class.java.name + "Priority")
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt
index 21bf76fad..17b520349 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt
@@ -115,7 +115,7 @@ class TestNotificationListener : NotificationListenerService() {
*/
fun waitForSingleNotificationMatching(
characteristics: NotificationCharacteristics,
- timeout: Duration = TIMEOUT_LONG
+ timeout: Duration = TIMEOUT_LONG,
): StatusBarNotificationWithChannel {
return waitForNotificationsMatching(characteristics, timeout = timeout).first()
}
@@ -128,12 +128,12 @@ class TestNotificationListener : NotificationListenerService() {
*/
fun waitForNotificationsMatching(
vararg characteristics: NotificationCharacteristics,
- timeout: Duration = TIMEOUT_LONG
+ timeout: Duration = TIMEOUT_LONG,
): List<StatusBarNotificationWithChannel> {
val charsList = characteristics.toList()
return waitForNotificationsToSatisfy(
timeout = timeout,
- description = "notification(s) matching characteristics $charsList"
+ description = "notification(s) matching characteristics $charsList",
) {
NotificationCharacteristics.areMatching(it, charsList)
}
@@ -147,7 +147,7 @@ class TestNotificationListener : NotificationListenerService() {
*/
fun waitForSuccessNotification(
successMessage: String,
- onNotification: (StatusBarNotification) -> Unit = {}
+ onNotification: (StatusBarNotification) -> Unit = {},
) {
// Only wait for the notification event and don't wait for all notifications to "settle"
// as this notification is auto-cancelled after 10s; which can cause flakyness.
@@ -185,7 +185,7 @@ class TestNotificationListener : NotificationListenerService() {
timeout: Duration = TIMEOUT_LONG,
forAtLeast: Duration = TIMEOUT_SHORT,
description: String,
- predicate: (List<StatusBarNotificationWithChannel>) -> Boolean
+ predicate: (List<StatusBarNotificationWithChannel>) -> Boolean,
): List<StatusBarNotificationWithChannel> {
// First we wait at most timeout for the active notifications to satisfy the given
// predicate or otherwise we throw:
@@ -198,7 +198,7 @@ class TestNotificationListener : NotificationListenerService() {
throw AssertionError(
"Expected: $description, but notifications were " +
"${getSafetyCenterNotifications()} after waiting for $timeout",
- e
+ e,
)
}
@@ -298,7 +298,7 @@ class TestNotificationListener : NotificationListenerService() {
getInstanceOrThrow().cancelNotification(key)
waitForNotificationsToSatisfy(
timeout = TIMEOUT_LONG,
- description = "no notification with the key $key"
+ description = "no notification with the key $key",
) { notifications ->
notifications.none { it.statusBarNotification.key == key }
}
@@ -327,7 +327,7 @@ class TestNotificationListener : NotificationListenerService() {
throw IllegalStateException(
"Notification dismissal was not recorded in the issue cache: " +
dumpIssueDismissalsRepositoryState(),
- e
+ e,
)
}
}
@@ -362,7 +362,7 @@ class TestNotificationListener : NotificationListenerService() {
fun reset(context: Context) {
waitForNotificationsToSatisfy(
forAtLeast = Duration.ZERO,
- description = "all Safety Center notifications removed in tear down"
+ description = "all Safety Center notifications removed in tear down",
) {
it.isEmpty()
}
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt
index 0e062692a..c7d195528 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt
@@ -175,7 +175,7 @@ object UiTestHelper {
fun waitGroupShownOnHomepage(context: Context, group: SafetySourcesGroup) {
waitAllTextDisplayed(
context.getString(group.titleResId),
- context.getString(group.summaryResId)
+ context.getString(group.summaryResId),
)
}