summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--PermissionController/Android.bp2
-rw-r--r--PermissionController/AndroidManifest.xml1
-rw-r--r--PermissionController/res/drawable/ic_edit.xml24
-rw-r--r--PermissionController/res/layout-v33/action_button_list_large_screen.xml (renamed from PermissionController/res/layout-w764dp-v33/action_button_list.xml)2
-rw-r--r--PermissionController/res/layout-v33/action_button_list_small_screen.xml (renamed from PermissionController/res/layout-v33/spaced_preference_category_no_label.xml)4
-rw-r--r--PermissionController/res/layout-v33/preference_entries_top_padding.xml (renamed from PermissionController/res/layout-v33/action_button_list.xml)6
-rw-r--r--PermissionController/res/layout-v33/preference_issue_card.xml10
-rw-r--r--PermissionController/res/layout-v33/view_more_issues.xml59
-rw-r--r--PermissionController/res/layout/app_permission.xml47
-rw-r--r--PermissionController/res/layout/grant_permissions.xml4
-rw-r--r--PermissionController/res/layout/grant_permissions_material3.xml2
-rw-r--r--PermissionController/res/values-af-television/strings.xml4
-rw-r--r--PermissionController/res/values-af/strings.xml90
-rw-r--r--PermissionController/res/values-am/strings.xml6
-rw-r--r--PermissionController/res/values-ar-v34/strings.xml2
-rw-r--r--PermissionController/res/values-ar/strings.xml48
-rw-r--r--PermissionController/res/values-as/strings.xml6
-rw-r--r--PermissionController/res/values-az/strings.xml2
-rw-r--r--PermissionController/res/values-b+sr+Latn/strings.xml54
-rw-r--r--PermissionController/res/values-be/strings.xml8
-rw-r--r--PermissionController/res/values-bg/strings.xml6
-rw-r--r--PermissionController/res/values-bn/strings.xml8
-rw-r--r--PermissionController/res/values-bs/strings.xml12
-rw-r--r--PermissionController/res/values-ca/strings.xml14
-rw-r--r--PermissionController/res/values-cs-v33/strings.xml2
-rw-r--r--PermissionController/res/values-cs/strings.xml10
-rw-r--r--PermissionController/res/values-da/strings.xml52
-rw-r--r--PermissionController/res/values-de-v34/strings.xml4
-rw-r--r--PermissionController/res/values-de/strings.xml30
-rw-r--r--PermissionController/res/values-el-car/strings.xml2
-rw-r--r--PermissionController/res/values-el/strings.xml24
-rw-r--r--PermissionController/res/values-en-rAU/strings.xml6
-rw-r--r--PermissionController/res/values-en-rCA/strings.xml4
-rw-r--r--PermissionController/res/values-en-rGB/strings.xml6
-rw-r--r--PermissionController/res/values-en-rIN/strings.xml6
-rw-r--r--PermissionController/res/values-en-rXC/strings.xml2
-rw-r--r--PermissionController/res/values-es-rUS-v34/strings.xml2
-rw-r--r--PermissionController/res/values-es-rUS/strings.xml62
-rw-r--r--PermissionController/res/values-es-v33/strings.xml2
-rw-r--r--PermissionController/res/values-es/strings.xml22
-rw-r--r--PermissionController/res/values-et/strings.xml14
-rw-r--r--PermissionController/res/values-eu/strings.xml24
-rw-r--r--PermissionController/res/values-fa/strings.xml100
-rw-r--r--PermissionController/res/values-fi/strings.xml12
-rw-r--r--PermissionController/res/values-fr-rCA-car/strings.xml2
-rw-r--r--PermissionController/res/values-fr-rCA-television/strings.xml8
-rw-r--r--PermissionController/res/values-fr-rCA-v33/strings.xml6
-rw-r--r--PermissionController/res/values-fr-rCA-v34/strings.xml6
-rw-r--r--PermissionController/res/values-fr-rCA-watch/strings.xml2
-rw-r--r--PermissionController/res/values-fr-rCA/strings.xml388
-rw-r--r--PermissionController/res/values-fr-television/strings.xml2
-rw-r--r--PermissionController/res/values-fr/strings.xml28
-rw-r--r--PermissionController/res/values-gl/strings.xml16
-rw-r--r--PermissionController/res/values-gu/strings.xml14
-rw-r--r--PermissionController/res/values-hi-v34/strings.xml2
-rw-r--r--PermissionController/res/values-hi/strings.xml46
-rw-r--r--PermissionController/res/values-hr-v33/strings.xml2
-rw-r--r--PermissionController/res/values-hr/strings.xml28
-rw-r--r--PermissionController/res/values-hu/strings.xml2
-rw-r--r--PermissionController/res/values-hy-v34/strings.xml2
-rw-r--r--PermissionController/res/values-hy/strings.xml36
-rw-r--r--PermissionController/res/values-in-v33/strings.xml2
-rw-r--r--PermissionController/res/values-in/strings.xml22
-rw-r--r--PermissionController/res/values-is/strings.xml6
-rw-r--r--PermissionController/res/values-it-v33/strings.xml2
-rw-r--r--PermissionController/res/values-it/strings.xml44
-rw-r--r--PermissionController/res/values-iw/strings.xml16
-rw-r--r--PermissionController/res/values-ja-v33/strings.xml2
-rw-r--r--PermissionController/res/values-ja/strings.xml30
-rw-r--r--PermissionController/res/values-ka/strings.xml4
-rw-r--r--PermissionController/res/values-kk/strings.xml18
-rw-r--r--PermissionController/res/values-km-v34/strings.xml2
-rw-r--r--PermissionController/res/values-km/strings.xml10
-rw-r--r--PermissionController/res/values-kn/strings.xml8
-rw-r--r--PermissionController/res/values-ko/strings.xml10
-rw-r--r--PermissionController/res/values-ky/strings.xml24
-rw-r--r--PermissionController/res/values-lo/strings.xml2
-rw-r--r--PermissionController/res/values-lt/strings.xml4
-rw-r--r--PermissionController/res/values-lv/strings.xml4
-rw-r--r--PermissionController/res/values-mk/strings.xml14
-rw-r--r--PermissionController/res/values-ml-v33/strings.xml2
-rw-r--r--PermissionController/res/values-ml/strings.xml2
-rw-r--r--PermissionController/res/values-mn/strings.xml10
-rw-r--r--PermissionController/res/values-mr/strings.xml12
-rw-r--r--PermissionController/res/values-ms/strings.xml6
-rw-r--r--PermissionController/res/values-my-v34/strings.xml2
-rw-r--r--PermissionController/res/values-my/strings.xml30
-rw-r--r--PermissionController/res/values-nb/strings.xml4
-rw-r--r--PermissionController/res/values-ne/strings.xml114
-rw-r--r--PermissionController/res/values-night-v33/themes.xml12
-rw-r--r--PermissionController/res/values-night-v34/themes.xml5
-rw-r--r--PermissionController/res/values-nl/strings.xml52
-rw-r--r--PermissionController/res/values-or-television/strings.xml2
-rw-r--r--PermissionController/res/values-or/strings.xml14
-rw-r--r--PermissionController/res/values-pa-v33/strings.xml6
-rw-r--r--PermissionController/res/values-pa-v34/strings.xml2
-rw-r--r--PermissionController/res/values-pa/strings.xml30
-rw-r--r--PermissionController/res/values-pl/strings.xml10
-rw-r--r--PermissionController/res/values-pt-rBR-v34/strings.xml2
-rw-r--r--PermissionController/res/values-pt-rBR/strings.xml20
-rw-r--r--PermissionController/res/values-pt-rPT/strings.xml34
-rw-r--r--PermissionController/res/values-pt-v34/strings.xml2
-rw-r--r--PermissionController/res/values-pt/strings.xml20
-rw-r--r--PermissionController/res/values-ro/strings.xml8
-rw-r--r--PermissionController/res/values-ru-v34/strings.xml2
-rw-r--r--PermissionController/res/values-ru/strings.xml32
-rw-r--r--PermissionController/res/values-si/strings.xml2
-rw-r--r--PermissionController/res/values-sk/strings.xml34
-rw-r--r--PermissionController/res/values-sl/strings.xml8
-rw-r--r--PermissionController/res/values-sq/strings.xml4
-rw-r--r--PermissionController/res/values-sr/strings.xml54
-rw-r--r--PermissionController/res/values-sv/strings.xml8
-rw-r--r--PermissionController/res/values-sw-v34/strings.xml4
-rw-r--r--PermissionController/res/values-sw/strings.xml20
-rw-r--r--PermissionController/res/values-ta/strings.xml2
-rw-r--r--PermissionController/res/values-te/strings.xml16
-rw-r--r--PermissionController/res/values-th-v34/strings.xml4
-rw-r--r--PermissionController/res/values-th/strings.xml12
-rw-r--r--PermissionController/res/values-tl/strings.xml2
-rw-r--r--PermissionController/res/values-tr/strings.xml6
-rw-r--r--PermissionController/res/values-uk-v34/strings.xml2
-rw-r--r--PermissionController/res/values-uk/strings.xml28
-rw-r--r--PermissionController/res/values-ur/strings.xml8
-rw-r--r--PermissionController/res/values-uz/strings.xml10
-rw-r--r--PermissionController/res/values-v31/styles.xml10
-rw-r--r--PermissionController/res/values-v33/attrs.xml3
-rw-r--r--PermissionController/res/values-v33/dimens.xml9
-rw-r--r--PermissionController/res/values-v33/layout.xml9
-rw-r--r--PermissionController/res/values-v33/styles.xml59
-rw-r--r--PermissionController/res/values-v33/themes.xml24
-rw-r--r--PermissionController/res/values-v34/strings.xml2
-rw-r--r--PermissionController/res/values-vi-v33/strings.xml2
-rw-r--r--PermissionController/res/values-vi/strings.xml10
-rw-r--r--PermissionController/res/values-w764dp-v33/layout.xml6
-rw-r--r--PermissionController/res/values-w764dp-v33/styles.xml14
-rw-r--r--PermissionController/res/values-w764dp-v34/dimens.xml19
-rw-r--r--PermissionController/res/values-zh-rCN-v34/strings.xml2
-rw-r--r--PermissionController/res/values-zh-rCN/strings.xml24
-rw-r--r--PermissionController/res/values-zh-rHK-v34/strings.xml2
-rw-r--r--PermissionController/res/values-zh-rHK/strings.xml12
-rw-r--r--PermissionController/res/values-zh-rTW-car/strings.xml2
-rw-r--r--PermissionController/res/values-zh-rTW-v34/strings.xml4
-rw-r--r--PermissionController/res/values-zh-rTW/strings.xml52
-rw-r--r--PermissionController/res/values-zu/strings.xml10
-rw-r--r--PermissionController/res/values/overlayable.xml36
-rw-r--r--PermissionController/res/values/strings.xml7
-rw-r--r--PermissionController/res/values/styles.xml13
-rw-r--r--PermissionController/res/xml/roles.xml2
-rw-r--r--PermissionController/res/xml/safety_center_dashboard.xml7
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/compat/AppOpsManagerCompat.java48
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/AppOpPermissions.java4
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Permission.java61
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java6
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Role.java28
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java43
-rw-r--r--PermissionController/src/com/android/permissioncontroller/DumpableLog.kt36
-rw-r--r--PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt213
-rw-r--r--PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt743
-rw-r--r--PermissionController/src/com/android/permissioncontroller/hibernation/v31/HibernationController.kt23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/hibernation/v31/InstallerPackagesLiveData.kt12
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/AllPackageInfosLiveData.kt8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt19
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt224
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveData.kt61
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/AutoRevokedPackagesLiveData.kt26
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/BroadcastReceiverLiveData.kt57
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/CarrierPrivilegedStatusLiveData.kt13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/CustomPermGroupNamesLiveData.kt19
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt74
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/DisabledPrintServicesLiveData.kt23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/EnabledAccessibilityServicesLiveData.kt11
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDeviceAdminsLiveData.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDreamServicesLiveData.kt23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/EnabledInputMethodsLiveData.kt18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/EnabledNotificationListenersLiveData.kt24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/ForegroundPermNamesLiveData.kt13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt88
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/HasIntentAction.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt47
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt103
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/LauncherPackagesLiveData.kt34
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt77
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt92
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt37
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/LoadAndFreezeLifeData.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/MicMutedLiveData.kt50
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/OpUsageLiveData.kt114
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt49
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt54
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt31
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupUsageLiveData.kt56
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesLiveData.kt42
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt58
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt50
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermissionChange.kt10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermissionEvent.kt9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermissionListenerMultiplexer.kt15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PreinstalledUserPackageInfosLiveData.kt24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/SelectedAutofillServiceLiveData.kt20
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/SelectedVoiceInteractionServiceLiveData.kt24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/SelectedWallpaperServiceLiveData.kt22
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/ServiceLiveData.kt134
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/SinglePermGroupPackagesUiInfoLiveData.kt81
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/SmartAsyncMediatorLiveData.kt16
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt52
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/StandardPermGroupNamesLiveData.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/UnusedPackagesLiveData.kt39
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/UsageStatsLiveData.kt15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/UserSensitivityLiveData.kt118
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/UsersLiveData.kt21
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/v33/RecentPermissionDecisionsLiveData.kt8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/v34/AppDataSharingUpdatesLiveData.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt168
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermGroupInfo.kt17
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt25
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt46
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroup.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroupPackagesUiInfo.kt18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/UidSensitivityState.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt17
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt186
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/BasePermissionEventStorage.kt57
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/CheckLifecycleRegistry.kt10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/ExemptRestrictedPermission.kt13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt57
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt87
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventCleanupJobService.kt47
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorage.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorageImpls.kt11
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PermissionStorageTimeChangeReceiver.kt42
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PersistedStoragePackageUninstalledReceiver.kt10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt577
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt62
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java55
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt172
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoDividerPreference.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java11
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt168
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsViewAllFragment.kt42
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt27
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsPreference.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt47
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt25
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java47
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt351
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt21
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsWrapperFragment.kt17
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java42
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppPreference.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/Utils.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt78
-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/v31/PermissionUsageDetailsFragment.java12
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingDetailsPreference.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt27
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt89
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/AllAppPermissionsViewModel.kt53
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt419
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt971
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt1703
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt26
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt76
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt28
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt234
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt91
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/UnusedAppsViewModel.kt193
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt61
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt128
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt73
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt766
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionDecisionsViewModel.kt149
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt102
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsPreference.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/v33/widget/SafetyProtectionSectionView.kt18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt55
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt846
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt53
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/SystemTimeSource.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/TimeSource.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/UserSensitiveFlagsUtils.kt57
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java70
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt43
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt418
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/AutoRevokePrivacySource.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/LocationAccessPrivacySource.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt162
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerPregrants.kt42
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceData.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceStorageRepository.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/TextStorageRepository.kt18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/WorkPolicyInfo.kt36
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/v34/AppDataSharingUpdatesPrivacySource.kt13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java11
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java32
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java29
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java30
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java69
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppListFragment.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java (renamed from PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppPreference.java)23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java (renamed from PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSettingsPreference.java)33
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessFragment.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessListFragment.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java76
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppListPreferenceFragment.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppPreferenceFragment.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java75
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java (renamed from PermissionController/src/com/android/permissioncontroller/role/ui/handheld/SettingsButtonPreference.java)30
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java16
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java16
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessListPreferenceFragment.java8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessPreferenceFragment.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java74
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java33
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterJobServiceFlags.java13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterSearchIndexablesProvider.kt28
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableGroupCardHelper.kt9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreference.kt7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EntriesTopPaddingPreference.kt13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EqualWidthContainer.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt112
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt16
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ParsedSafetyCenterIntent.kt9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt17
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java46
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java26
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt30
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterTouchTarget.kt1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java20
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt59
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt31
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/TextFadeAnimator.kt19
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt39
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/LazyProperties.kt12
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/MoreIssuesHeaderView.kt15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryCommonViewsManager.kt10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryGroupView.kt24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryView.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistory.kt12
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistoryPersistence.kt60
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt23
-rw-r--r--PermissionController/tests/inprocess/AppThatUsesCameraPermission/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt3
-rw-r--r--PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/GetPermissionGroupInfoTest.kt10
-rw-r--r--PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveDataTest.kt6
-rw-r--r--PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/DataUtils.kt11
-rw-r--r--PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/ArrayUtilsTest.kt3
-rw-r--r--PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/KotlinUtilsTest.kt47
-rw-r--r--PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/PermissionMappingTest.kt34
-rw-r--r--PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt74
-rw-r--r--PermissionController/tests/mocking/Android.bp2
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationControllerTest.kt35
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt56
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/FakeEventStorage.kt4
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/v33/RecentPermissionDecisionsLiveDataTest.kt16
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/BasePermissionEventStorageTest.kt63
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionChangeStorageImplTest.kt63
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionEventCleanupJobServiceTest.kt31
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionStorageTimeChangeReceiverTest.kt54
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PersistedStoragePackageUninstalledReceiverTest.kt38
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/RuntimePermissionsUpgradeControllerTest.kt371
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/v33/PermissionDecisionStorageImplTest.kt33
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/handheld/v31/DashboardUtilsTest.kt138
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt164
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt520
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/StringUtilsTest.kt58
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AccessibilitySourceServiceTest.kt36
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AppDataSharingUpdatesPrivacySourceTest.kt39
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerCheckInternalTest.kt51
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPregrantsTest.kt8
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPrivacySourceTest.kt173
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt71
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/TextStorageRepositoryTest.kt52
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/WorkPolicyInfoTest.kt64
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt48
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt51
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryPersistenceTest.kt195
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryTest.kt84
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/SafetyLabelChangesJobServiceTest.kt10
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/TestSafetyLabels.kt20
-rw-r--r--PermissionController/tests/outofprocess/src/com/android/permissioncontroller/tests/outofprocess/DumpTest.kt8
-rw-r--r--PermissionController/tests/permissionui/Android.bp2
-rw-r--r--PermissionController/tests/permissionui/AndroidTest.xml4
-rw-r--r--PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/Android.bp34
-rw-r--r--PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/AndroidManifest.xml (renamed from PermissionController/res/values-w764dp-v33/dimens.xml)15
-rw-r--r--PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt21
-rw-r--r--PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/Android.bp34
-rw-r--r--PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/AndroidManifest.xml34
-rw-r--r--PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt21
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionGroupPreferenceUtils.kt22
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionHub2Test.kt48
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/UiUtils.kt10
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AllAppPermissionsFragmentTest.kt36
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AppPermissionFragmentTest.kt41
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/BasePermissionUiTest.kt14
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/CustomPermissionAppsFragmentTest.kt26
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt141
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt107
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/LocationPermissionAppsFragmentTest.kt12
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/PermissionAppsFragmentTest.kt23
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/StoragePermissionAppsFragmentTest.kt14
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt18
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/BaseHandheldPermissionUiTest.kt5
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt22
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt47
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ReviewOngoingUsageFragmentTest.kt17
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/v31/PermissionUsageFragmentTest.kt6
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsFragmentTest.kt41
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsOtherFragmentTest.kt38
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUiBaseTest.kt16
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUtils.kt27
-rw-r--r--SafetyCenter/Resources/res/values-ar/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-bs/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-eu-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-fa-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-fa/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-fi-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-fi/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-fr-rCA/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-fr/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-hi-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-hy-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-in-v34/strings.xml8
-rw-r--r--SafetyCenter/Resources/res/values-it/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-ja/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-kk-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-km-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-ky/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-mr-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-ms-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-my-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-my/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-pa-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-ru-v34/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-sr/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-sw-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-th-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-v34/config.xml2
-rw-r--r--SafetyCenter/Resources/res/values-zh-rCN-v34/strings.xml6
-rw-r--r--SafetyCenter/Resources/res/values-zh-rHK-v34/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml6
-rw-r--r--SafetyCenter/Resources/res/values/config.xml2
-rw-r--r--SafetyCenter/Resources/shared_res/values-af/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-am/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ar/strings.xml5
-rw-r--r--SafetyCenter/Resources/shared_res/values-as/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-az/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-be/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-bg/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-bn/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-bs/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ca/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-cs/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-da/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-de/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-el/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-es/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-et/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-eu/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-fa/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-fi/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-fr/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-gl/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-gu/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-hi/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-hr/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-hu/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-hy/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-in/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-is/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-it/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-iw/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ja/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ka/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-kk/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-km/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-kn/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ko/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ky/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-lo/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-lt/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-lv/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-mk/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ml/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-mn/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-mr/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ms/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-my/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-nb/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ne/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-nl/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-or/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-pa/strings.xml5
-rw-r--r--SafetyCenter/Resources/shared_res/values-pl/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-pt/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ro/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ru/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-si/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-sk/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-sl/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-sq/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-sr/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-sv/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-sw/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-ta/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-te/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-th/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-tl/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-tr/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-uk/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-ur/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-uz/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-vi/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml3
-rw-r--r--SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values-zu/strings.xml1
-rw-r--r--SafetyCenter/Resources/shared_res/values/strings.xml3
-rw-r--r--SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesApk.java353
-rw-r--r--SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesContext.java403
-rw-r--r--SafetyCenter/ResourcesLib/tests/AndroidTest.xml2
-rw-r--r--SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/res/raw/safety_center_config.txt1
-rw-r--r--SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesApkTest.kt440
-rw-r--r--SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesContextTest.kt226
-rw-r--r--SafetyCenter/TEST_MAPPING6
-rw-r--r--framework-s/Android.bp1
-rw-r--r--framework-s/java/android/app/role/RoleManager.java2
-rw-r--r--framework-s/java/android/safetycenter/SafetyCenter.md3
-rw-r--r--framework-s/java/android/safetycenter/SafetyCenterData.java12
-rw-r--r--framework-s/java/android/safetycenter/SafetyCenterIssue.java17
-rw-r--r--framework-s/java/android/safetycenter/SafetyCenterManager.java2
-rw-r--r--framework-s/java/android/safetycenter/SafetyEvent.java3
-rw-r--r--framework-s/java/android/safetycenter/SafetySourceData.java14
-rw-r--r--framework-s/java/android/safetycenter/SafetySourceIssue.java44
-rw-r--r--framework-s/java/android/safetycenter/SafetySourceStatus.java3
-rw-r--r--framework-s/java/android/safetycenter/config/SafetyCenterConfig.java3
-rw-r--r--framework-s/java/android/safetycenter/config/SafetySource.java24
-rw-r--r--framework-s/java/android/safetycenter/config/SafetySourcesGroup.java3
-rw-r--r--ktfmt_includes.txt1
-rw-r--r--permissions/Android.bp6
-rw-r--r--service/Android.bp1
-rw-r--r--service/java/com/android/safetycenter/ApiLock.java5
-rw-r--r--service/java/com/android/safetycenter/DevicePolicyResources.java22
-rw-r--r--service/java/com/android/safetycenter/PendingIntentFactory.java58
-rw-r--r--service/java/com/android/safetycenter/RefreshReasons.java11
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java68
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterConfigReader.java45
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterDataChangeNotifier.java5
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterDataFactory.java117
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterFlags.java137
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterListeners.java18
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java92
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterService.java441
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java14
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterTimeouts.java5
-rw-r--r--service/java/com/android/safetycenter/SafetySourceIssueInfo.java6
-rw-r--r--service/java/com/android/safetycenter/SafetySourceIssues.java87
-rw-r--r--service/java/com/android/safetycenter/SafetySourceKey.java6
-rw-r--r--service/java/com/android/safetycenter/SafetySources.java5
-rw-r--r--service/java/com/android/safetycenter/SafetySourcesGroups.java5
-rw-r--r--service/java/com/android/safetycenter/UserProfileGroup.java17
-rw-r--r--service/java/com/android/safetycenter/data/AndroidLockScreenFix.java152
-rw-r--r--service/java/com/android/safetycenter/data/DefaultActionOverrideFix.java180
-rw-r--r--service/java/com/android/safetycenter/data/SafetyCenterDataManager.java74
-rw-r--r--service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java36
-rw-r--r--service/java/com/android/safetycenter/data/SafetyCenterIssueDeduplicator.java12
-rw-r--r--service/java/com/android/safetycenter/data/SafetyCenterIssueDismissalRepository.java32
-rw-r--r--service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java18
-rw-r--r--service/java/com/android/safetycenter/data/SafetyEventFix.java118
-rw-r--r--service/java/com/android/safetycenter/data/SafetySourceDataFix.java76
-rw-r--r--service/java/com/android/safetycenter/data/SafetySourceDataOverrides.java81
-rw-r--r--service/java/com/android/safetycenter/data/SafetySourceDataRepository.java68
-rw-r--r--service/java/com/android/safetycenter/data/SafetySourceDataValidator.java20
-rw-r--r--service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java9
-rw-r--r--service/java/com/android/safetycenter/data/package-info.java5
-rw-r--r--service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java18
-rw-r--r--service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java6
-rw-r--r--service/java/com/android/safetycenter/logging/package-info.java5
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationChannels.java98
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationFactory.java120
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java39
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java41
-rw-r--r--service/java/com/android/safetycenter/notifications/package-info.java5
-rw-r--r--service/java/com/android/safetycenter/package-info.java5
-rw-r--r--tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt10
-rw-r--r--tests/cts/safetycenter/AndroidTest.xml3
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt62
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt80
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt177
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt39
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt4
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt16
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt204
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt4
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt4
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt10
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt8
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt44
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt33
-rw-r--r--tests/functional/safetycenter/multiusers/AndroidTest.xml3
-rw-r--r--tests/functional/safetycenter/multiusers/TEST_MAPPING27
-rw-r--r--tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt423
-rw-r--r--tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml3
-rw-r--r--tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt107
-rw-r--r--tests/functional/safetycenter/singleuser/AndroidManifest.xml9
-rw-r--r--tests/functional/safetycenter/singleuser/AndroidTest.xml3
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt1385
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt654
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterShellCommandsTest.kt (renamed from tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterShellCommandsTest.kt)46
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt298
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterQsActivityTest.kt92
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt108
-rw-r--r--tests/functional/safetycenter/subpages/Android.bp43
-rw-r--r--tests/functional/safetycenter/subpages/AndroidManifest.xml30
-rw-r--r--tests/functional/safetycenter/subpages/AndroidTest.xml64
-rw-r--r--tests/functional/safetycenter/subpages/TEST_MAPPING17
-rw-r--r--tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt (renamed from tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt)85
-rw-r--r--tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt (renamed from tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt)86
-rw-r--r--tests/hostside/safetycenter/Android.bp1
-rw-r--r--tests/hostside/safetycenter/AndroidTest.xml3
-rw-r--r--tests/hostside/safetycenter/helper-app/Android.bp1
-rw-r--r--tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt47
-rw-r--r--tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterNotificationLoggingHelperTests.kt43
-rw-r--r--tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt61
-rw-r--r--tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt131
-rw-r--r--tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterSystemEventReportedLoggingHostTest.kt57
-rw-r--r--tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetySourceStateCollectedLoggingHostTest.kt14
-rw-r--r--tests/hostside/safetycenter/src/android/safetycenter/hostside/rules/RequireSafetyCenterRule.kt28
-rw-r--r--tests/utils/safetycenter/AndroidManifest.xml27
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt1
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt72
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt (renamed from tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/NotificationCharacteristics.kt)37
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt18
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt16
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt66
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt71
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt65
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt25
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt57
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt14
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt38
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt250
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt (renamed from tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/StatusBarNotificationWithChannel.kt)2
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SupportsSafetyCenter.kt29
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SupportsSafetyCenterRule.kt50
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt34
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt (renamed from tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/TestNotificationListener.kt)155
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt85
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/WaitForBroadcasts.kt74
714 files changed, 18671 insertions, 12440 deletions
diff --git a/Android.bp b/Android.bp
index 4a436da6c..bf7437814 100644
--- a/Android.bp
+++ b/Android.bp
@@ -30,7 +30,6 @@ apex_defaults {
systemserverclasspath_fragments: ["com.android.permission-systemserverclasspath-fragment"],
prebuilts: [
"current_sdkinfo",
- "privapp_allowlist_com.android.permissioncontroller.xml",
],
key: "com.android.permission.key",
certificate: ":com.android.permission.certificate",
diff --git a/PermissionController/Android.bp b/PermissionController/Android.bp
index 9b20819f0..892f12f2b 100644
--- a/PermissionController/Android.bp
+++ b/PermissionController/Android.bp
@@ -82,7 +82,7 @@ android_app {
privileged: true,
certificate: "platform",
rename_resources_package: false,
- required: ["privapp_allowlist_com.android.permissioncontroller.xml"],
+ privapp_allowlist: ":privapp_allowlist_com.android.permissioncontroller.xml",
srcs: [":permissioncontroller-sources"],
diff --git a/PermissionController/AndroidManifest.xml b/PermissionController/AndroidManifest.xml
index 874ba35d6..c319e6ace 100644
--- a/PermissionController/AndroidManifest.xml
+++ b/PermissionController/AndroidManifest.xml
@@ -593,6 +593,7 @@
android:name="com.android.permissioncontroller.safetycenter.ui.SafetyCenterActivity"
android:enabled="@bool/is_at_least_t"
android:exported="true"
+ android:enableOnBackInvokedCallback="true"
android:theme="@style/Theme.SafetyCenter">
<intent-filter android:priority="1">
<action android:name="android.intent.action.SAFETY_CENTER"/>
diff --git a/PermissionController/res/drawable/ic_edit.xml b/PermissionController/res/drawable/ic_edit.xml
new file mode 100644
index 000000000..6b806a894
--- /dev/null
+++ b/PermissionController/res/drawable/ic_edit.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+android:width="24dp"
+android:height="24dp"
+android:viewportWidth="24"
+android:viewportHeight="24"
+android:tint="?android:attr/colorControlNormal">
+<path android:fillColor="#FFFFFFFF"
+ android:pathData="M5,19H6.4L16.45,8.975L15.75,8.25L15.025,7.55L5,17.6ZM3,21V16.75L16.45,3.325Q17.025,2.75 17.863,2.75Q18.7,2.75 19.275,3.325L20.675,4.75Q21.25,5.325 21.25,6.15Q21.25,6.975 20.675,7.55L7.25,21ZM19.25,6.15 L17.85,4.75ZM16.45,8.975 L15.75,8.25 15.025,7.55 16.45,8.975Z"/>
+</vector> \ No newline at end of file
diff --git a/PermissionController/res/layout-w764dp-v33/action_button_list.xml b/PermissionController/res/layout-v33/action_button_list_large_screen.xml
index 4db6df47c..141290fa2 100644
--- a/PermissionController/res/layout-w764dp-v33/action_button_list.xml
+++ b/PermissionController/res/layout-v33/action_button_list_large_screen.xml
@@ -16,6 +16,4 @@
<com.android.permissioncontroller.safetycenter.ui.EqualWidthContainer
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/action_button_list"
style="@style/SafetyCenterIssueActionButtonList" />
diff --git a/PermissionController/res/layout-v33/spaced_preference_category_no_label.xml b/PermissionController/res/layout-v33/action_button_list_small_screen.xml
index cecdc8bcd..048ac4595 100644
--- a/PermissionController/res/layout-v33/spaced_preference_category_no_label.xml
+++ b/PermissionController/res/layout-v33/action_button_list_small_screen.xml
@@ -14,5 +14,5 @@
~ limitations under the License.
-->
-<Space
- style="@style/SafetyCenterNoLabelPreferenceCategory"/> \ No newline at end of file
+<LinearLayout
+ style="@style/SafetyCenterIssueActionButtonList" />
diff --git a/PermissionController/res/layout-v33/action_button_list.xml b/PermissionController/res/layout-v33/preference_entries_top_padding.xml
index 3217f4c78..0e6e27148 100644
--- a/PermissionController/res/layout-v33/action_button_list.xml
+++ b/PermissionController/res/layout-v33/preference_entries_top_padding.xml
@@ -14,7 +14,7 @@
~ limitations under the License.
-->
-<LinearLayout
+<Space
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/action_button_list"
- style="@style/SafetyCenterIssueActionButtonList" />
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/sc_spacing_xsmall" /> \ No newline at end of file
diff --git a/PermissionController/res/layout-v33/preference_issue_card.xml b/PermissionController/res/layout-v33/preference_issue_card.xml
index 571efae3b..e6d749142 100644
--- a/PermissionController/res/layout-v33/preference_issue_card.xml
+++ b/PermissionController/res/layout-v33/preference_issue_card.xml
@@ -54,7 +54,7 @@
<include
android:id="@+id/issue_card_action_button_list"
- layout="@layout/action_button_list"/>
+ layout="?attr/scActionButtonListLayout"/>
<com.android.permissioncontroller.permission.ui.v33.widget.SafetyProtectionSectionView
android:id="@+id/issue_card_protected_by_android"
@@ -72,10 +72,10 @@
android:text="@string/safety_center_resolved_issue_fallback"
style="@style/SafetyCenterIssueCardResolvedTitle" />
- <!-- This group doesn't contain 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. -->
+ <!-- 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"
diff --git a/PermissionController/res/layout-v33/view_more_issues.xml b/PermissionController/res/layout-v33/view_more_issues.xml
index e1db725ad..7365e11c5 100644
--- a/PermissionController/res/layout-v33/view_more_issues.xml
+++ b/PermissionController/res/layout-v33/view_more_issues.xml
@@ -15,31 +15,58 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<ImageView
android:id="@+id/status_icon"
android:importantForAccessibility="no"
android:src="@drawable/safety_status_info"
- style="@style/SafetyCenterMoreIssuesIcon"/>
+ style="@style/SafetyCenterMoreIssuesIcon"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/title"/>
<TextView
- android:id="@+id/title"
- style="@style/SafetyCenterMoreIssuesTitle"/>
+ android:id="@id/title"
+ style="@style/SafetyCenterMoreIssuesTitle"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/status_icon"
+ app:layout_constraintEnd_toStartOf="@+id/widget_title"/>
- <LinearLayout
- android:id="@android:id/widget_frame"
- style="@style/SafetyCenterMoreIssuesCounter">
+ <View
+ android:id="@+id/widget_background"
+ style="@style/SafetyCenterMoreIssuesCounter"
+ app:layout_constraintTop_toTopOf="@id/widget_title"
+ app:layout_constraintBottom_toBottomOf="@id/widget_title"
+ app:layout_constraintStart_toStartOf="@id/widget_title"
+ app:layout_constraintEnd_toEndOf="parent"/>
- <TextView
- android:id="@+id/widget_title"
- style="@style/SafetyCenterMoreIssuesWidgetTitle" />
+ <TextView
+ android:id="@id/widget_title"
+ style="@style/SafetyCenterMoreIssuesWidgetTitle"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/title"
+ app:layout_constraintEnd_toStartOf="@+id/widget_icon"/>
+
+ <ImageView
+ android:id="@id/widget_icon"
+ android:importantForAccessibility="no"
+ style="@style/SafetyCenterMoreIssuesWidgetIcon"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/widget_title"
+ app:layout_constraintEnd_toEndOf="parent"/>
- <ImageView
- android:id="@+id/widget_icon"
- android:importantForAccessibility="no"
- style="@style/SafetyCenterMoreIssuesWidgetIcon" />
+ <androidx.constraintlayout.widget.Group
+ android:id="@+id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="visible"
+ app:constraint_referenced_ids="widget_background,widget_title,widget_icon"/>
- </LinearLayout>
-</merge> \ No newline at end of file
+</merge>
diff --git a/PermissionController/res/layout/app_permission.xml b/PermissionController/res/layout/app_permission.xml
index 6d68611fa..042cc7130 100644
--- a/PermissionController/res/layout/app_permission.xml
+++ b/PermissionController/res/layout/app_permission.xml
@@ -83,12 +83,6 @@
android:id="@+id/permission_message"
style="@style/AppPermissionMessage" />
- <RadioGroup
- android:id="@+id/radiogroup"
- android:animateLayoutChanges="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
<RadioButton
android:id="@+id/allow_radio_button"
android:text="@string/app_permission_button_allow"
@@ -104,10 +98,42 @@
android:text="@string/app_permission_button_allow_foreground"
style="@style/AppPermissionRadioButton" />
- <RadioButton
- android:id="@+id/select_radio_button"
- android:text="@string/app_permission_button_ask"
- style="@style/AppPermissionRadioButton" />
+ <RelativeLayout
+ android:id="@+id/radio_select_layout"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_marginTop="16dp"
+ android:layout_height="wrap_content">
+
+ <RadioButton
+ android:id="@+id/select_radio_button"
+ android:text="@string/grant_dialog_button_allow_limited_access"
+ android:layout_alignParentStart="true"
+ style="@style/AppPermissionRadioButton"
+ android:layout_marginTop="0dp" />
+
+ <View
+ android:id="@+id/edit_photos_divider"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_toStartOf="@id/edit_selected_button"
+ android:layout_alignParentTop="true"
+ android:layout_alignBottom="@+id/select_radio_button"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:theme="@style/PreferenceDivider"/>
+
+ <ImageButton
+ android:id="@+id/edit_selected_button"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:contentDescription="@string/edit_photos_description"
+ style="SettingsActionButton"
+ android:background="@null"
+ android:src="@drawable/ic_edit"/>
+ </RelativeLayout>
<RadioButton
android:id="@+id/ask_one_time_radio_button"
@@ -129,7 +155,6 @@
android:text="@string/app_permission_button_deny"
style="@style/AppPermissionRadioButton" />
- </RadioGroup>
<LinearLayout
android:layout_width="match_parent"
diff --git a/PermissionController/res/layout/grant_permissions.xml b/PermissionController/res/layout/grant_permissions.xml
index 9becddb7f..f01f00c60 100644
--- a/PermissionController/res/layout/grant_permissions.xml
+++ b/PermissionController/res/layout/grant_permissions.xml
@@ -143,7 +143,7 @@
<com.android.permissioncontroller.permission.ui.widget.SecureButton
android:id="@+id/permission_allow_selected_button"
- android:text="@string/grant_dialog_button_allow_selected_photos"
+ android:text="@string/grant_dialog_button_allow_limited_access"
style="@style/PermissionGrantButtonAllowSelected" />
<com.android.permissioncontroller.permission.ui.widget.SecureButton
@@ -184,7 +184,7 @@
<com.android.permissioncontroller.permission.ui.widget.SecureButton
android:id="@+id/permission_dont_allow_more_selected_button"
android:text="@string/grant_dialog_button_dont_select_more"
- style="@style/PermissionGrantButtonDeny" />
+ style="@style/PermissionGrantButtonDontAllowMore" />
</LinearLayout>
</LinearLayout>
diff --git a/PermissionController/res/layout/grant_permissions_material3.xml b/PermissionController/res/layout/grant_permissions_material3.xml
index d405976cf..6b1ccb2a4 100644
--- a/PermissionController/res/layout/grant_permissions_material3.xml
+++ b/PermissionController/res/layout/grant_permissions_material3.xml
@@ -144,7 +144,7 @@
<com.android.permissioncontroller.permission.ui.widget.SecureButton
android:id="@+id/permission_allow_selected_button"
- android:text="@string/grant_dialog_button_allow_selected_photos"
+ android:text="@string/grant_dialog_button_allow_limited_access"
style="@style/PermissionGrantButtonAllowSelectedMaterial3" />
<com.android.permissioncontroller.permission.ui.widget.SecureButton
diff --git a/PermissionController/res/values-af-television/strings.xml b/PermissionController/res/values-af-television/strings.xml
index 26a8bec01..fe101523c 100644
--- a/PermissionController/res/values-af-television/strings.xml
+++ b/PermissionController/res/values-af-television/strings.xml
@@ -20,8 +20,8 @@
<string name="grant_dialog_how_to_change" msgid="997462845048160559">"Jy kan dit later verander in Instellings &gt; Programme"</string>
<string name="current_permission_template" msgid="6240787325714651204">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
<string name="preference_show_system_apps" msgid="4262140518693221093">"Wys stelselprogramme"</string>
- <string name="app_permissions_decor_title" msgid="7438716722786036814">"Programtoestemmings"</string>
- <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Programtoestemmings"</string>
+ <string name="app_permissions_decor_title" msgid="7438716722786036814">"Apptoestemmings"</string>
+ <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Apptoestemmings"</string>
<string name="permission_apps_decor_title" msgid="2811550489429789828">"<xliff:g id="PERMISSION">%1$s</xliff:g>-toestemmings"</string>
<string name="additional_permissions_decor_title" msgid="5113847982502484225">"Bykomende toestemmings"</string>
<string name="system_apps_decor_title" msgid="4402004958937474803">"<xliff:g id="PERMISSION">%1$s</xliff:g>-toestemmings"</string>
diff --git a/PermissionController/res/values-af/strings.xml b/PermissionController/res/values-af/strings.xml
index 977651196..b7ff6f8a5 100644
--- a/PermissionController/res/values-af/strings.xml
+++ b/PermissionController/res/values-af/strings.xml
@@ -29,11 +29,12 @@
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"Program 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 program gebruik word\""</string>
+ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Hou \"Terwyl die app gebruik word\""</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Hou \"Net hierdie keer\""</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Meer inligting"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Laat alles toe"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Laat altyd alles toe"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Laat beperkte toegang toe"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Kies foto’ en video’s"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Kies meer"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Moenie meer kies nie"</string>
@@ -50,7 +51,7 @@
<string name="permission_revoked_none" msgid="9213345075484381180">"geen is gedeaktiveer nie"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"Laat toe"</string>
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"Laat altyd toe"</string>
- <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Terwyl die program gebruik word"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Terwyl die app gebruik word"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"Verander na presiese ligging"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"Hou benaderde ligging"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"Net hierdie keer"</string>
@@ -59,7 +60,8 @@
<string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"Laat toegang tot medialêers toe"</string>
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"Programtoestemmings"</string>
- <string name="unused_apps" msgid="2058057455175955094">"Ongebruikte programme"</string>
+ <string name="unused_apps" msgid="2058057455175955094">"Ongebruikte apps"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Redigeer geselekteerde foto’s vir hierdie app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Geen ongebruikte programme nie"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ongebruikte programme"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Onlangse toestemmingbesluite"</string>
@@ -69,8 +71,8 @@
<string name="granted_permission_decision" msgid="7824827491551861365">"Jy het vir <xliff:g id="APP_NAME">%1$s</xliff:g> toegang tot <xliff:g id="PERMISSION_NAME">%2$s</xliff:g> gegee"</string>
<string name="denied_permission_decision" msgid="5308961501779563781">"Jy het <xliff:g id="APP_NAME">%1$s</xliff:g> toegang tot <xliff:g id="PERMISSION_NAME">%2$s</xliff:g> geweier"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Vandag}=1{1 dag gelede}other{# dae gelede}}"</string>
- <string name="app_disable_dlg_positive" msgid="7418444149981904940">"Deaktiveer program"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"As jy hierdie program deaktiveer, sal Android en ander programme dalk nie meer soos bedoel werk nie. Hou in gedagte dat jy nie hierdie program kan uitvee nie, want dit is vooraf op jou toestel geïnstalleer. Deur dit te deaktiveer, skakel jy hierdie program af en versteek jy dit op jou toestel."</string>
+ <string name="app_disable_dlg_positive" msgid="7418444149981904940">"Deaktiveer app"</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"As jy hierdie app deaktiveer, sal Android en ander apps dalk nie meer soos bedoel werk nie. Hou in gedagte dat jy nie hierdie app kan uitvee nie, want dit is vooraf op jou toestel geïnstalleer. Deur dit te deaktiveer, skakel jy hierdie app af en versteek jy dit op jou toestel."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Toestemmingbestuurder"</string>
<string name="never_ask_again" msgid="4728762438198560329">"Moenie weer vra nie"</string>
<string name="no_permissions" msgid="3881676756371148563">"Geen toestemmings nie"</string>
@@ -88,7 +90,7 @@
<string name="menu_show_7_days_data" msgid="8979611198508523706">"Wys 7 dae"</string>
<string name="menu_show_24_hours_data" msgid="8228054833323380780">"Wys 24 uur"</string>
<string name="manage_permission" msgid="2895385393037061964">"Bestuur toestemming"</string>
- <string name="no_apps" msgid="2412612731628386816">"Geen programme nie"</string>
+ <string name="no_apps" msgid="2412612731628386816">"Geen apps nie"</string>
<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>
@@ -163,7 +165,7 @@
<string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Toestemminggebruik in afgelope 1 minuut"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nie in die afgelope # dag gebruik nie}other{Nie in die afgelope # dae gebruik nie}}"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nie in die afgelope # uur gebruik nie}other{Nie in die afgelope # uur gebruik nie}}"</string>
- <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Gebruik deur 1 program}other{Gebruik deur # programme}}"</string>
+ <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Gebruik deur 1 app}other{Gebruik deur # apps}}"</string>
<string name="permission_usage_view_details" msgid="6675335735468752787">"Sien alles in Dashboard"</string>
<string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Gefiltreer volgens: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"Verwyder filter"</string>
@@ -194,16 +196,16 @@
<string name="precise_image_description" msgid="6349638632303619872">"Presiese ligging"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Benaderde ligging"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Gebruik presiese ligging"</string>
- <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Wanneer presiese ligging af is, kan programme jou benaderde ligging bepaal"</string>
+ <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Wanneer presiese ligging af is, kan apps jou benaderde ligging bepaal"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g>-toestemming"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"Toegang tot <xliff:g id="PERM">%1$s</xliff:g> vir hierdie program"</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"Toegang tot <xliff:g id="PERM">%1$s</xliff:g> vir hierdie app"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Sien al <xliff:g id="APP">%1$s</xliff:g> se toestemmings"</string>
- <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Sien alle programme met hierdie toestemming"</string>
+ <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">"Ongebruikte programinstellings"</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="unused_apps_label" msgid="2595428768404901064">"Verwyder toestemmings en maak spasie beskikbaar"</string>
- <string name="unused_apps_label_v2" msgid="7058776770056517980">"Onderbreek programaktiwiteit as ongebruik"</string>
+ <string name="unused_apps_label_v2" msgid="7058776770056517980">"Onderbreek appaktiwiteit as ongebruik"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Verwyder toestemmings, vee tydelike lêers uit, en stop kennisgewings"</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_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>
@@ -218,10 +220,10 @@
<string name="auto_revoked_app_summary_one" msgid="7093213590301252970">"<xliff:g id="PERMISSION_NAME">%s</xliff:g>-toestemming verwyder"</string>
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"<xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g>- en <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g>-toestemming verwyder"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> en <xliff:g id="NUMBER">%2$s</xliff:g> ander toestemmings verwyder"</string>
- <string name="unused_apps_page_title" msgid="6986983535677572559">"Ongebruikte programme"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"As \'n program 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 program oop om weer toestemmings en kennisgewings toe te laat."</string>
+ <string name="unused_apps_page_title" msgid="6986983535677572559">"Ongebruikte apps"</string>
+ <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 # maande gelede laas oopgemaak}other{Meer as # maande gelede laas oopgemaak}}"</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_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>
@@ -232,8 +234,8 @@
<string name="permission_description_summary_call_log" msgid="7321437186317577624">"Programme met hierdie toestemming kan foonoproeprekord lees en skryf"</string>
<string name="permission_description_summary_camera" msgid="108004375101882069">"Programme met hierdie toestemming kan foto\'s neem en video\'s opneem"</string>
<string name="permission_description_summary_contacts" msgid="2337798886460408996">"Programme met hierdie toestemming kan toegang tot jou kontakte kry"</string>
- <string name="permission_description_summary_location" msgid="2817531799933480694">"Programme met hierdie toestemming kan toegang tot jou toestel se ligging kry"</string>
- <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Programme met hierdie toestemming kan toestelle in die omtrek soek, aan hulle koppel en hul relatiewe posisie bepaal"</string>
+ <string name="permission_description_summary_location" msgid="2817531799933480694">"Apps met hierdie toestemming kan toegang tot jou toestel se ligging kry"</string>
+ <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Apps met hierdie toestemming kan toestelle in die omtrek soek, aan hulle koppel en hul relatiewe posisie bepaal"</string>
<string name="permission_description_summary_microphone" msgid="630834800308329907">"Programme met hierdie toestemming kan oudio opneem"</string>
<string name="permission_description_summary_phone" msgid="4515277217435233619">"Programme met hierdie toestemming kan foonoproepe maak en bestuur"</string>
<string name="permission_description_summary_sensors" msgid="1836045815643119949">"Programme met hierdie toestemming kan toegang kry tot sensordata oor jou lewenstekens"</string>
@@ -302,7 +304,7 @@
<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>
<string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"Toestemmings sal verwyder word om jou privaatheid te beskerm. Tik om te kontroleer."</string>
- <string name="unused_apps_title" msgid="8589298917717872239">"Ongebruikte programme"</string>
+ <string name="unused_apps_title" msgid="8589298917717872239">"Ongebruikte apps"</string>
<string name="unused_apps_subtitle_after" msgid="2034267519506357898">"Toestemmings verwyder uit"</string>
<string name="unused_apps_subtitle_before" msgid="5233302577076132427">"Toestemmings sal verwyder word uit"</string>
<string name="unused_permissions_subtitle_two" msgid="2207266295008423015">"<xliff:g id="PERM_NAME_0">%1$s</xliff:g> en <xliff:g id="PERM_NAME_1">%2$s</xliff:g>"</string>
@@ -311,7 +313,7 @@
<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="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 program gebruik word"</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>
<string name="permission_subtitle_all_files" msgid="4982613338298067862">"Alle lêers"</string>
<string name="permission_subtitle_background" msgid="8916750995309083180">"Altyd toegelaat"</string>
@@ -347,23 +349,23 @@
<string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"<xliff:g id="NUM_SERVICES">%s</xliff:g> toeganklikheidprogramme het volle toegang tot jou toestel"</string>
<string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kan jou skerm, handelinge en invoere bekyk, handelinge uitvoer, en die skerm beheer."</string>
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Hierdie programme kan jou skerm, handelinge en invoere bekyk, handelinge uitvoer, en die skerm beheer."</string>
- <string name="role_assistant_label" msgid="4727586018198208128">"Verstekdigitaleassistentprogram"</string>
- <string name="role_assistant_short_label" msgid="3369003713187703399">"Digitaleassistentprogram"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"Bystandprogramme kan jou help op grond van inligting vanaf die skerm waarna jy kyk. Sommige programme steun sowel lanseerder- as steminvoerdienste om vir jou geïntegreerde bystand te gee."</string>
+ <string name="role_assistant_label" msgid="4727586018198208128">"Verstekdigitaleassistentapp"</string>
+ <string name="role_assistant_short_label" msgid="3369003713187703399">"Digitaleassistentapp"</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"Bystandapps kan jou help op grond van inligting vanaf die skerm waarna jy kyk. Sommige apps steun sowel lanseerder- as steminvoerdienste om vir jou geïntegreerde bystand te gee."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Verstekblaaier"</string>
- <string name="role_browser_short_label" msgid="6745009127123292296">"Blaaierprogram"</string>
- <string name="role_browser_description" msgid="3465253637499842671">"Programme wat jou toegang tot die internet gee en na vertoonskakels waarop jy tik"</string>
+ <string name="role_browser_short_label" msgid="6745009127123292296">"Blaaierapp"</string>
+ <string name="role_browser_description" msgid="3465253637499842671">"Apps wat jou toegang tot die internet gee en na vertoonskakels waarop jy tik"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstekblaaierprogram?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"Geen toestemmings is nodig nie"</string>
- <string name="role_dialer_label" msgid="1100224146343237968">"Verstekfoonprogram"</string>
- <string name="role_dialer_short_label" msgid="7186888549465352489">"Foonprogram"</string>
- <string name="role_dialer_description" msgid="8768708633696539612">"Programme wat jou toelaat om telefoonoproepe op jou toestel te maak en te ontvang"</string>
+ <string name="role_dialer_label" msgid="1100224146343237968">"Verstekfoonapp"</string>
+ <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_search_keywords" msgid="3324448983559188087">"beller"</string>
- <string name="role_sms_label" msgid="8456999857547686640">"Verstek-SMS-program"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"SMS-program"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Programme 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_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_search_keywords" msgid="8022048144395047352">"sms, teksboodskappe, boodskappe, boodskappe"</string>
@@ -373,9 +375,9 @@
<string name="role_emergency_request_title" msgid="8469579020654348567">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou versteknoodprogram?"</string>
<string name="role_emergency_request_description" msgid="131645948770262850">"Geen toestemmings is nodig nie"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"ice"</string>
- <string name="role_home_label" msgid="3871847846649769412">"Verstekhuisprogram"</string>
- <string name="role_home_short_label" msgid="8544733747952272337">"Huisprogram"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Programme, wat dikwels lanseerders genoem word, wat die Tuisskerms op jou Android-toestel vervang en vir jou toegang gee tot die inhoud en kenmerke van jou toestel"</string>
+ <string name="role_home_label" msgid="3871847846649769412">"Verstekhuisapp"</string>
+ <string name="role_home_short_label" msgid="8544733747952272337">"Tuis-app"</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Apps, wat dikwels lanseerders genoem word, wat die Tuisskerms op jou Android-toestel vervang en vir jou toegang gee tot die inhoud en kenmerke van jou toestel"</string>
<string name="role_home_request_title" msgid="738136983453341081">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstekhuisprogram?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Geen toestemmings is nodig nie"</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"lanseerder"</string>
@@ -384,9 +386,9 @@
<string name="role_call_redirection_description" msgid="6091669882014664420">"Programme wat jou toelaat om uitgaande oproepe na \'n ander foonnommer aan te stuur"</string>
<string name="role_call_redirection_request_title" msgid="2816244455003562925">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou oproepherleiding-verstekprogram?"</string>
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"Geen toestemmings is nodig nie"</string>
- <string name="role_call_screening_label" msgid="883935222060878724">"Verstekbeller-ID- en -strooiposprogram"</string>
- <string name="role_call_screening_short_label" msgid="2048465565063130834">"Beller-ID en strooiposprogram"</string>
- <string name="role_call_screening_description" msgid="2349431420497468981">"Programme wat dit vir jou moontlik maak om oproepe te identifiseer en strooipos, robotoproepe en ongewenste nommers te blokkeer"</string>
+ <string name="role_call_screening_label" msgid="883935222060878724">"Verstekbeller-ID- en -strooiposapp"</string>
+ <string name="role_call_screening_short_label" msgid="2048465565063130834">"Beller-ID- en strooiposapp"</string>
+ <string name="role_call_screening_description" msgid="2349431420497468981">"Apps wat dit vir jou moontlik maak om oproepe te identifiseer en strooipos, robotoproepe en ongewenste nommers te blokkeer"</string>
<string name="role_call_screening_request_title" msgid="7358309224566977290">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstekbeller-ID en -strooiposprogram?"</string>
<string name="role_call_screening_request_description" msgid="7338511921032446006">"Geen toestemmings is nodig nie"</string>
<string name="role_automotive_navigation_label" msgid="2701890757955474751">"Versteknavigasieprogram"</string>
@@ -418,23 +420,23 @@
<string name="ongoing_usage_dialog_title_mic_camera" msgid="9079747867228772797">"Onlangse gebruik van mikrofoon en kamera"</string>
<string name="ongoing_usage_dialog_separator" msgid="1715181526581520068">", "</string>
<string name="ongoing_usage_dialog_last_separator" msgid="4170995004748832163">" en "</string>
- <string name="default_app_search_keyword" msgid="8330125736889689743">"verstekprogramme"</string>
+ <string name="default_app_search_keyword" msgid="8330125736889689743">"verstekapps"</string>
<string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Mikrofoon en kamera"</string>
<string name="settings_button" msgid="4414988414732479636">"Instellings"</string>
- <string name="default_apps" msgid="5119201969348748639">"Verstekprogramme"</string>
+ <string name="default_apps" msgid="5119201969348748639">"Verstekapps"</string>
<string name="no_default_apps" msgid="2593466527182950231">"Geen verstekprogramme nie"</string>
<string name="default_apps_more" msgid="4078194675848858093">"Meer verstekke"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Oopmaak van skakels"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Verstek vir werk"</string>
<string name="default_app_none" msgid="9084592086808194457">"Geen"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Stelselverstek)"</string>
- <string name="default_app_no_apps" msgid="115720991680586885">"Geen programme nie"</string>
+ <string name="default_app_no_apps" msgid="115720991680586885">"Geen apps nie"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Gekies"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Gekies – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
<string name="special_app_access_search_keyword" msgid="8032347212290774210">"spesiale apptoegang"</string>
<string name="special_app_access" msgid="5019319067120213797">"Spesiale apptoegang"</string>
<string name="no_special_app_access" msgid="6950277571805106247">"Geen spesiale apptoegang nie"</string>
- <string name="special_app_access_no_apps" msgid="4102911722787886970">"Geen programme 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="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>
@@ -492,7 +494,7 @@
<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_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>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot sensordata oor jou lewenstekens?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Dié program wil dalk deurentyd toegang tot sensordata oor jou lewenstekens hê, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Gaan na instellings"</annotation>" om hierdie verandering te maak."</string>
+ <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="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>
@@ -517,7 +519,7 @@
<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>
- <string name="blocked_sensor_summary" msgid="4443707628305027375">"Vir programme en dienste"</string>
+ <string name="blocked_sensor_summary" msgid="4443707628305027375">"Vir apps en dienste"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Mikrofoondata kan steeds gedeel word wanneer jy \'n noodnommer bel."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Verander"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sekuriteit en privaatheid"</string>
@@ -581,7 +583,7 @@
<string name="mic_toggle_title" msgid="2649991093496110162">"Mikrofoontoegang"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Vir apps en dienste"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Vir apps en dienste. As hierdie instelling af is, kan mikrofoondata steeds gedeel word wanneer jy ’n noodnommer bel."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"Sien programme en dienste met toegang tot ligging"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"Sien apps en dienste met toegang tot ligging"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Wys knipbordtoegang"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Wys ’n boodskap wanneer apps toegang het tot teks, prente of ander inhoud wat jy gekopieer het"</string>
<string name="show_password_title" msgid="2877269286984684659">"Wys wagwoorde"</string>
@@ -611,7 +613,7 @@
<string name="data_sharing_updates_summary" msgid="764113985772233889">"Gaan apps na wat die manier waarop hulle jou liggingdata kan deel, verander het"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Hierdie apps het die manier waarop hulle jou liggingdata kan deel, verander. Hulle het dit dalk nie voorheen gedeel nie, of kan dit nou vir reklame- of bemarkingdoeleindes deel."</string>
<string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Die ontwikkelaars van hierdie apps het inligting oor hul datadelingpraktyke met ’n appwinkel gedeel. Hulle kan dit mettertyd opdateer.\n\nDatadelingpraktyke kan verskil op grond van jou appweergawe en -gebruik, streek en ouderdom."</string>
- <string name="learn_about_data_sharing" msgid="4200480587079488045">"Kom meer te wete oor datadeling"</string>
+ <string name="learn_about_data_sharing" msgid="4200480587079488045">"Meer inligting oor datadeling"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Jou liggingdata word nou met derde partye gedeel"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Jou liggingdata word nou met derde partye gedeel vir reklame of bemarking"</string>
<string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{In die afgelope dag opgedateer}=1{In die afgelope dag opgedateer}other{In die afgelope # dae opgedateer}}"</string>
diff --git a/PermissionController/res/values-am/strings.xml b/PermissionController/res/values-am/strings.xml
index aa5edc983..b09cb9f57 100644
--- a/PermissionController/res/values-am/strings.xml
+++ b/PermissionController/res/values-am/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"ተጨማሪ መረጃ"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"ሁሉንም ፍቀድ"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"ሁልጊዜ ሁሉንም ፍቀድ"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"የተገደበ መዳረሻ ፍቀድ"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ፎቶዎችን እና ቪድዮዎችን ምረጥ"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"ተጨማሪ ምረጥ"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"ተጨማሪ አትምረጥ"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"መተግበሪያዎች"</string>
<string name="app_permissions" msgid="3369917736607944781">"የመተግበሪያ ፈቃዶች"</string>
<string name="unused_apps" msgid="2058057455175955094">"ጥቅም ላይ ያልዋሉ መተግበሪያዎች"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"ለዚህ መተግበሪያ የተመረጡ ፎቶዎችን ያርትዑ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"አገልግሎት ላይ ያልዋሉ መተግበሪያዎች የሉም"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ሥራ ላይ ያልዋሉ መተግበሪያዎች"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"የቅርብ ጊዜ የፈቃድ ውሳኔዎች"</string>
@@ -483,9 +485,9 @@
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"ለ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የማይክራፎን መዳረሻ ይለወጥ?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"ይህ መተግበሪያ መተግበሪያውን በማይጠቀሙበት ጊዜም እንኳ ሁልጊዜ ኦዲዮ መቅዳት ይፈልጋል። "<annotation id="link">"በቅንብሮች ውስጥ ይፍቀዱ።"</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የእርስዎን አካላዊ እንቅስቃሴ እንዲደርስበት ይፈቀድለት?"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ስዕሎችን እንዲያነሳ እና ቪዲዮን እንዲቀርጽ ይፈቀድለት?"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ሥዕሎችን እንዲያነሳ እና ቪዲዮን እንዲቀርጽ ይፈቀድለት?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"መተግበሪያው ስዕሎችን ማንሳት እና ቪዲዮውን መቅዳት የሚችለው መተግበሪያውን እርስዎ ሲጠቀሙበት ብቻ ነው"</string>
- <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ስዕሎችን እንዲያነሳ እና ቪዲዮን እንዲቀርጽ ይፈቀድለት?"</string>
+ <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ሥዕሎችን እንዲያነሳ እና ቪዲዮን እንዲቀርጽ ይፈቀድለት?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"ይህ መተግበሪያ መተግበሪያውን በማይጠቀሙበት ጊዜም እንኳ ሁልጊዜ ሥዕሎችን ማንሳት እና ቪዲዮ መቅዳት ሊፈልግ ይችላል። "<annotation id="link">"በቅንብሮች ውስጥ ይፍቀዱ።"</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"ለ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የካሜራ መዳረሻ ይለወጥ?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"ይህ መተግበሪያ መተግበሪያውን በማይጠቀሙበት ጊዜም እንኳ ሁልጊዜ ሥዕሎችን ማንሳት እና ቪዲዮ መቅዳት ይፈልጋል። "<annotation id="link">"በቅንብሮች ውስጥ ይፍቀዱ።"</annotation></string>
diff --git a/PermissionController/res/values-ar-v34/strings.xml b/PermissionController/res/values-ar-v34/strings.xml
index 5a0d25e63..d8d01e5bc 100644
--- a/PermissionController/res/values-ar-v34/strings.xml
+++ b/PermissionController/res/values-ar-v34/strings.xml
@@ -22,6 +22,6 @@
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"إدارة إمكانية وصول التطبيقات إلى البيانات الصحية"</string>
<string name="location_settings" msgid="8863940440881290182">"الوصول إلى الموقع الجغرافي"</string>
- <string name="mic_toggle_description" msgid="1504101620086616040">"للتطبيقات والخدمات: إذا كان هذا الخيار غير مفعّل، ستستمر إمكانية مشاركة بيانات الميكروفون عند الاتصال برقم طوارئ."</string>
+ <string name="mic_toggle_description" msgid="1504101620086616040">"للتطبيقات والخدمات. إذا كان هذا الخيار غير مفعّل، قد تتم مشاركة بيانات الميكروفون عند الاتصال برقم طوارئ"</string>
<string name="location_settings_subtitle" msgid="6846532794702613851">"للتطبيقات والخدمات"</string>
</resources>
diff --git a/PermissionController/res/values-ar/strings.xml b/PermissionController/res/values-ar/strings.xml
index 8a6f4909d..f205d02a7 100644
--- a/PermissionController/res/values-ar/strings.xml
+++ b/PermissionController/res/values-ar/strings.xml
@@ -29,11 +29,12 @@
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"لم يتمّ العثور على التطبيق."</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"عدم السماح"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"عدم السماح وعدم طرح السؤال مرةً أخرى"</string>
- <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"عدم تغيير الإذن \"السماح فقط أثناء استخدام التطبيق\""</string>
+ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"عدم تغيير إذن \"السماح فقط أثناء استخدام التطبيق\""</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"مواصلة استخدام الإذن \"هذه المرة فقط\""</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"معلومات أكثر"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"السماح بالكل"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"السماح بالكل دومًا"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"السماح بالوصول المحدود إلى الصور والفيديوهات"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"اختيار صور وفيديوهات"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"اختيار المزيد"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"عدم اختيار المزيد"</string>
@@ -60,7 +61,8 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"تطبيقات"</string>
<string name="app_permissions" msgid="3369917736607944781">"أذونات التطبيقات"</string>
<string name="unused_apps" msgid="2058057455175955094">"التطبيقات غير المستخدمة"</string>
- <string name="no_unused_apps" msgid="12809387670415295">"ما مِن تطبيقات غير مستخدمة."</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"تغيير الصور المتاحة لهذا التطبيق"</string>
+ <string name="no_unused_apps" msgid="12809387670415295">"ما مِن تطبيقات غير مستخدمة"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"التطبيقات غير المستخدمة: 0"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"قرارات حديثة متعلقة بالأذونات"</string>
<string name="review_permission_decisions_view_all" msgid="90391040431566130">"عرض كل القرارات الحديثة المتعلقة بالأذونات"</string>
@@ -163,7 +165,7 @@
<string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"استخدام الإذن خلال الدقيقة الماضية"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{لم يتم استخدام الإذن في اليوم السابق.}zero{لم يتم استخدام الإذن في الأيام الـ # السابقة.}two{لم يتم استخدام الإذن في اليومين السابقين.}few{لم يتم استخدام الإذن في الأيام الـ # السابقة.}many{لم يتم استخدام الإذن في الأيام الـ # السابقة.}other{لم يتم استخدام الإذن في الأيام الـ # السابقة.}}"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{لم يتم استخدام الإذن خلال الساعة السابقة}zero{لم يتم استخدام الإذن خلال الساعات الـ # السابقة.}two{لم يتم استخدام الإذن خلال الساعتين السابقتين.}few{لم يتم استخدام الإذن خلال الساعات الـ # السابقة.}many{لم يتم استخدام الإذن خلال الساعات الـ # السابقة.}other{لم يتم استخدام الإذن خلال الساعات الـ # السابقة.}}"</string>
- <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{يستخدِمه تطبيق واحد.}zero{يستخدِمه # تطبيق.}two{يستخدِمه تطبيقان.}few{يستخدِمه # تطبيقات.}many{يستخدِمه # تطبيقًا.}other{يستخدِمه # تطبيق.}}"</string>
+ <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{يستخدِمه تطبيق واحد}zero{يستخدِمه # تطبيق}two{يستخدِمه تطبيقان}few{يستخدِمه # تطبيقات}many{يستخدِمه # تطبيقًا}other{يستخدِمه # تطبيق}}"</string>
<string name="permission_usage_view_details" msgid="6675335735468752787">"الاطّلاع على الكل في \"لوحة البيانات\""</string>
<string name="app_permission_usage_filter_label" msgid="7182861154638631550">"تمّت الفلترة حسب: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"إزالة الفلتر"</string>
@@ -194,7 +196,7 @@
<string name="precise_image_description" msgid="6349638632303619872">"الموقع الجغرافي الدقيق"</string>
<string name="approximate_image_description" msgid="938803699637069884">"الموقع الجغرافي التقريبي"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"استخدام الموقع الجغرافي الدقيق"</string>
- <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"عندما يكون الموقع الجغرافي الدقيق غير مفعّل، يمكن للتطبيقات الوصول إلى الموقع الجغرافي التقريبي."</string>
+ <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"التطبيقات يمكنها الوصول إلى الموقع الجغرافي التقريبي عندما يكون الموقع الجغرافي الدقيق غير مفعّل"</string>
<string name="app_permission_title" msgid="2090897901051370711">"إذن <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"منح هذا التطبيق الإذن بالوصول إلى <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"الاطّلاع على جميع أذونات تطبيق \"<xliff:g id="APP">%1$s</xliff:g>\""</string>
@@ -219,7 +221,7 @@
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"تمت إزالة إذنَي \"<xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g>\" و\"<xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g>\"."</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"تمت إزالة إذن \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\" وعدد <xliff:g id="NUMBER">%2$s</xliff:g> من الأذونات الأخرى."</string>
<string name="unused_apps_page_title" msgid="6986983535677572559">"التطبيقات غير المستخدمة"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"في حال عدم استخدام التطبيق لبضعة أشهر:\n\n• تتم إزالة الأذونات لحماية بياناتك.\n• يتم إيقاف الإشعارات لتوفير شحن البطارية.\n• تتم إزالة الملفات المؤقتة لتوفير مساحة.\n\nلمنح الأذونات والسماح للتطبيقات مرة أخرى، افتح التطبيق."</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"في حال عدم استخدام التطبيق لبضعة أشهر:\n\n• تتم إزالة الأذونات لحماية بياناتك\n• يتم إيقاف الإشعارات لتوفير شحن البطارية\n• تتم إزالة الملفات المؤقتة لتوفير مساحة\n\nلمنح الأذونات والسماح للتطبيقات مرة أخرى، افتح التطبيق."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"في حال عدم استخدام تطبيق لمدة شهر:\n\n• تتم إزالة الأذونات لحماية بياناتك.\n• تتم إزالة الملفات المؤقتة لإخلاء بعض المساحة.\n\nللسماح بالأذونات مرة أخرى، افتَح التطبيق."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{التطبيقات التي تم فتحها آخر مرة قبل أكثر من شهر واحد}zero{التطبيقات التي تم فتحها آخر مرة قبل أكثر من # شهر}two{التطبيقات التي تم فتحها آخر مرة قبل أكثر من شهرَين}few{التطبيقات التي تم فتحها آخر مرة قبل أكثر من # أشهر}many{التطبيقات التي تم فتحها آخر مرة قبل أكثر من # شهرًا}other{التطبيقات التي تم فتحها آخر مرة قبل أكثر من # شهر}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"آخر مرة تم فتح التطبيق فيها: <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -232,8 +234,8 @@
<string name="permission_description_summary_call_log" msgid="7321437186317577624">"يمكن للتطبيقات التي تحصل على هذا الإذن قراءة بيانات سجلّ مكالمات الهاتف وتعديلها."</string>
<string name="permission_description_summary_camera" msgid="108004375101882069">"يمكن للتطبيقات التي تملك هذا الإذن التقاط صور وتسجيل فيديوهات."</string>
<string name="permission_description_summary_contacts" msgid="2337798886460408996">"يمكن للتطبيقات التي لديها هذا الإذن الوصول إلى جهات الاتصال الخاصة بك."</string>
- <string name="permission_description_summary_location" msgid="2817531799933480694">"يمكن للتطبيقات التي لديها هذا الإذن الوصول إلى الموقع الجغرافي لهذا الجهاز."</string>
- <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"تتمكن التطبيقات التي لديها هذا الإذن من العثور على الأجهزة المجاورة والربط بها وتحديد موضعها النسبي."</string>
+ <string name="permission_description_summary_location" msgid="2817531799933480694">"يمكن للتطبيقات التي لديها هذا الإذن الوصول إلى الموقع الجغرافي لهذا الجهاز"</string>
+ <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"تتمكن التطبيقات التي لديها هذا الإذن من العثور على الأجهزة المجاورة والربط بها وتحديد موقعها النسبي."</string>
<string name="permission_description_summary_microphone" msgid="630834800308329907">"يمكن للتطبيقات التي لديها هذا الإذن تسجيل الصوت."</string>
<string name="permission_description_summary_phone" msgid="4515277217435233619">"يمكن للتطبيقات التي لديها هذا الإذن إجراء مكالمات هاتفية وإدارتها."</string>
<string name="permission_description_summary_sensors" msgid="1836045815643119949">"يمكن للتطبيقات التي لديها هذا الإذن أن تصل إلى بيانات جهاز الاستشعار الخاصة بمؤشراتك الحيوية."</string>
@@ -315,7 +317,7 @@
<string name="permission_subtitle_media_only" msgid="8917869683764720717">"الوسائط"</string>
<string name="permission_subtitle_all_files" msgid="4982613338298067862">"كل الملفات"</string>
<string name="permission_subtitle_background" msgid="8916750995309083180">"مسموح بالوصول إليه طوال الوقت"</string>
- <string name="app_perms_24h_access" msgid="99069906850627181">"تم استخدامه آخر مرة في <xliff:g id="TIME_DATE">%1$s</xliff:g>."</string>
+ <string name="app_perms_24h_access" msgid="99069906850627181">"تم استخدامه آخر مرة في <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
<string name="app_perms_24h_access_yest" msgid="5411926024794555022">"تم استخدامه آخر مرة أمس في <xliff:g id="TIME_DATE">%1$s</xliff:g>."</string>
<string name="app_perms_7d_access" msgid="4945055548894683751">"تم الوصول آخر مرة بتاريخ <xliff:g id="TIME_DATE_0">%1$s</xliff:g> في <xliff:g id="TIME_DATE_1">%2$s</xliff:g>."</string>
<string name="app_perms_content_provider_24h" msgid="1055526027667508972">"تم الوصول للأذونات في آخر 24 ساعة."</string>
@@ -335,7 +337,7 @@
<string name="app_perms_7d_access_all_files" msgid="8246193786397635824">"تم الوصول آخر مرة بتاريخ <xliff:g id="TIME_DATE_0">%1$s</xliff:g> في <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • جميع الملفات"</string>
<string name="app_perms_content_provider_24h_all_files" msgid="573104317727770850">"تم الوصول في آخر 24 ساعة • كل الملفات"</string>
<string name="app_perms_content_provider_7d_all_files" msgid="7962416229708835558">"تم الوصول في آخر 7 أيام • جميع الملفات"</string>
- <string name="no_permissions_allowed" msgid="6081976856354669209">"لم يتم منح أي أذونات."</string>
+ <string name="no_permissions_allowed" msgid="6081976856354669209">"لم يتم منح أي أذونات"</string>
<string name="no_permissions_denied" msgid="8159923922804043282">"لم يتم رفض أي أذونات."</string>
<string name="no_apps_allowed" msgid="7718822655254468631">"لم يتم السماح لأي تطبيقات."</string>
<string name="no_apps_allowed_full" msgid="8011716991498934104">"ما من تطبيقات تم منحها إذن الوصول إلى جميع الملفات."</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"يمكن لهذه التطبيقات عرض شاشتك وإجراءاتك ومدخلاتك وتنفيذ الإجراءات والتحكم في العرض."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"تطبيق المساعد الرقمي التلقائي"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"تطبيق المساعد الرقمي"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"بإمكان التطبيقات المساعِدة مساعدتك استنادًا إلى المعلومات على الشاشة التي تشاهدها. وتتوافق بعض التطبيقات مع كل من خدمة المشغّل وخدمة الإدخال الصوتي لتوفير مساعدة متكاملة لك."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"بإمكان التطبيقات المساعِدة مساعدتك استنادًا إلى المعلومات التي تظهر على شاشتك. وتعمل بعض التطبيقات مع كل من خدمة المشغّل وخدمة الإدخال الصوتي لتوفير مساعدة متكاملة لك."</string>
<string name="role_browser_label" msgid="2877796144554070207">"تطبيق المتصفّح التلقائي"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"تطبيق المتصفح"</string>
<string name="role_browser_description" msgid="3465253637499842671">"التطبيقات التي تتيح إمكانية الوصول إلى الإنترنت وتعرض روابط تنقر عليها"</string>
@@ -435,7 +437,7 @@
<string name="special_app_access" msgid="5019319067120213797">"أذونات خاصة للتطبيقات"</string>
<string name="no_special_app_access" msgid="6950277571805106247">"لا إذن وصول خاص إلى التطبيق."</string>
<string name="special_app_access_no_apps" msgid="4102911722787886970">"ليست هناك تطبيقات."</string>
- <string name="home_missing_work_profile_support" msgid="1756855847669387977">"لا يتوافق التطبيق مع الملف الشخصي للعمل."</string>
+ <string name="home_missing_work_profile_support" msgid="1756855847669387977">"لا يتوافق التطبيق مع ملف العمل."</string>
<string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"ملاحظة: في حال إعادة تشغيل جهازك وضبط قفل شاشة، لا يمكن بدء هذا التطبيق إلى أن تفتح جهازك."</string>
<string name="assistant_confirmation_message" msgid="7476540402884416212">"سيتمكّن المساعِد من قراءة المعلومات عن التطبيقات قيد الاستخدام على نظامك، بما في ذلك المعلومات المرئية على شاشتك أو التي يمكن الوصول إليها داخل التطبيقات."</string>
<string name="incident_report_channel_name" msgid="3144954065936288440">"مشاركة بيانات تصحيح الأخطاء"</string>
@@ -462,8 +464,8 @@
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"قد يطلب هذا التطبيق الوصول الدائم إلى موقعك الجغرافي، حتى عند عدم استخدامك للتطبيق. يمكنك "<annotation id="link">"السماح بذلك في الإعدادات."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"‏هل تريد تغيير إمكانية الوصول إلى الموقع الجغرافي بالنسبة إلى &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;؟"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"يطلب هذا التطبيق الوصول الدائم إلى موقعك الجغرافي، حتى عند عدم استخدامك للتطبيق. يمكنك "<annotation id="link">"السماح بذلك في الإعدادات."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالعثور على الأجهزة المجاورة والربط بها وتحديد موضعها النسبي؟"</string>
- <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالعثور على الأجهزة المجاورة والربط بها وتحديد موضعها النسبي؟ "<annotation id="link">"يمكنك السماح بذلك في \"الإعدادات\"."</annotation></string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالعثور على الأجهزة المجاورة والربط بها وتحديد موقعها النسبي؟"</string>
+ <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالعثور على الأجهزة المجاورة والربط بها وتحديد موقعها النسبي؟ "<annotation id="link">"يمكنك السماح بذلك في \"الإعدادات\"."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"هل تريد تغيير إذن وصول <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> من الموقع الجغرافي التقريبي إلى الموقع الجغرافي الدقيق؟"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الموقع الجغرافي التقريبي لهذا الجهاز؟"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"دقيق"</string>
@@ -471,9 +473,9 @@
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى التقويم؟"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بإرسال رسائل SMS وعرضها؟"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الصور والوسائط والملفات على جهازك؟"</string>
- <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"‏هل تسمح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى &lt;b&gt;الصور والفيديوهات والموسيقى والملفات الصوتية&lt;/b&gt; على هذا الجهاز؟"</string>
- <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‏هل تسمح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى &lt;b&gt;الصور والفيديوهات والموسيقى والملفات الصوتية وملفات أخرى&lt;/b&gt; على هذا الجهاز؟"</string>
- <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‏هل تريد السماح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى المقاطع الموسيقية والملفات الصوتية على هذا الجهاز؟"</string>
+ <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى &lt;b&gt;الصور والفيديوهات والموسيقى والملفات الصوتية&lt;/b&gt; على هذا الجهاز؟"</string>
+ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى &lt;b&gt;الصور والفيديوهات والموسيقى والملفات الصوتية وملفات أخرى&lt;/b&gt; على هذا الجهاز؟"</string>
+ <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الموسيقى والملفات الصوتية على هذا الجهاز؟"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‏هل تريد السماح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى الصور والفيديوهات على هذا الجهاز؟"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"‏هل تريد السماح بوصول تطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى المزيد من الصور والفيديوهات على هذا الجهاز؟"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بتسجيل الصوت؟"</string>
@@ -492,9 +494,9 @@
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى سجلّ مكالماتك الهاتفية؟"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بإجراء المكالمات الهاتفية وإدارتها؟"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى بيانات استشعار مؤشراتك الحيوية؟"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"يطلب هذا التطبيق الوصول الدائم إلى بيانات جهاز الاستشعار المتعلّقة بالمؤشرات الحيوية، حتى في حال عدم استخدامك للتطبيق. لإجراء هذا التغيير، "<annotation id="link">"انتقِل إلى الإعدادات."</annotation></string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"يطلب هذا التطبيق الوصول الدائم إلى بيانات استشعار المؤشرات الحيوية، حتى في حال عدم استخدامك للتطبيق. لإجراء هذا التغيير، "<annotation id="link">"انتقِل إلى الإعدادات."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى بيانات استشعار مؤشراتك الحيوية؟"</string>
- <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"للسماح لهذا التطبيق بالوصول إلى بيانات أجهزة استشعار الجسم دائمًا وحتى عند عدم استخدامك للتطبيق، "<annotation id="link">"انتقِل إلى الإعدادات"</annotation>"."</string>
+ <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"للسماح لهذا التطبيق بالوصول دائمًا إلى بيانات أجهزة استشعار الجسم حتى عند عدم استخدامك للتطبيق، "<annotation id="link">"انتقِل إلى الإعدادات"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"‏هل تريد مواصلة السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; عند استخدامه بالوصول إلى بيانات أجهزة استشعار الجسم؟"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بإرسال إشعارات إليك؟"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"الأذونات خاضعة لتحكّم المشرف"</string>
@@ -516,7 +518,7 @@
<string name="exempt_info_label" msgid="6286190981253476699">"‏تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> محمي بواسطة Android. لأن بياناتك تتم معالجتها على هذا الجهاز، لا يظهر استخدام إذن هذا التطبيق في لوحة بيانات الخصوصية."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"كاميرا الجهاز محظورة"</string>
<string name="blocked_microphone_title" msgid="1631517143648232585">"ميكروفون الجهاز محظور"</string>
- <string name="blocked_location_title" msgid="2005608279812892383">"ميزة الموقع الجغرافي للجهاز غير مفعّلة."</string>
+ <string name="blocked_location_title" msgid="2005608279812892383">"ميزة الموقع الجغرافي للجهاز غير مفعّلة"</string>
<string name="blocked_sensor_summary" msgid="4443707628305027375">"للتطبيقات والخدمات"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"لا يزال يمكن مشاركة بيانات الميكروفون عند الاتصال برقم طوارئ."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"تغيير"</string>
@@ -536,7 +538,7 @@
<string name="privacy_controls_qs" msgid="5780144882040591169">"عناصر التحكّم في خصوصيتك"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"إعدادات إضافية"</string>
<string name="camera_toggle_label_qs" msgid="3880261453066157285">"الوصول إلى الكاميرا"</string>
- <string name="microphone_toggle_label_qs" msgid="8132912469813396552">"الوصول إلى الميكروفون"</string>
+ <string name="microphone_toggle_label_qs" msgid="8132912469813396552">"الوصول للميكروفون"</string>
<string name="permissions_removed_qs" msgid="8957319130625294572">"تمت إزالة الإذن."</string>
<string name="camera_usage_qs" msgid="4394233566086665994">"عرض بيانات استخدام حديثة للكاميرا"</string>
<string name="microphone_usage_qs" msgid="8527666682168170417">"عرض بيانات استخدام حديثة للميكروفون"</string>
@@ -580,10 +582,10 @@
<string name="camera_toggle_title" msgid="1251201397431837666">"الوصول إلى الكاميرا"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"الوصول إلى الميكروفون"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"للتطبيقات والخدمات"</string>
- <string name="mic_toggle_description" msgid="9163104307990677157">"بالنسبة للتطبيقات والخدمات. إذا كان هذا الخيار غير مفعّل، قد يظل بالإمكان مشاركة بيانات الميكروفون عند الاتصال برقم طوارئ."</string>
+ <string name="mic_toggle_description" msgid="9163104307990677157">"للتطبيقات والخدمات. إذا كان هذا الخيار غير مفعّل، قد تتم مشاركة بيانات الميكروفون عند الاتصال برقم طوارئ"</string>
<string name="location_settings_subtitle" msgid="2328360561197430695">"عرض التطبيقات والخدمات التي يمكنها الوصول إلى الموقع الجغرافي"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"عرض إشعار عند الوصول إلى الحافظة"</string>
- <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"عرض رسالة عندما يصل التطبيق إلى نص أو صور أو محتوى آخر تم نسخه"</string>
+ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"عرض رسالة عندما يصل التطبيق إلى نص أو صور أو محتوى آخر نسخته"</string>
<string name="show_password_title" msgid="2877269286984684659">"عرض كلمات المرور"</string>
<string name="show_password_summary" msgid="1110166488865981610">"عرض الأحرف لفترة وجيزة أثناء الكتابة"</string>
<string name="permission_rationale_message_location" msgid="2153841534298068414">"وضَّح هذا التطبيق أنه يمكنه مشاركة بيانات الموقع الجغرافي مع جهات خارجية."</string>
@@ -615,7 +617,7 @@
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"تتم الآن مشاركة بيانات موقعك الجغرافي مع جهات خارجية."</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"تتم الآن مشاركة بيانات موقعك الجغرافي مع جهات خارجية بهدف الإعلان أو التسويق."</string>
<string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{تم التعديل خلال آخر 24 ساعة}=1{تم التعديل خلال آخر 24 ساعة}two{تم التعديل خلال آخر يومَين}few{تم التعديل خلال آخر # أيام}many{تم التعديل خلال آخر # يومًا}other{تم التعديل خلال آخر # يوم}}"</string>
- <string name="no_updates_at_this_time" msgid="9031085635689982935">"ما مِن تعديلات في الوقت الحالي."</string>
+ <string name="no_updates_at_this_time" msgid="9031085635689982935">"ما مِن تعديلات في الوقت الحالي"</string>
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"تعديلات مشاركة البيانات"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"غيّرت بعض التطبيقات الطريقة التي قد تشارك بها بيانات موقعك الجغرافي."</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"الإعدادات"</string>
diff --git a/PermissionController/res/values-as/strings.xml b/PermissionController/res/values-as/strings.xml
index 2656530e4..b998d8284 100644
--- a/PermissionController/res/values-as/strings.xml
+++ b/PermissionController/res/values-as/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"অধিক তথ্য"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"আটাইবোৰৰে অনুমতি দিয়ক"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"আটাইবোৰকে সদায় অনুমতি দিয়ক"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"সীমিত এক্সেছৰ অনুমতি দিয়ক"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ফট’ আৰু ভিডিঅ’ বাছনি কৰক"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"অধিক বাছনি কৰক"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"আৰু অধিক বাছনি নকৰিব"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"এপ্"</string>
<string name="app_permissions" msgid="3369917736607944781">"এপৰ অনুমতি"</string>
<string name="unused_apps" msgid="2058057455175955094">"অব্যৱহৃত এপ্‌সমূহ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"এই এপ্‌টোৰ বাবে বাছনি কৰা ফট’ সম্পাদনা কৰক"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ব্যৱহাৰ নকৰা কোনো এপ্‌ নাই"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"০ টা অব্যৱহৃত এপ্‌"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"শেহতীয়া অনুমতিৰ সিদ্ধান্তসমূহ"</string>
@@ -108,7 +110,7 @@
<!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) -->
<!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) -->
<string name="permission_access_always" msgid="1474641821883823446">"সদায় অনুমতি দিয়ক"</string>
- <string name="permission_access_only_foreground" msgid="7801170728159326195">"এপ্ ব্যৱহাৰ হৈ থাকোঁতে অনুমতি"</string>
+ <string name="permission_access_only_foreground" msgid="7801170728159326195">"কেৱল এপ্‌টো ব্যৱহাৰ কৰাৰ সময়তহে অনুমতি দিয়ক"</string>
<string name="permission_access_never" msgid="4647014230217936900">"অনুমতি নিদিব"</string>
<string name="loading" msgid="4789365003890741082">"ল’ড হৈ আছে…"</string>
<string name="all_permissions" msgid="6911125611996872522">"সকলো অনুমতি"</string>
@@ -362,7 +364,7 @@
<string name="role_dialer_request_description" msgid="6288839625724909320">"এই এপ্‌টোক আপোনাৰ কেমেৰা, সম্পৰ্ক, মাইক্ৰ’ফ’ন, ফ’ন আৰু এছএমএছৰ এক্সেছ দিয়া হ’ব"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"ডায়েলাৰ"</string>
<string name="role_sms_label" msgid="8456999857547686640">"ডিফ’ল্ট এছএমএছ এপ্"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"এছএমএছ এপ্"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"এছএমএছ এপ"</string>
<string name="role_sms_description" msgid="3424020199148153513">"যিবোৰ এপে আপোনাক নিজৰ ফ’ন নম্বৰ ব্যৱহাৰ কৰি চমু পাঠ বাৰ্তা, ফট’, ভিডিঅ’ আৰু অন্য সমল পঠিওৱাৰ অনুমতি দিয়ে"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক আপোনাৰ ডিফ’ল্ট এছএমএছ এপ্ হিচাপে ছেট কৰিবনে?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"এই এপ্‌টোক আপোনাৰ কেমেৰা, সম্পৰ্ক, ফাইল আৰু মিডিয়া, মাইক্ৰ’ফ’ন, ফ’ন আৰু এছএমএছৰ এক্সেছ দিয়া হ’ব"</string>
diff --git a/PermissionController/res/values-az/strings.xml b/PermissionController/res/values-az/strings.xml
index a517d458f..97790fe48 100644
--- a/PermissionController/res/values-az/strings.xml
+++ b/PermissionController/res/values-az/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Ətraflı məlumat"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Hamısına icazə verin"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Həmişə hamısına icazə verin"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Məhdud girişə icazə verin"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Foto və videolar seçin"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Digərlərini seçin"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Daha seçməyin"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Tətbiq"</string>
<string name="app_permissions" msgid="3369917736607944781">"Tətbiq icazələri"</string>
<string name="unused_apps" msgid="2058057455175955094">"İşlədilməyən tətbiqlər"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Bu tətbiq üçün seçilmiş fotoları redaktə edin"</string>
<string name="no_unused_apps" msgid="12809387670415295">"İstifadə olunmayan tətbiq yoxdur"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 istifadə olunmayan tətbiq"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Son icazə qərarları"</string>
diff --git a/PermissionController/res/values-b+sr+Latn/strings.xml b/PermissionController/res/values-b+sr+Latn/strings.xml
index 510680ef7..907edc1f5 100644
--- a/PermissionController/res/values-b+sr+Latn/strings.xml
+++ b/PermissionController/res/values-b+sr+Latn/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Više informacija"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Dozvoli sve"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Uvek dozvoli sve"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Dozvoli ograničen pristup"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Izaberite slike i video snimke"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Izaberite još"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Ništa više"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikacije"</string>
<string name="app_permissions" msgid="3369917736607944781">"Dozvole za aplikacije"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplikacije koje se ne koriste"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Menjaj izabrane slike za ovu aplikaciju"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nema aplik. koje se ne koriste"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 aplikac. koje se ne koriste"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nedavne odluke o dozvolama"</string>
@@ -93,7 +95,7 @@
<string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> pruža usluge lokacije za ovaj uređaj. Pristup lokaciji možete da izmenite u podešavanjima lokacije."</string>
<string name="system_warning" msgid="1173400963234358816">"Ako odbijete ovu dozvolu, osnovne funkcije uređaja možda neće više ispravno raditi."</string>
<string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Ova aplikacija je dizajnirana za stariju verziju Android-a. Ako ovoj aplikaciji odbijete pristup za slike i video snimke, povlači se i pristup muzici i drugim audio snimcima."</string>
- <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Ova aplikacija je dizajnirana za stariju verziju Android-a. Ako ovoj aplikaciji odbijete pristup za muziku i druge audio snimke, povlači se i pristup slikama i video snimcima."</string>
+ <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Ova aplikacija je dizajnirana za stariju verziju Android-a. Ako ovoj aplikaciji odbijete pristup za muziku i druge audio snimke, povlači se i pristup slikama i videima."</string>
<string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"Ako odbijete ovu dozvolu, neke funkcije uređaja kojima upravlja ova aplikacija možda neće više ispravno raditi."</string>
<string name="permission_summary_enforced_by_policy" msgid="4443598170942950519">"Primenjuje se u skladu sa smernicama"</string>
<string name="permission_summary_disabled_by_policy_background_only" msgid="221995005556362660">"Pristup u pozadini je onemogućen smernicama"</string>
@@ -240,7 +242,7 @@
<string name="permission_description_summary_sms" msgid="725999468547768517">"Aplikacije sa ovom dozvolom mogu da šalju i pregledaju SMS-ove"</string>
<string name="permission_description_summary_storage" msgid="6575759089065303346">"Aplikacije sa ovom dozvolom mogu da pristupaju slikama, medijskom sadržaju i fajlovima na uređaju"</string>
<string name="permission_description_summary_read_media_aural" msgid="3354728149930482199">"Aplikacije sa ovom dozvolom mogu da pristupaju muzici i drugim audio fajlovima na ovom uređaju"</string>
- <string name="permission_description_summary_read_media_visual" msgid="4991801977881732641">"Aplikacije sa ovom dozvolom mogu da pristupaju slikama i video snimcima na ovom uređaju"</string>
+ <string name="permission_description_summary_read_media_visual" msgid="4991801977881732641">"Aplikacije sa ovom dozvolom mogu da pristupaju slikama i videima na ovom uređaju"</string>
<string name="app_permission_most_recent_summary" msgid="4292074449384040590">"Poslednji pristup: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
<string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"Trenutno odbijeno/poslednji pristup: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
<string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Bez pristupa"</string>
@@ -373,7 +375,7 @@
<string name="role_emergency_request_title" msgid="8469579020654348567">"Želite li da podesite <xliff:g id="APP_NAME">%1$s</xliff:g> kao podrazumevanu aplikaciju za hitne slučajeve?"</string>
<string name="role_emergency_request_description" msgid="131645948770262850">"Nije potrebna nijedna dozvola"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"u hitnom slučaju"</string>
- <string name="role_home_label" msgid="3871847846649769412">"Podraz. apl. početne stranice"</string>
+ <string name="role_home_label" msgid="3871847846649769412">"Podrazumevana apl. početne stranice"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Aplikacija početne stranice"</string>
<string name="role_home_description" msgid="7997371519626556675">"Aplikacije koje se često zovu pokretači i zamenjuju početne ekrane na Android uređaju i pružaju pristup sadržaju i funkcijama na uređaju"</string>
<string name="role_home_request_title" msgid="738136983453341081">"Želite li da podesite <xliff:g id="APP_NAME">%1$s</xliff:g> kao podrazumevanu aplikaciju početne stranice?"</string>
@@ -385,13 +387,13 @@
<string name="role_call_redirection_request_title" msgid="2816244455003562925">"Želite li da podesite <xliff:g id="APP_NAME">%1$s</xliff:g> kao podrazumevanu aplikaciju za preusmeravanje poziva?"</string>
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"Nije potrebna nijedna dozvola"</string>
<string name="role_call_screening_label" msgid="883935222060878724">"Aplikacija za ID pozivaoca i nepoželjne poruke"</string>
- <string name="role_call_screening_short_label" msgid="2048465565063130834">"Apl. za ID poz. i nepož. poz."</string>
+ <string name="role_call_screening_short_label" msgid="2048465565063130834">"ID pozivaoca i nepoželjni pozivi"</string>
<string name="role_call_screening_description" msgid="2349431420497468981">"Aplikacije koje vam omogućavaju da identifikujete pozive, blokirate nepoželjne i automatizovane pozive i neželjene brojeve"</string>
<string name="role_call_screening_request_title" msgid="7358309224566977290">"Želite li da podesite <xliff:g id="APP_NAME">%1$s</xliff:g> kao podrazumevanu aplikaciju za ID pozivaoca i nepoželjne poruke?"</string>
<string name="role_call_screening_request_description" msgid="7338511921032446006">"Nije potrebna nijedna dozvola"</string>
<string name="role_automotive_navigation_label" msgid="2701890757955474751">"Podrazumevana aplikacija za navigaciju"</string>
<string name="role_automotive_navigation_short_label" msgid="5165823092506922457">"Aplikacija za navigaciju"</string>
- <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Aplikacije koje mogu da pružaju pretragu zanimljivih mesta i detaljnu pomoć pri navigaciji"</string>
+ <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Aplikacije koje mogu da pružaju pretragu tačaka od interesa i detaljnu pomoć pri navigaciji"</string>
<string name="role_automotive_navigation_request_title" msgid="7525693151489384300">"Želite da podesite aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> kao podrazumevanu aplikaciju za navigaciju?"</string>
<string name="role_automotive_navigation_request_description" msgid="7073023813249245540">"Nije potrebna nijedna dozvola"</string>
<string name="role_watch_description" msgid="267003778693177779">"<xliff:g id="APP_NAME">%1$s</xliff:g> će dobiti dozvolu za interakciju sa obaveštenjima i pristup dozvolama za telefon, SMS poruke, kontakte i kalendar."</string>
@@ -436,7 +438,7 @@
<string name="no_special_app_access" msgid="6950277571805106247">"Nema posebnog pristupa apl."</string>
<string name="special_app_access_no_apps" msgid="4102911722787886970">"Nema aplikacija"</string>
<string name="home_missing_work_profile_support" msgid="1756855847669387977">"Ne podržava poslovni profil"</string>
- <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Napomena: Ako restartujete uređaj i podesili ste zaključavanje ekrana, ova aplikacija ne može da se pokrene dok ne otključate uređaj."</string>
+ <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Napomena: Ako restartujete uređaj i podesili ste otključavanje ekrana, ova aplikacija ne može da se pokrene dok ne otključate uređaj."</string>
<string name="assistant_confirmation_message" msgid="7476540402884416212">"Pomoćnik će moći da čita informacije o aplikacijama koje se koriste u sistemu, uključujući informacije vidljive na ekranu ili kojima može da se pristupa u okviru aplikacija."</string>
<string name="incident_report_channel_name" msgid="3144954065936288440">"Deljenje podataka o otklanjanju grešaka"</string>
<string name="incident_report_notification_title" msgid="4635984625656519773">"Delite detaljne podatke za otklanjanje grešaka?"</string>
@@ -472,10 +474,10 @@
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; šalje i pregleda SMS-ove?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa slikama, medijskim i drugim fajlovima na uređaju?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Pristup &lt;b&gt;slikama, videu, muzici i zvuku&lt;/b&gt; na uređaju za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Pristup slikama, videu, muzici, zvuku i drugom na uređaju za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dozvoljavate li pristup muzici i zvuku na ovom uređaju za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dozvoljavate li pristup slikama i videu na ovom uređaju za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequest_more_photos" msgid="128933814654231321">"Dozvoljavate li da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa i drugim slikama i video snimcima na ovom uređaju?"</string>
+ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Dozvolićete pristup slikama, videu, muzici, zvuku i drugom na uređaju za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dozvolićete pristup muzici i zvuku na ovom uređaju za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dozvoljavate da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa slikama i videu na ovom uređaju?"</string>
+ <string name="permgrouprequest_more_photos" msgid="128933814654231321">"Dozvoljavate li da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa i drugim slikama i videima na ovom uređaju?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima zvuk?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći da snima zvuk samo dok koristite aplikaciju"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima zvuk?"</string>
@@ -483,16 +485,16 @@
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Želite da promenite pristup mikrofonu za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Ova aplikacija želi da snima zvuk sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Želite li da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa fizičkim aktivnostima?"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima slike i video snimke?"</string>
- <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikacija će moći da snima slike i video snimke samo dok koristite aplikaciju"</string>
- <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima slike i video snimke?"</string>
- <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ova aplikacija možda želi da snima slike i video snimke sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima slike i video?"</string>
+ <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikacija će moći da snima slike i video samo dok koristite aplikaciju"</string>
+ <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima slike i video?"</string>
+ <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ova aplikacija možda želi da snima slike i video sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Želite da promenite pristup kameri za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ova aplikacija želi da snima slike i video snimke sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string>
+ <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ova aplikacija želi da snima slike i video sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa evidencijama poziva na telefonu?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; upućuje pozive i upravlja njima?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa podacima senzora o vitalnim funkcijama?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ova aplikacija želi da sve vreme pristupa podacima senzora o vitalnim funkcijama, čak i kada ne koristite aplikaciju. Da biste obavili ovu izmenu, "<annotation id="link">"idite u podešavanja."</annotation></string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ova aplikacija želi da sve vreme pristupa podacima senzora o vitalnim funkcijama, čak i kada ne koristite aplikaciju. Da biste to promenili, "<annotation id="link">"idite u podešavanja."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Želite da omogućite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa podacima senzora o vitalnim funkcijama?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Da biste dozvolili ovoj aplikaciji da sve vreme pristupa podacima senzora za telo, čak i kada ne koristite aplikaciju, "<annotation id="link">"idite u podešavanja."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Želite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; i dalje pristupa podacima senzora za telo dok se aplikacija koristi?"</string>
@@ -558,18 +560,18 @@
<string name="media_confirm_dialog_title_a_to_p_aural_deny" msgid="7841428716317307685">"Ni pristup drugim fajlovima neće biti dozvoljen"</string>
<string name="media_confirm_dialog_title_a_to_p_visual_allow" msgid="6469086448310893751">"Biće dozvoljen pristup i drugim fajlovima"</string>
<string name="media_confirm_dialog_title_a_to_p_visual_deny" msgid="5767849609024384226">"Ni pristup drugim fajlovima neće biti dozvoljen"</string>
- <string name="media_confirm_dialog_title_q_to_s_aural_allow" msgid="3191904399336990537">"Biće dozvoljen pristup i slikama i video snimcima"</string>
- <string name="media_confirm_dialog_title_q_to_s_aural_deny" msgid="3128147568953297969">"Ni pristup slikama i video snimcima neće biti dozvoljen"</string>
+ <string name="media_confirm_dialog_title_q_to_s_aural_allow" msgid="3191904399336990537">"Biće dozvoljen pristup i slikama i videima"</string>
+ <string name="media_confirm_dialog_title_q_to_s_aural_deny" msgid="3128147568953297969">"Ni pristup slikama i videima neće biti dozvoljen"</string>
<string name="media_confirm_dialog_title_q_to_s_visual_allow" msgid="6310682466493330434">"Biće dozvoljen pristup i muzici i audio fajlovima"</string>
<string name="media_confirm_dialog_title_q_to_s_visual_deny" msgid="1123845663785900471">"Ni pristup muzici i audio fajlovima neće biti dozvoljen"</string>
- <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija može da pristupa muzici i audio fajlovima, biće joj dozvoljeno i da pristupa slikama, video snimcima i drugim fajlovima."</string>
- <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija ne može da pristupa muzici i audio fajlovima, neće joj biti dozvoljeno ni da pristupa slikama, video snimcima i drugim fajlovima."</string>
- <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija može da pristupa slikama i video snimcima, biće joj dozvoljeno i da pristupa muzici, audio i drugim fajlovima."</string>
- <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija ne može da pristupa slikama i video snimcima, neće joj biti dozvoljeno ni da pristupa muzici, audio i drugim fajlovima."</string>
- <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija može da pristupa muzici i audio fajlovima, biće joj dozvoljeno i da pristupa slikama i video snimcima."</string>
- <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija ne može da pristupa muzici i audio fajlovima, neće joj biti dozvoljeno ni da pristupa slikama i video snimcima."</string>
- <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija može da pristupa slikama i video snimcima, biće joj dozvoljeno i da pristupa muzici i audio fajlovima."</string>
- <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija ne može da pristupa muzici i audio fajlovima, neće joj biti dozvoljeno ni da pristupa slikama i video snimcima."</string>
+ <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija može da pristupa muzici i audio fajlovima, biće joj dozvoljeno i da pristupa slikama, videima i drugim fajlovima."</string>
+ <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija ne može da pristupa muzici i audio fajlovima, neće joj biti dozvoljeno ni da pristupa slikama, videima i drugim fajlovima."</string>
+ <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija može da pristupa slikama i videima, biće joj dozvoljeno i da pristupa muzici, audio i drugim fajlovima."</string>
+ <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija ne može da pristupa slikama i videima, neće joj biti dozvoljeno ni da pristupa muzici, audio i drugim fajlovima."</string>
+ <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija može da pristupa muzici i audio fajlovima, biće joj dozvoljeno i da pristupa slikama i videima."</string>
+ <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija ne može da pristupa muzici i audio fajlovima, neće joj biti dozvoljeno ni da pristupa slikama i videima."</string>
+ <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija može da pristupa slikama i videima, biće joj dozvoljeno i da pristupa muzici i audio fajlovima."</string>
+ <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Ova aplikacija ne podržava najnoviju verziju Android-a. Ako ova aplikacija ne može da pristupa muzici i audio fajlovima, neće joj biti dozvoljeno ni da pristupa slikama i videima."</string>
<string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"Pregledajte aplikaciju sa pristupom lokaciji u pozadini"</string>
<string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> uvek može da pristupa vašoj lokaciji, čak i kad je aplikacija zatvorena"</string>
<string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Pregledajte aplikaciju sa pristupom lokaciji u pozadini"</string>
diff --git a/PermissionController/res/values-be/strings.xml b/PermissionController/res/values-be/strings.xml
index c6e081982..0c7b41c1d 100644
--- a/PermissionController/res/values-be/strings.xml
+++ b/PermissionController/res/values-be/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Падрабязней"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Дазволіць усе"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Заўсёды дазваляць усе"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Дазволіць абмежаваны доступ"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Выбраць фота і відэа"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Яшчэ"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Больш не выбіраць"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Праграмы"</string>
<string name="app_permissions" msgid="3369917736607944781">"Дазволы праграмы"</string>
<string name="unused_apps" msgid="2058057455175955094">"Праграмы, якія не выкарыстоўваюцца"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Змяніць спіс фота, да якіх гэта праграма мае доступ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Няма нескарыстаных праграм"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 праграм не ў карыстанні"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Нядаўнія рашэнні наконт дазволаў"</string>
@@ -90,7 +92,7 @@
<string name="manage_permission" msgid="2895385393037061964">"Кіраваць дазволам"</string>
<string name="no_apps" msgid="2412612731628386816">"Няма праграм"</string>
<string name="location_settings" msgid="3624412509133422562">"Налады месцазнаходжання"</string>
- <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> з\'яўляецца службай геалакацыі для гэтай прылады. Доступ да вызначэння месцазнаходжання можна змяніць у наладах."</string>
+ <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> з’яўляецца службай геалакацыі для гэтай прылады. Доступ да вызначэння месцазнаходжання можна змяніць у наладах."</string>
<string name="system_warning" msgid="1173400963234358816">"Калі вы адхіліце гэты дазвол, асноўныя функцыі прылады могуць перастаць працаваць належным чынам."</string>
<string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Гэта праграма створана для ранейшай версіі Android. Забараніўшы праграме доступ да фота і відэа, вы таксама закрыеце доступ да музыкі і іншых аўдыяфайлаў."</string>
<string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Гэта праграма створана для ранейшай версіі Android. Забараніўшы праграме доступ да музыкі і іншых аўдыяфайлаў, вы таксама закрыеце доступ да фота і відэа."</string>
@@ -362,14 +364,14 @@
<string name="role_dialer_request_description" msgid="6288839625724909320">"Гэта праграма атрымае доступ да камеры, кантактаў, мікрафона, тэлефона і SMS"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"набіральнік нумара"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Стандартная праграма для SMS"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"Праграма для SMS"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"праграма для SMS"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Праграмы, якія дазваляюць вам выкарыстоўваць нумар тэлефона для адпраўкі і атрымання кароткіх тэкставых паведамленняў, фота, відэа і іншага"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Зрабіць \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" стандартнай праграмай для SMS?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Гэта праграма атрымае доступ да камеры, кантактаў, файлаў і мультымедыя, мікрафона, тэлефона і SMS"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"тэкставае паведамленне, SMS, паведамленні, абмен паведамленнямі"</string>
<string name="role_emergency_label" msgid="7028825857206842366">"Праграма экстраннай дапамогі"</string>
<string name="role_emergency_short_label" msgid="2388431453335350348">"Праграма экстраннай дапамогі"</string>
- <string name="role_emergency_description" msgid="5051840234887686630">"Праграмы, якія даюць вам магчымасць: запісаць медыцынскую інфармацыю і зрабіць яе даступнай для экстраннай службы; атрымліваць абвесткі пра стыхійныя бедствы і складаныя ўмовы надвор\'я; апавяшчаць іншых пра тое, што вам патрабуецца дапамога"</string>
+ <string name="role_emergency_description" msgid="5051840234887686630">"Праграмы, якія даюць вам магчымасць: запісаць медыцынскую інфармацыю і зрабіць яе даступнай для экстраннай службы; атрымліваць абвесткі пра стыхійныя бедствы і складаныя ўмовы надвор’я; апавяшчаць іншых пра тое, што вам патрабуецца дапамога"</string>
<string name="role_emergency_request_title" msgid="8469579020654348567">"Прызначыць \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" стандартнай праграмай экстраннай дапамогі?"</string>
<string name="role_emergency_request_description" msgid="131645948770262850">"Дазволы не патрэбныя"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"у выпадку надзвычайнай сітуацыі"</string>
diff --git a/PermissionController/res/values-bg/strings.xml b/PermissionController/res/values-bg/strings.xml
index a8c0e6315..382d7b7b6 100644
--- a/PermissionController/res/values-bg/strings.xml
+++ b/PermissionController/res/values-bg/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Още информация"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Разрешаване на пълен достъп"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Винаги да се разрешава пълен достъп"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Разрешаване на ограничения достъп"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Избиране на снимки и видеоклипове"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Избиране на още"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Без избиране на още"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Приложения"</string>
<string name="app_permissions" msgid="3369917736607944781">"Разрешения за приложенията"</string>
<string name="unused_apps" msgid="2058057455175955094">"Неизползвани приложения"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Редактиране на избраните снимки за това приложение"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Няма неизползвани приложения"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 неизползвани приложения"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Скорошни решения за разрешения"</string>
@@ -471,7 +473,7 @@
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до календара ви?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да изпраща и преглежда SMS съобщения?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да има достъп до снимките, мултимедията и файловете на устройството ви?"</string>
- <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Разрешаване на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; достъп до &lt;b&gt;снимките, видео- и аудиосъдържанието&lt;/b&gt; на устройството ви?"</string>
+ <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; достъп до &lt;b&gt;снимките, видео- и аудиосъдържанието&lt;/b&gt; на устройството?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Разр. на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; достъп до &lt;b&gt;снимките, видео- и аудиосъдърж. и другите файлове&lt;/b&gt; на у-вото?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до музиката и аудиофайловете на това устройство?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до снимките и видеоклиповете на това устройство?"</string>
@@ -495,7 +497,7 @@
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Това приложение иска постоянен достъп до данните от сензорите за жизнените ви показатели – дори когато не го използвате. За да извършите тази промяна, "<annotation id="link">"отворете настройките"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до данните от сензорите за жизнените ви показатели?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"За да разрешите на това приложение да осъществява достъп до данните от сензорите за тяло по всяко време – дори когато не го използвате, "<annotation id="link">"отворете настройките"</annotation>"."</string>
- <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Да се предоставя ли достъп на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до данните от сензорите за тяло, докато приложението се използва?"</string>
+ <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Да се запази ли достъпът на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до данните от сензорите за тяло, докато приложението се използва?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да ви изпраща известия?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Контролирани разрешения"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> има достъп до местоположението"</string>
diff --git a/PermissionController/res/values-bn/strings.xml b/PermissionController/res/values-bn/strings.xml
index ca112b542..7703875f3 100644
--- a/PermissionController/res/values-bn/strings.xml
+++ b/PermissionController/res/values-bn/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"আরও তথ্য"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"সব অনুমোদন করুন"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"সবসময় সব অনুমতি দিন"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"সীমিত অ্যাক্সেসের অনুমতি দিন"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ফটো এবং ভিডিও বেছে নিন"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"আরও বেছে নিন"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"আর বেছে নেবেন না"</string>
@@ -44,13 +45,13 @@
<string name="permission_add_background_warning_template" msgid="1812914855915092273">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপটিকে সব সময় এটি করার অনুমতি দেবেন?: <xliff:g id="ACTION">%2$s</xliff:g>"</string>
<string name="allow_permission_foreground_only" msgid="116465816039675404">"শুধুমাত্র অ্যাপ ব্যবহার করার সময়"</string>
<string name="allow_permission_always" msgid="5194342531206054051">"সব সময়"</string>
- <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"অনুমতি দেবেন না এবং আর জিজ্ঞাসা করবেন না"</string>
+ <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"অনুমতি দেবেন না ও আবার জিজ্ঞাসা করা হোক তা চান না"</string>
<string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g>টি বন্ধ করা হয়েছে"</string>
<string name="permission_revoked_all" msgid="3397649017727222283">"সবগুলি বন্ধ করা হয়েছে"</string>
<string name="permission_revoked_none" msgid="9213345075484381180">"কোনওটিই বন্ধ করা হয়নি"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"অনুমতি দিন"</string>
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"সর্বদা অনুমতি দিন"</string>
- <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"অ্যাপ ব্যবহার করার সময়"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"শুধুমাত্র অ্যাপ ব্যবহার করার সময়"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"সুনির্দিষ্ট লোকেশনে পরিবর্তন করুন"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"আনুমানিক লোকেশন রাখুন"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"শুধুমাত্র এই সময়ে"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"অ্যাপ"</string>
<string name="app_permissions" msgid="3369917736607944781">"অ্যাপের অনুমতি"</string>
<string name="unused_apps" msgid="2058057455175955094">"অব্যবহৃত অ্যাপ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"এই অ্যাপের জন্য বেছে নেওয়া ফটো এডিট করুন"</string>
<string name="no_unused_apps" msgid="12809387670415295">"অব্যবহৃত কোনও অ্যাপ নেই"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"ব্যবহার না করা একটিও অ্যাপ নেই"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"অনুমতি সংক্রান্ত সাম্প্রতিক সিদ্ধান্ত"</string>
@@ -493,7 +495,7 @@
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে কল করতে এবং কল পরিচালনা করতে দেবেন?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে সেন্সর থেকে আপনার ভাইটাল সাইনের ডেটা অ্যাক্সেস করতে দেবেন?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"এই অ্যাপ সবসময় শারীরবৃত্তীয় লক্ষণ সংক্রান্ত সেন্সর ডেটা অ্যাক্সেস করার অনুমতি চায়, এমনকী আপনি যখন অ্যাপটি ব্যবহার করছেন না, তখনও। এই পরিবর্তন চালু করতে, "<annotation id="link">"সেটিংসে যান।"</annotation></string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে আপনার বেঁচে থাকার গুরুত্বপূর্ণ লক্ষণ সম্পর্কিত সেন্সর ডেটা অ্যাক্সেস করার অনুমতি দিতে চান?"</string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে আপনার ভাইটাল সাইন সম্পর্কিত সেন্সর ডেটা অ্যাক্সেস করার অনুমতি দিতে চান?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"অ্যাপটি ব্যবহার না করলেও, সেটিকে সবসময় বডি সেন্সর ডেটাতে অ্যাক্সেস দিতে "<annotation id="link">"সেটিংসে যান"</annotation>"।"</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"ব্যবহার করার সময় &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে বডি সেন্সর ডেটার অ্যাক্সেস দিয়ে রাখতে চান?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে বিজ্ঞপ্তি পাঠানোর অনুমতি দেবেন?"</string>
diff --git a/PermissionController/res/values-bs/strings.xml b/PermissionController/res/values-bs/strings.xml
index 069c6e418..09857785d 100644
--- a/PermissionController/res/values-bs/strings.xml
+++ b/PermissionController/res/values-bs/strings.xml
@@ -29,11 +29,12 @@
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"Aplikacija nije pronađena"</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"Nemoj dozvoliti"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Nemoj dozvoliti i ne pitaj ponovo"</string>
- <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadrži “Kada se aplikacija koristi”"</string>
+ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadrži “Dok se aplikacija koristi”"</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zadrži “Samo ovaj put”"</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Više informacija"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Dozvoli sve"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Uvijek dozvoli sve"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Dozvoli ograničeni pristup"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Odabir fotografija i videozapisa"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Odaberi više"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Nemoj odabrati više"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikacije"</string>
<string name="app_permissions" msgid="3369917736607944781">"Odobrenja za aplikaciju"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nekorištene aplikacije"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Uredite odabrane fotografije za aplikaciju"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nema nekorištenih aplikacija"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 nekorištenih aplikacija"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nedavne odluke o odobrenjima"</string>
@@ -362,11 +364,11 @@
<string name="role_dialer_request_description" msgid="6288839625724909320">"Ova aplikacija će dobiti pristup vašoj Kameri, Kontaktima, Mikrofonu, Telefonu i SMS-u"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"brojčana tastatura"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Zadana aplikacija za SMS"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"Aplikacija za SMS"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"aplikacija za SMS"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Aplikacije koje vam omogućavaju korištenje broja telefona za slanje i primanje kratkih SMS-ova, fotografija, videozapisa i drugog"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Postaviti aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> kao zadanu aplikaciju za SMS?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Ova aplikacija će dobiti pristup vašoj Kameri, Kontaktima, Fajlovima i medijima, Mikrofonu, Telefonu i SMS-u"</string>
- <string name="role_sms_search_keywords" msgid="8022048144395047352">"SMS, slanje SMS-ova, poruke, slanje poruka"</string>
+ <string name="role_sms_search_keywords" msgid="8022048144395047352">"poruka, slanje poruka, poruke, razmjena poruka"</string>
<string name="role_emergency_label" msgid="7028825857206842366">"Zadana apl. za hitne situacije"</string>
<string name="role_emergency_short_label" msgid="2388431453335350348">"Aplikacija za hitne situacije"</string>
<string name="role_emergency_description" msgid="5051840234887686630">"Aplikacije koje vam omogućavaju da zabilježite svoje zdravstvene podatke i omogućite hitnim službama da im pristupe; da primate upozorenja o jakom nevremenu i katastrofama; da obavijestite druge kada vam je potrebna pomoć"</string>
@@ -374,7 +376,7 @@
<string name="role_emergency_request_description" msgid="131645948770262850">"Nije potrebno odobrenje"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"hitni slučaj"</string>
<string name="role_home_label" msgid="3871847846649769412">"Zadana apl. na početnom ekranu"</string>
- <string name="role_home_short_label" msgid="8544733747952272337">"Aplikacija na početnom ekranu"</string>
+ <string name="role_home_short_label" msgid="8544733747952272337">"Aplikacija za početni ekran"</string>
<string name="role_home_description" msgid="7997371519626556675">"Aplikacije, poznate i kao pokretači, koje zamjenjuju početne ekrane na Android uređaju te vam daju pristup sadržaju i funkcijama uređaja"</string>
<string name="role_home_request_title" msgid="738136983453341081">"Postaviti aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> kao zadanu aplikaciju na početnom ekranu?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Nije potrebno odobrenje"</string>
@@ -468,7 +470,7 @@
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa približnoj lokaciji ovog uređaja?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Tačno"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Približno"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupi vašem kalendaru?"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa vašem kalendaru?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da šalje i pregleda SMS poruke?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fotografijama, medijima i fajlovima na vašem uređaju?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Dozvoliti da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa &lt;b&gt;foto/video/muzičkim/audio fajlovima&lt;/b&gt; na ovom uređaju?"</string>
diff --git a/PermissionController/res/values-ca/strings.xml b/PermissionController/res/values-ca/strings.xml
index 0187eabc6..e0fa86e9a 100644
--- a/PermissionController/res/values-ca/strings.xml
+++ b/PermissionController/res/values-ca/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Més informació"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permet-ho tot"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Permet sempre tot"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Permet l\'accés limitat"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Selecciona fotos i vídeos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Selecciona\'n més"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"No seleccionis més"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplicacions"</string>
<string name="app_permissions" msgid="3369917736607944781">"Permisos d\'aplicacions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplicacions no utilitzades"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Edita les fotos seleccionades per a aquesta aplicació"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Cap aplicació sense utilitzar"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 aplicacions no utilitzades"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Decisions recents de permisos"</string>
@@ -290,7 +292,7 @@
<string name="notification_listener_remove_access_success_label" msgid="2477611529875633107">"S\'ha suprimit l\'accés"</string>
<string name="accessibility_access_reminder_notification_title" msgid="2971317234668807566">"Revisa l\'aplicació amb accés complet al dispositiu"</string>
<string name="accessibility_access_reminder_notification_content" msgid="7389454158175306720">"<xliff:g id="APP_NAME">%s</xliff:g> pot veure la pantalla i dur a terme accions al dispositiu. Les aplicacions d\'accessibilitat necessiten aquest tipus d\'accés perquè funcionin de la manera prevista."</string>
- <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"Aquesta aplicació pot veure la pantalla i dur a terme accions al dispositiu. Les aplicacions d\'accessibilitat necessiten aquest tipus d\'accés perquè funcionin de la manera prevista, però comprova l\'aplicació i assegura\'t que sigui de confiança."</string>
+ <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"Aquesta aplicació pot veure la pantalla i dur a terme accions al dispositiu. Les aplicacions d\'accessibilitat necessiten aquest tipus d\'accés perquè funcionin de la manera prevista, però comprova l\'aplicació i assegura\'t que hi confies."</string>
<string name="accessibility_remove_access_button_label" msgid="44145801526711640">"Suprimeix l\'accés"</string>
<string name="accessibility_show_all_apps_button_label" msgid="960067249326392280">"Veure les aplicacions amb accés complet"</string>
<string name="accessibility_remove_access_success_label" msgid="4380995302917014670">"S\'ha suprimit l\'accés"</string>
@@ -355,7 +357,7 @@
<string name="role_browser_description" msgid="3465253637499842671">"Aplicacions que et donen accés a Internet i que mostren els enllaços que toques"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"Vols establir <xliff:g id="APP_NAME">%1$s</xliff:g> com a aplicació de navegador predeterminada?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"No calen permisos"</string>
- <string name="role_dialer_label" msgid="1100224146343237968">"Aplicació Telèfon predeterminada"</string>
+ <string name="role_dialer_label" msgid="1100224146343237968">"Aplicació de telèfon predeterminada"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"Aplicació de telèfon"</string>
<string name="role_dialer_description" msgid="8768708633696539612">"Aplicacions que et permeten fer i rebre trucades al dispositiu"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"Vols establir <xliff:g id="APP_NAME">%1$s</xliff:g> com a aplicació de telèfon predeterminada?"</string>
@@ -363,7 +365,7 @@
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"marcador"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Aplicació d\'SMS predeterminada"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"Aplicació d\'SMS"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Aplicacions que et permeten utilitzar el número de telèfon per enviar i rebre missatges curts de text, fotos, vídeos i més"</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Aplicacions que et permeten utilitzar el número de telèfon per enviar i rebre missatges de text, fotos, vídeos i més"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Vols establir <xliff:g id="APP_NAME">%1$s</xliff:g> com a aplicació d\'SMS predeterminada?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Aquesta aplicació tindrà accés a la càmera, als contactes, als fitxers i contingut multimèdia, al micròfon, al telèfon i als SMS"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"missatge de text, enviar missatges, missatges, missatgeria"</string>
@@ -397,7 +399,7 @@
<string name="role_watch_description" msgid="267003778693177779">"<xliff:g id="APP_NAME">%1$s</xliff:g> tindrà permís per interaccionar amb les teves notificacions i accedir al telèfon, SMS, contactes i calendari."</string>
<string name="role_app_streaming_description" msgid="7341638576226183992">"<xliff:g id="APP_NAME">%1$s</xliff:g> tindrà permís per interaccionar amb les teves notificacions i reproduir en continu les teves aplicacions al dispositiu connectat."</string>
<string name="role_companion_device_computer_description" msgid="416099879217066377">"Aquest servei comparteix les fotos, el contingut multimèdia i les notificacions del telèfon amb altres dispositius."</string>
- <string name="role_notes_label" msgid="7451627001058089536">"App de notes predeterminada"</string>
+ <string name="role_notes_label" msgid="7451627001058089536">"Aplicació de notes predeterminada"</string>
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplicació de notes"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplicacions que et permeten prendre notes al dispositiu"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notes"</string>
@@ -456,7 +458,7 @@
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mostra una icona a la barra d\'estat quan s\'utilitzi el micròfon per activar l\'assistent de veu"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les fotos i al contingut multimèdia del dispositiu?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi als contactes?"</string>
- <string name="permgrouprequest_location" msgid="6990232580121067883">"Permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la ubicació del dispositiu?"</string>
+ <string name="permgrouprequest_location" msgid="6990232580121067883">"Vols permetre que la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la ubicació del dispositiu?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"L\'aplicació només tindrà accés a la ubicació quan l\'estiguis utilitzant"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la ubicació del dispositiu?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Aquesta aplicació vol tenir accés a la teva ubicació sempre, fins i tot quan no l\'estiguis utilitzant. "<annotation id="link">"Pots permetre-ho a Configuració"</annotation>"."</string>
@@ -492,7 +494,7 @@
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi als registres de trucades del telèfon?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faci trucades telefòniques i les gestioni?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les dades del sensor de constants vitals?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Aquesta aplicació vol accedir a les dades del sensor sobre les constants vitals sempre, fins i tot quan no l\'utilitzis. Per fer aquest canvi, "<annotation id="link">"ves a la configuració"</annotation>"."</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Aquesta aplicació vol accedir a les dades del sensor sobre les constants vitals sempre, fins i tot quan no la utilitzis. Per fer aquest canvi, "<annotation id="link">"ves a la configuració"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les dades dels sensors sobre les constants vitals?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Per permetre que l\'aplicació accedeixi sempre a les dades del sensor corporal, fins i tot quan no la utilitzis, "<annotation id="link">"ves a la configuració"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Vols continuar permetent que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les dades del sensor corporal mentre s\'utilitza?"</string>
diff --git a/PermissionController/res/values-cs-v33/strings.xml b/PermissionController/res/values-cs-v33/strings.xml
index 2e30db000..84a0f9fd1 100644
--- a/PermissionController/res/values-cs-v33/strings.xml
+++ b/PermissionController/res/values-cs-v33/strings.xml
@@ -20,7 +20,7 @@
<string name="role_sms_request_description" msgid="1506966389698625395">"Tato aplikace vám bude moci zasílat oznámení a bude mít přístup k fotoaparátu, kontaktům, souborům, mikrofonu, telefonu a SMS"</string>
<string name="permission_description_summary_storage" msgid="1917071243213043858">"Aplikace s tímto oprávněním mají přístup ke všem souborům v tomto zařízení"</string>
<string name="work_policy_title" msgid="832967780713677409">"Informace o vašich pracovních zásadách"</string>
- <string name="work_policy_summary" msgid="3886113358084963931">"Nastavení spravuje administrátor IT"</string>
+ <string name="work_policy_summary" msgid="3886113358084963931">"Nastavení spravuje IT administrátor"</string>
<string name="safety_center_entry_group_expand_action" msgid="5358289574941779652">"Rozbalit a zobrazit seznam"</string>
<string name="safety_center_entry_group_collapse_action" msgid="1525710152244405656">"Sbalit seznam a skrýt nastavení"</string>
<string name="safety_center_entry_group_content_description" msgid="7048420958214443333">"Seznam. <xliff:g id="ENTRY_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_SUMMARY">%2$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-cs/strings.xml b/PermissionController/res/values-cs/strings.xml
index 1c28eb912..7de999898 100644
--- a/PermissionController/res/values-cs/strings.xml
+++ b/PermissionController/res/values-cs/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Další informace"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Povolit vše"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Vždy zobrazit vše"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Povolit omezený přístup"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Vybrat fotky a videa"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Vybrat další"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Nevybírat další"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikace"</string>
<string name="app_permissions" msgid="3369917736607944781">"Oprávnění aplikace"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nepoužívané aplikace"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Upravit vybrané fotky pro tuto aplikaci"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Žádné nepoužívané aplikace"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Žádné nepoužívané aplikace"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nedávná rozhodnutí o oprávnění"</string>
@@ -219,7 +221,7 @@
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"Byla odebrána oprávnění <xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g> a <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g>"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"Bylo odebráno oprávnění <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> a další oprávnění (<xliff:g id="NUMBER">%2$s</xliff:g>)"</string>
<string name="unused_apps_page_title" msgid="6986983535677572559">"Nepoužívané aplikace"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"Když aplikaci několik měsíců nepoužíváte:\n\n• Oprávnění jsou odstraněna kvůli ochraně dat\n• Oznámení se přestanou zobrazovat kvůli úspoře baterie\n• Dočasné soubory jsou odstraněny kvůli uvolnění místa\n\nPokud chcete oprávnění a oznámení znovu povolit, otevřete aplikaci."</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"Když aplikaci několik měsíců nepoužíváte:\n\n• Oprávnění jsou odstraněna z důvodu ochrany dat.\n• Oznámení se přestanou zobrazovat kvůli úspoře baterie.\n• Dočasné soubory jsou odstraněny, aby se uvolnilo místo.\n\nPokud chcete oprávnění a oznámení znovu povolit, příslušnou aplikaci otevřete."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Když nějakou aplikaci měsíc nepoužíváte:\n\n• Kvůli ochraně dat se odstraní oprávnění.\n• Kvůli uvolnění místa se odstraní dočasné soubory.\n\nPokud chcete oprávnění znovu povolit, otevřete aplikaci."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Naposledy otevřeno před více než # měsícem}few{Naposledy otevřeno před více než # měsíci}many{Naposledy otevřeno před více než # měsíce}other{Naposledy otevřeno před více než # měsíci}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"Aplikace byla naposledy otevřena <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -375,7 +377,7 @@
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"v případě nouze"</string>
<string name="role_home_label" msgid="3871847846649769412">"Výchozí aplikace pro domácnost"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Vstupní aplikace"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Aplikace (tzv. spouštěče), které nahrazují plochy na zařízení Android a dávají vám přístup k obsahu a funkcím zařízení."</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Aplikace (tzv. spouštěče), které nahrazují plochu na zařízení Android a dávají vám přístup k obsahu a funkcím zařízení."</string>
<string name="role_home_request_title" msgid="738136983453341081">"Nastavit <xliff:g id="APP_NAME">%1$s</xliff:g> jako výchozí aplikaci pro domácnost?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Není potřeba žádné oprávnění"</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"spouštěč"</string>
@@ -426,7 +428,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Další výchozí nastavení"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Otevírání odkazů"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Výchozí pracovní"</string>
- <string name="default_app_none" msgid="9084592086808194457">"Žádná"</string>
+ <string name="default_app_none" msgid="9084592086808194457">"Žádná aplikace"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Výchozí nastavení systému)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Žádné aplikace"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Vybráno"</string>
@@ -581,7 +583,7 @@
<string name="mic_toggle_title" msgid="2649991093496110162">"Přístup k mikrofonu"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Aplikace a služby"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Týká se aplikací a služeb. Pokud je toto nastavení vypnuté a zavoláte na číslo tísňového volání, data z mikrofonu se mohou stále sdílet."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"Podívejte se, které aplikace a služby mají přístup k poloze"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"Seznam aplikací a služeb, které mají přístup k poloze"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Zobrazovat použití schránky"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Zobrazovat zprávu, když aplikace použijí text, obrázky nebo jiný obsah, který jste zkopírovali"</string>
<string name="show_password_title" msgid="2877269286984684659">"Zobrazovat hesla"</string>
diff --git a/PermissionController/res/values-da/strings.xml b/PermissionController/res/values-da/strings.xml
index 7fe9f9309..f88390ccd 100644
--- a/PermissionController/res/values-da/strings.xml
+++ b/PermissionController/res/values-da/strings.xml
@@ -31,9 +31,10 @@
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Tillad ikke, og spørg ikke igen"</string>
<string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Behold \"Mens appen er i brug\""</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Behold \"Kun denne ene gang\""</string>
- <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mere info"</string>
+ <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Flere oplysninger"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Tillad alle"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Tillad altid alle"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Tillad begrænset adgang"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Vælg billeder og videoer"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Vælg flere"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Vælg ikke flere"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"Apptilladelser"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps, du ikke bruger"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Rediger valget af billeder for denne app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ingen ubrugte apps"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Der er 0 ubrugte apps"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nylige beslutninger om tilladelser"</string>
@@ -181,8 +183,8 @@
<string name="permission_history_category_today" msgid="7496389369158806620">"I dag"</string>
<string name="permission_history_category_yesterday" msgid="7242517121222012521">"I går"</string>
<string name="app_permission_usage_title" msgid="6676802437831981822">"Brug af apptilladelser"</string>
- <string name="app_permission_usage_summary" msgid="390383661936709672">"Adgang: <xliff:g id="NUM">%1$s</xliff:g> gange. Samlet varighed: <xliff:g id="DURATION">%2$s</xliff:g>. Senest brugt for <xliff:g id="TIME">%3$s</xliff:g> siden."</string>
- <string name="app_permission_usage_summary_no_duration" msgid="3698475875179457400">"Adgang: <xliff:g id="NUM">%1$s</xliff:g> gange. Senest brugt for <xliff:g id="TIME">%2$s</xliff:g> siden."</string>
+ <string name="app_permission_usage_summary" msgid="390383661936709672">"Adgang: <xliff:g id="NUM">%1$s</xliff:g> gange. Samlet varighed: <xliff:g id="DURATION">%2$s</xliff:g>. Senest anvendt for <xliff:g id="TIME">%3$s</xliff:g> siden."</string>
+ <string name="app_permission_usage_summary_no_duration" msgid="3698475875179457400">"Adgang: <xliff:g id="NUM">%1$s</xliff:g> gange. Senest anvendt for <xliff:g id="TIME">%2$s</xliff:g> siden."</string>
<string name="app_permission_button_allow" msgid="5808039516494774647">"Tillad"</string>
<string name="app_permission_button_allow_all_files" msgid="1792232272599018825">"Tillad administration af alle filer"</string>
<string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Tillad kun adgang til mediefiler"</string>
@@ -221,9 +223,9 @@
<string name="unused_apps_page_title" msgid="6986983535677572559">"Apps, du ikke bruger"</string>
<string name="unused_apps_page_summary" msgid="1867593913217272155">"Hvis en app ikke bruges i et par måneder, sker følgende:\n\n• Tilladelser fjernes for at beskytte dine data\n• Notifikationer stoppes for at spare på batteriet\n• Midlertidige filer fjernes for at frigøre plads\n\nHvis du vil aktivere tilladelser og notifikationer igen, skal du åbne appen."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Hvis en app ikke bruges i en måned, sker følgende:\n\n• Tilladelser fjernes for at beskytte dine data\n• Midlertidige filer fjernes for at frigøre plads\n\nHvis du vil aktivere tilladelserne igen, skal du åbne appen."</string>
- <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Sidst åbnet for mere end # måned siden}one{Sidst åbnet for mere end # måned siden}other{Sidst åbnet for mere end # måneder siden}}"</string>
+ <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Senest åbnet for mere end # måned siden}one{Senest åbnet for mere end # måned siden}other{Senest åbnet for mere end # måneder siden}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"Appen blev sidst åbnet <xliff:g id="DATE">%s</xliff:g>"</string>
- <string name="last_opened_summary_short" msgid="1646067226191176825">"Sidst åbnet <xliff:g id="DATE">%s</xliff:g>"</string>
+ <string name="last_opened_summary_short" msgid="1646067226191176825">"Senest åbnet <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"Hvis du tillader administration af alle filer, får denne app adgang og tilladelse til at ændre og slette alle filer på enhedens almindelige lagerplads og tilsluttede lagerenheder. Appen kan få adgang til filer uden at spørge dig først."</string>
<string name="special_file_access_dialog" msgid="583804114020740610">"Vil du give denne app adgang og tilladelse til at ændre og slette filer på denne enhed og tilsluttede lagerenheder? Appen kan få adgang til filer uden at spørge dig først."</string>
<string name="permission_description_summary_generic" msgid="5401399408814903391">"Apps med denne tilladelse kan <xliff:g id="DESCRIPTION">%1$s</xliff:g>"</string>
@@ -236,7 +238,7 @@
<string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Apps med denne tilladelse kan finde, oprette forbindelse til og fastslå den relative placering af enheder i nærheden"</string>
<string name="permission_description_summary_microphone" msgid="630834800308329907">"Apps med denne tilladelse kan optage lyd"</string>
<string name="permission_description_summary_phone" msgid="4515277217435233619">"Apps med denne tilladelse kan foretage og administrere telefonopkald"</string>
- <string name="permission_description_summary_sensors" msgid="1836045815643119949">"Apps med denne tilladelse kan få adgang til sensordata om dine livstegn"</string>
+ <string name="permission_description_summary_sensors" msgid="1836045815643119949">"Apps med denne tilladelse kan få adgang til sensordata om dine vitale værdier"</string>
<string name="permission_description_summary_sms" msgid="725999468547768517">"Apps med denne tilladelse kan sende og se sms-beskeder"</string>
<string name="permission_description_summary_storage" msgid="6575759089065303346">"Apps med denne tilladelse har adgang til billeder, medier og filer på din enhed"</string>
<string name="permission_description_summary_read_media_aural" msgid="3354728149930482199">"Apps med denne tilladelse kan tilgå musik og andre lydfiler på denne enhed"</string>
@@ -263,7 +265,7 @@
<string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Nogle tilladelser blev fjernet for at beskytte dit privatliv. Tryk for at gennemgå dem"</string>
<string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Tilladelser er fjernet for ubrugte apps"</string>
<string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Nogle apps er ikke blevet brugt i et par måneder. Tryk for at gennemgå dem."</string>
- <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# ubrugt app}one{# ubrugt app}other{# ubrugt apps}}"</string>
+ <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# ubrugt app}one{# ubrugt app}other{# ubrugte apps}}"</string>
<string name="unused_apps_notification_content" msgid="9195026773244581246">"Tilladelser og midlertidige filer er blevet fjernet, og notifikationer blev stoppet. Tryk for at se."</string>
<string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Gennemgå apps, som har fået fjernet tilladelser"</string>
<string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Tilladelser og midlertidige filer for de apps, du ikke har brugt for nylig, blev fjernet, og notifikationer ophørte."</string>
@@ -315,24 +317,24 @@
<string name="permission_subtitle_media_only" msgid="8917869683764720717">"Mediefiler"</string>
<string name="permission_subtitle_all_files" msgid="4982613338298067862">"Alle filer"</string>
<string name="permission_subtitle_background" msgid="8916750995309083180">"Altid tilladt"</string>
- <string name="app_perms_24h_access" msgid="99069906850627181">"Sidst brugt <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
- <string name="app_perms_24h_access_yest" msgid="5411926024794555022">"Sidst brugt i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
- <string name="app_perms_7d_access" msgid="4945055548894683751">"Sidst brugt <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="app_perms_24h_access" msgid="99069906850627181">"Senest anvendt <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="app_perms_24h_access_yest" msgid="5411926024794555022">"Senest anvendt i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="app_perms_7d_access" msgid="4945055548894683751">"Senest anvendt <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
<string name="app_perms_content_provider_24h" msgid="1055526027667508972">"Brugt inden for de seneste 24 timer"</string>
<string name="app_perms_content_provider_7d" msgid="3215454898257814868">"Brugt inden for de seneste 7 dage"</string>
- <string name="app_perms_24h_access_background" msgid="3413674718969576843">"Sidst brugt <xliff:g id="TIME_DATE">%1$s</xliff:g> • Altid tilladt"</string>
- <string name="app_perms_24h_access_yest_background" msgid="9174750810998076725">"Sidst brugt i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g> • Altid tilladt"</string>
- <string name="app_perms_7d_access_background" msgid="408099213372185627">"Sidst brugt <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Altid tilladt"</string>
+ <string name="app_perms_24h_access_background" msgid="3413674718969576843">"Senest anvendt <xliff:g id="TIME_DATE">%1$s</xliff:g> • Altid tilladt"</string>
+ <string name="app_perms_24h_access_yest_background" msgid="9174750810998076725">"Senest anvendt i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g> • Altid tilladt"</string>
+ <string name="app_perms_7d_access_background" msgid="408099213372185627">"Senest anvendt <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Altid tilladt"</string>
<string name="app_perms_content_provider_24h_background" msgid="3825902995186961496">"Brugt inden for de seneste 24 timer • Altid tilladt"</string>
<string name="app_perms_content_provider_7d_background" msgid="4818839672116463542">"Brugt inden for de seneste 7 dage • Altid tilladt"</string>
- <string name="app_perms_24h_access_media_only" msgid="6651699644199132054">"Sidst brugt <xliff:g id="TIME_DATE">%1$s</xliff:g> • Medier"</string>
- <string name="app_perms_24h_access_yest_media_only" msgid="7213187706424998792">"Sidst brugt i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g> • Medier"</string>
- <string name="app_perms_7d_access_media_only" msgid="1031096653668235200">"Sidst brugt <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Mediefiler"</string>
+ <string name="app_perms_24h_access_media_only" msgid="6651699644199132054">"Senest anvendt <xliff:g id="TIME_DATE">%1$s</xliff:g> • Medier"</string>
+ <string name="app_perms_24h_access_yest_media_only" msgid="7213187706424998792">"Senest anvendt i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g> • Medier"</string>
+ <string name="app_perms_7d_access_media_only" msgid="1031096653668235200">"Senest anvendt <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Mediefiler"</string>
<string name="app_perms_content_provider_24h_media_only" msgid="7797963000596179491">"Brugt inden for de seneste 24 timer • Mediefiler"</string>
<string name="app_perms_content_provider_7d_media_only" msgid="8446239884570262243">"Brugt inden for de seneste 7 dage • Mediefiler"</string>
- <string name="app_perms_24h_access_all_files" msgid="8902360456978159091">"Sidst brugt <xliff:g id="TIME_DATE">%1$s</xliff:g> • Alle filer"</string>
- <string name="app_perms_24h_access_yest_all_files" msgid="5708424073126844909">"Sidst brugt i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g> • Alle filer"</string>
- <string name="app_perms_7d_access_all_files" msgid="8246193786397635824">"Sidst brugt <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Alle filer"</string>
+ <string name="app_perms_24h_access_all_files" msgid="8902360456978159091">"Senest åbnet <xliff:g id="TIME_DATE">%1$s</xliff:g> • Alle filer"</string>
+ <string name="app_perms_24h_access_yest_all_files" msgid="5708424073126844909">"Senest anvendt i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g> • Alle filer"</string>
+ <string name="app_perms_7d_access_all_files" msgid="8246193786397635824">"Senest anvendt <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Alle filer"</string>
<string name="app_perms_content_provider_24h_all_files" msgid="573104317727770850">"Brugt inden for de seneste 24 timer • Alle filer"</string>
<string name="app_perms_content_provider_7d_all_files" msgid="7962416229708835558">"Brugt inden for de seneste 7 dage • Alle filer"</string>
<string name="no_permissions_allowed" msgid="6081976856354669209">"Der ikke givet nogen tilladelser"</string>
@@ -350,10 +352,10 @@
<string name="role_assistant_label" msgid="4727586018198208128">"Standardapp for digital assistent"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"App for digital assistent"</string>
<string name="role_assistant_description" msgid="6622458130459922952">"Assistanceapps kan hjælpe dig på baggrund af oplysningerne på den aktuelle skærm. Nogle apps understøtter både startertjenester og tjenester til indtaling for at give dig integreret assistance."</string>
- <string name="role_browser_label" msgid="2877796144554070207">"Standardbrowserapp"</string>
+ <string name="role_browser_label" msgid="2877796144554070207">"Standardapp til browsing"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Browserapp"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Apps, der giver dig adgang til internettet og viser links, som du trykker på"</string>
- <string name="role_browser_request_title" msgid="2895200507835937192">"Vil du angive <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardbrowserapp?"</string>
+ <string name="role_browser_request_title" msgid="2895200507835937192">"Vil du angive <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardapp til browsing?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"Der kræves ingen tilladelser"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"Standardapp til opkald"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"Opkaldsapp"</string>
@@ -362,7 +364,7 @@
<string name="role_dialer_request_description" msgid="6288839625724909320">"Denne app får adgang til Kamera, Kontakter, Mikrofon, Telefon og SMS"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"opkald"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Standardapp til sms-beskeder"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"Sms-app"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"sms-app"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Apps, der bruger dit telefonnummer til at sende og modtage sms-beskeder, billeder, videoer m.m."</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Vil du angive <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardapp til sms-beskeder?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Denne app får adgang til Kamera, Kontakter, Filer og medier, Mikrofon, Telefon og SMS"</string>
@@ -593,8 +595,8 @@
<string name="permission_rationale_data_sharing_source_message" msgid="8330794595417986883">"Udvikleren gav oplysninger til "<annotation id="link"><annotation id="install_source" example="App Store">"%1$s"</annotation></annotation>" om, hvordan denne app deler data. Udvikleren opdaterer muligvis disse oplysninger senere."</string>
<string name="permission_rationale_location_purpose_title" msgid="5115877143670012618">"Denne app deler muligvis lokationsdata for:"</string>
<string name="permission_rationale_permission_data_sharing_varies_title" msgid="9103718980919908316">"Datadeling varierer"</string>
- <string name="permission_rationale_data_sharing_varies_message" msgid="4224469559084489222">"Databehandlingen kan variere afhængigt af din appversion, din brug, dit område og din alder. "<annotation id="link">"Flere oplysninger om datadeling"</annotation></string>
- <string name="permission_rationale_data_sharing_varies_message_without_link" msgid="4912763761399025094">"Databehandlingen kan variere afhængigt af din appversion, din brug, dit område og din alder."</string>
+ <string name="permission_rationale_data_sharing_varies_message" msgid="4224469559084489222">"Databehandlingen kan variere afhængigt af din appversion, din brug, din region og din alder. "<annotation id="link">"Flere oplysninger om datadeling"</annotation></string>
+ <string name="permission_rationale_data_sharing_varies_message_without_link" msgid="4912763761399025094">"Databehandlingen kan variere afhængigt af din appversion, din brug, din region og din alder."</string>
<string name="permission_rationale_location_settings_title" msgid="7204145004850190953">"Dine lokationsdata"</string>
<string name="permission_rationale_permission_settings_message" msgid="631286040979660267">"Skift denne apps adgang i "<annotation id="link">"privatlivsindstillingerne"</annotation></string>
<string name="permission_rationale_purpose_app_functionality" msgid="8397736681065841405">"Appfunktionalitet"</string>
@@ -610,7 +612,7 @@
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Opdateringer om deling af lokationsdata"</string>
<string name="data_sharing_updates_summary" msgid="764113985772233889">"Gennemse apps, der har ændret måden, hvorpå de kan dele dine lokationsdata"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Disse apps har ændret den måde, hvorpå de kan dele dine lokationsdata. De har muligvis ikke delt dataene før eller kan nu anvende dem til annoncering eller marketing."</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Udviklerne af disse apps har givet oplysninger om deres procedurer for datadeling til en appbutik. De kan opdatere procedurerne med tiden.\n\nProcedurer for datadeling kan variere afhængigt af din appversion, din brug, dit område og din alder."</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Udviklerne af disse apps har givet oplysninger om deres procedurer for datadeling til en appbutik. De kan opdatere procedurerne med tiden.\n\nProcedurer for datadeling kan variere afhængigt af din appversion, din brug, din region og din alder."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Få flere oplysninger om deling af data"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Dine lokationsdata deles nu med tredjeparter"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Dine lokationsdata deles nu med tredjeparter med henblik på annoncering eller marketing"</string>
diff --git a/PermissionController/res/values-de-v34/strings.xml b/PermissionController/res/values-de-v34/strings.xml
index 8b6f95055..2606bff18 100644
--- a/PermissionController/res/values-de-v34/strings.xml
+++ b/PermissionController/res/values-de-v34/strings.xml
@@ -17,11 +17,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"Sicherheit &amp; Datenschutz"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"Datenschutz &amp; Sicherheit"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Einstellungen"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"App-Zugriff auf Gesundheitsdaten verwalten"</string>
<string name="location_settings" msgid="8863940440881290182">"Standortzugriff"</string>
- <string name="mic_toggle_description" msgid="1504101620086616040">"Für Apps und Dienste. Wenn du eine Notrufnummer wählst, können Mikrofondaten trotz Deaktivierung dieser Berechtigung weitergegeben werden."</string>
+ <string name="mic_toggle_description" msgid="1504101620086616040">"Für Apps und Dienste. Wenn du eine Notrufnummer wählst, können Mikrofondaten weitergegeben werden, auch wenn diese Berechtigung deaktiviert ist."</string>
<string name="location_settings_subtitle" msgid="6846532794702613851">"Für Apps und Dienste"</string>
</resources>
diff --git a/PermissionController/res/values-de/strings.xml b/PermissionController/res/values-de/strings.xml
index 1ac8e1d38..c2b2b0251 100644
--- a/PermissionController/res/values-de/strings.xml
+++ b/PermissionController/res/values-de/strings.xml
@@ -32,8 +32,9 @@
<string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"„Wenn die App verwendet wird“ beibehalten"</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Nur dieses Mal\" aktiviert lassen"</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Weitere Infos"</string>
- <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Vollen Zugriff erlauben"</string>
+ <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Vollen Zugriff zulassen"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Immer vollen Zugriff erlauben"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Eingeschränkten Zugriff zulassen"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Fotos und Videos auswählen"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Mehr auswählen"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Keine weiteren auswählen"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"App-Berech­tigungen"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nicht verwendete Apps"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Für diese App ausgewählte Fotos bearbeiten"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Keine nicht verwendeten Apps"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Keine nicht verwendeten Apps"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Aktuelle Berechtigungsentscheidungen"</string>
@@ -70,7 +72,7 @@
<string name="denied_permission_decision" msgid="5308961501779563781">"Du hast <xliff:g id="APP_NAME">%1$s</xliff:g> den Zugriff auf die Berechtigung „<xliff:g id="PERMISSION_NAME">%2$s</xliff:g>“ verweigert"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Heute}=1{Vor 1 Tag}other{Vor # Tagen}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"App deaktivieren"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"Bei Deaktivierung dieser App funktionieren Android und andere Apps möglicherweise nicht mehr ordnungsgemäß. Beachte hierbei, dass du diese App nicht löschen kannst, weil sie auf deinem Gerät vorinstalliert war. Durch die Deaktivierung schaltest du diese App ab und blendest sie auf deinem Gerät aus."</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"Bei Deaktivierung dieser App funktionieren Android und andere Apps möglicherweise nicht mehr ordnungsgemäß. Beachte hierbei, dass du diese App nicht löschen kannst, weil sie auf deinem Gerät vorinstalliert war. Durch die Deaktivierung schaltest du diese App aus und blendest sie auf deinem Gerät aus."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Berechti­gungsmanager"</string>
<string name="never_ask_again" msgid="4728762438198560329">"Nicht mehr fragen"</string>
<string name="no_permissions" msgid="3881676756371148563">"Keine Berechtigungen"</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Diese Apps können deinen Bildschirm, deine Aktionen und deine Eingaben sehen, Aktionen ausführen und den Bildschirm steuern."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"Standard-App digit. Assistent"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"App für digitalen Assistenten"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"Assistent-Apps können dir bei bestimmten Dingen helfen. Dazu greifen sie auf die Informationen zu, die aktuell auf deinem Bildschirm angezeigt werden. Damit sie dir eine umfassende Hilfe sind, unterstützen einige Apps sowohl Launcher- als auch Spracheingabedienste."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"Assistent-Apps können dir bei bestimmten Dingen helfen. Dazu greifen sie auf die Informationen zu, die aktuell auf deinem Bildschirm angezeigt werden. Einige Apps unterstützen sowohl Launcher- als auch Spracheingabedienste."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Standard-Browser-App"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Browser-App"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Apps, mit denen du auf das Internet zugreifen kannst und die die entsprechende Seite öffnen, wenn du auf einen Link tippst"</string>
@@ -363,7 +365,7 @@
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"telefon"</string>
<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 SMS, Fotos, Videos und mehr senden und empfangen kannst"</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_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>
@@ -408,7 +410,7 @@
<string name="phone_call_uses_microphone_and_camera" msgid="6291898755681748189">"Kamera und Mikrofon werden für &lt;b&gt;Videoanruf&lt;/b&gt; verwendet"</string>
<string name="phone_call_uses_camera" msgid="2048417022147857418">"Kamera wird für &lt;b&gt;Videoanruf&lt;/b&gt; verwendet"</string>
<string name="system_uses_microphone" msgid="576672130318877143">"Der Zugriff auf das Mikrofon erfolgt über den Systemdienst"</string>
- <string name="system_uses_microphone_and_camera" msgid="5124478304275138804">"Der Zugriff auf die Kamera und das Mikrofon erfolt über den Systemdienst"</string>
+ <string name="system_uses_microphone_and_camera" msgid="5124478304275138804">"Der Zugriff auf die Kamera und das Mikrofon erfolgt über den Systemdienst"</string>
<string name="system_uses_camera" msgid="1911223105234441470">"Der Zugriff auf die Kamera erfolgt über den Systemdienst"</string>
<string name="other_use" msgid="6564855051022776692">"Andere Funktionen:"</string>
<string name="ongoing_usage_dialog_ok" msgid="103556809118460072">"Ok"</string>
@@ -427,7 +429,7 @@
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Links öffnen"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Standard-Apps für Arbeit"</string>
<string name="default_app_none" msgid="9084592086808194457">"Keine"</string>
- <string name="default_app_system_default" msgid="6218386768175513760">"(System-Standardeinstellung)"</string>
+ <string name="default_app_system_default" msgid="6218386768175513760">"(System-Standard­einstellung)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Keine Apps"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Ausgewählt"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Ausgewählt – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
@@ -455,7 +457,7 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Auslösererkennung für Assistenten anzeigen"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Symbol in der Statusleiste anzeigen, wenn das Mikrofon verwendet wird, um den Sprachassistenten zu aktivieren"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf Fotos und Medien auf deinem Gerät zuzugreifen?"</string>
- <string name="permgrouprequest_contacts" msgid="8391550064551053695">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ erlauben, auf deine Kontakte zuzugreifen?"</string>
+ <string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf deine Kontakte zuzugreifen?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, den Gerätestandort abzurufen?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Die App hat nur Zugriff auf den Gerätestandort, solange du sie verwendest"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, den Gerätestandort abzurufen?"</string>
@@ -491,7 +493,7 @@
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Diese App möchte jederzeit Bilder und Videos aufnehmen können, auch wenn du sie nicht verwendest. "<annotation id="link">"Du kannst das in den Einstellungen zulassen."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf deine Anrufliste zuzugreifen?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, Anrufe zu starten und zu verwalten?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf Sensordaten zu deinen Vitalfunktionen zuzugreifen?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; Sensor­daten zu deinen Vitalfunktionen abrufen?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Die App möchte jederzeit auf die Sensordaten zu deinen Vitalfunktionen zugreifen, auch wenn du sie nicht verwendest. Du kannst das "<annotation id="link">"in den Einstellungen ändern"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf Sensordaten zu deinen Vitalfunktionen zuzugreifen?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Damit diese App dauerhaft auf Daten des Körpersensors zugreifen kann, auch dann, wenn sie nicht verwendet wird, "<annotation id="link">"rufe die Einstellungen auf"</annotation>"."</string>
@@ -520,7 +522,7 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Für Apps und Dienste"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Die Mikrofondaten können immer noch geteilt werden, wenn du den Notruf wählst."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Ändern"</string>
- <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sicherheit &amp; Datenschutz"</string>
+ <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Datenschutz &amp; Sicherheit"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Gerät prüfen"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Schließen"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"Diese Warnung schließen?"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"Sicherheits- und Datenschutzstatus. <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"Sicherheitseinstellungen"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"Berechtigungen"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Sicherheit &amp; Datenschutz"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Datenschutz &amp; Sicherheit"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Status prüfen"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Datenschutzeinstellungen"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"Weitere Einstellungen"</string>
@@ -580,8 +582,8 @@
<string name="camera_toggle_title" msgid="1251201397431837666">"Kamerazugriff"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"Mikrofonzugriff"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Für Apps und Dienste"</string>
- <string name="mic_toggle_description" msgid="9163104307990677157">"Für Apps und Dienste. Wenn diese Einstellung deaktiviert ist, können Mikrofondaten dennoch freigegeben werden, wenn du den Notruf wählst."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"Apps und Dienste sehen, die Zugriff auf diesen Standort haben"</string>
+ <string name="mic_toggle_description" msgid="9163104307990677157">"Für Apps und Dienste. Wenn du eine Notrufnummer wählst, können Mikrofondaten weitergegeben werden, auch wenn diese Berechtigung deaktiviert ist."</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"Apps und Dienste sehen, die Zugriff auf den Standort haben"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Zugriff auf Zwischenablage anzeigen"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Eine Meldung wird angezeigt, wenn Apps auf Text, Bilder oder andere Inhalte zugreifen, die du kopiert hast"</string>
<string name="show_password_title" msgid="2877269286984684659">"Passwörter anzeigen"</string>
@@ -596,7 +598,7 @@
<string name="permission_rationale_data_sharing_varies_message" msgid="4224469559084489222">"Die Art der Datennutzung kann von der Version und Verwendung der App, von der Region und vom Alter des Nutzers abhängen. "<annotation id="link">"Weitere Informationen zur Datenweitergabe"</annotation></string>
<string name="permission_rationale_data_sharing_varies_message_without_link" msgid="4912763761399025094">"Die Datenschutz- und Sicherheitspraktiken können je nach App-Version, Verwendung, Region und Alter des Nutzers variieren."</string>
<string name="permission_rationale_location_settings_title" msgid="7204145004850190953">"Deine Standortdaten"</string>
- <string name="permission_rationale_permission_settings_message" msgid="631286040979660267">"Du kannst den Zugriff dieser App in den "<annotation id="link">"Datenschutzeinstellungen"</annotation>" ändern"</string>
+ <string name="permission_rationale_permission_settings_message" msgid="631286040979660267">"Du kannst den Zugriff dieser App in den "<annotation id="link">"Daten­schutz­ein­stel­lun­gen"</annotation>" ändern"</string>
<string name="permission_rationale_purpose_app_functionality" msgid="8397736681065841405">"Funktionen der App"</string>
<string name="permission_rationale_purpose_analytics" msgid="2070800501189620712">"Analytics"</string>
<string name="permission_rationale_purpose_developer_communications" msgid="6453047018892062374">"Mitteilungen des Entwicklers"</string>
@@ -610,7 +612,7 @@
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Änderungen bei der Weitergabe von Standortdaten"</string>
<string name="data_sharing_updates_summary" msgid="764113985772233889">"Apps ansehen, bei denen sich die Art der Weitergabe deiner Standortdaten geändert hat"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Bei diesen Apps hat sich die Art der Weitergabe deiner Standortdaten geändert. Möglicherweise wurden sie zuvor nicht weitergegeben oder sie werden jetzt zu Werbe- oder Marketingzwecken weitergegeben."</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Die Entwickler dieser Apps haben Informationen über ihre Praktiken zur Datenweitergabe an App-Shops zur Verfügung gestellt. Diese Praktiken können im Laufe der Zeit geändert werden.\n\nPraktiken zur Datenweitergabe können je nach App-Version, Verwendung, Region und Alter des Nutzers variieren."</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Die Entwickler dieser Apps haben für App-Shops Informationen über ihre Praktiken zur Datenweitergabe zur Verfügung gestellt. Diese Praktiken können im Laufe der Zeit geändert werden.\n\nPraktiken zur Datenweitergabe können je nach App-Version, Verwendung, Region und Alter des Nutzers variieren."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Informationen zur Datenweitergabe"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Deine Standortdaten werden ab jetzt an Dritte weitergegeben"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Deine Standortdaten werden ab jetzt zu Werbe- oder Marketingzwecken an Dritte weitergegeben"</string>
diff --git a/PermissionController/res/values-el-car/strings.xml b/PermissionController/res/values-el-car/strings.xml
index 605dfdf64..762f42d2b 100644
--- a/PermissionController/res/values-el-car/strings.xml
+++ b/PermissionController/res/values-el-car/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="unused_apps_page_summary" msgid="7505839764289846511">"Εάν μια εφαρμογή δεν χρησιμοποιηθεί για μερικούς μήνες:\n\n• Οι άδειες καταργούνται για την προστασία των δεδομένων σας\n• Τα προσωρινά αρχεία καταργούνται για την απελευθέρωση χώρου"</string>
+ <string name="unused_apps_page_summary" msgid="7505839764289846511">"Εάν μια εφαρμογή δεν χρησιμοποιηθεί για μερικούς μήνες:\n\n• Οι άδειες καταργούνται για την προστασία των δεδομένων σας\n• Τα προσωρινά αρχεία καταργούνται για την αποδέσμευση χώρου"</string>
</resources>
diff --git a/PermissionController/res/values-el/strings.xml b/PermissionController/res/values-el/strings.xml
index 54056ef01..436caee3d 100644
--- a/PermissionController/res/values-el/strings.xml
+++ b/PermissionController/res/values-el/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Περισσότερα"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Να επιτρέπεται"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Να επιτρέπονται πάντα όλα"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Να επιτρέπεται περιορισμένη πρόσβαση"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Επιλογή φωτογραφιών και βίντεο"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Επιλέξτε περισσότερα"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Να μην γίνει επιλογή περισσότερων"</string>
@@ -53,13 +54,14 @@
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Κατά τη χρήση της εφαρμογής"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"Αλλαγή σε ακριβή τοποθεσία"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"Διατήρηση κατά προσέγγιση τοποθεσίας"</string>
- <string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"Μόνο αυτήν τη φορά"</string>
+ <string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"Μόνο αυτή τη φορά"</string>
<string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"Να επιτρέπεται πάντα"</string>
<string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"Να επιτρέπεται η διαχείριση όλων των αρχείων"</string>
<string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"Να επιτρέπεται η πρόσβαση σε αρχεία μέσων"</string>
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Εφαρμογές"</string>
<string name="app_permissions" msgid="3369917736607944781">"Άδειες εφαρμογών"</string>
<string name="unused_apps" msgid="2058057455175955094">"Εφαρ. που δεν χρησιμοποιούνται"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Επεξεργασία επιλεγμένων φωτογραφιών για αυτή την εφαρμογή"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Όλες οι εφαρμογές χρησ/νται"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 εφαρμογές που δεν χρησ/νται"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Πρόσφατες αποφάσεις για άδειες"</string>
@@ -108,7 +110,7 @@
<!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) -->
<!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) -->
<string name="permission_access_always" msgid="1474641821883823446">"Να επιτρέπεται πάντα"</string>
- <string name="permission_access_only_foreground" msgid="7801170728159326195">"Μόνο με τη χρήση της εφαρμογής"</string>
+ <string name="permission_access_only_foreground" msgid="7801170728159326195">"Να επιτρέπεται μόνο κατά τη χρήση της εφαρμογής"</string>
<string name="permission_access_never" msgid="4647014230217936900">"Να μην επιτρέπεται"</string>
<string name="loading" msgid="4789365003890741082">"Φόρτωση…"</string>
<string name="all_permissions" msgid="6911125611996872522">"Όλες οι άδειες"</string>
@@ -187,9 +189,9 @@
<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>
+ <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Να επιτρέπεται μόνο κατά τη χρήση της εφαρμογής"</string>
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Να επιτρέπονται πάντα όλα"</string>
- <string name="app_permission_button_ask" msgid="3342950658789427">"Να ερωτώμαι κάθε φορά"</string>
+ <string name="app_permission_button_ask" msgid="3342950658789427">"Ερώτηση κάθε φορά"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Να μην επιτρέπεται"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Ακριβής τοποθεσία"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Τοποθεσία κατά προσέγγιση"</string>
@@ -202,7 +204,7 @@
<string name="assistant_mic_label" msgid="1011432357152323896">"Εμφάνιση χρήσης μικροφώνου βοηθού"</string>
<string name="unused_apps_category_title" msgid="2988455616845243901">"Ρυθμίσεις μη χρησιμοποιούμενων εφαρμογών"</string>
<string name="auto_revoke_label" msgid="5068393642936571656">"Καταργήστε τις άδειες, εάν η εφαρμογή δεν χρησιμοποιείται."</string>
- <string name="unused_apps_label" msgid="2595428768404901064">"Κατάργηση αδειών και απελευθέρωση χώρου"</string>
+ <string name="unused_apps_label" msgid="2595428768404901064">"Κατάργηση αδειών και αποδέσμευση χώρου"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Παύση δραστηριότητας αδρανούς εφαρμογής"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Κατάργηση αδειών, διαγραφή προσωρινών αρχείων και διακοπή ειδοποιήσεων"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Για την προστασία των δεδομένων σας, οι άδειες για αυτή την εφαρμογή θα καταργηθούν εάν η εφαρμογή δεν χρησιμοποιηθεί για μερικούς μήνες."</string>
@@ -219,8 +221,8 @@
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"Καταργήθηκαν οι άδειες <xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g> και <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g>"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"Καταργήθηκε η άδεια <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> και <xliff:g id="NUMBER">%2$s</xliff:g> ακόμη άδειες"</string>
<string name="unused_apps_page_title" msgid="6986983535677572559">"Εφαρμογές που δεν χρησιμοποιούνται"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"Εάν μια εφαρμογή δεν χρησιμοποιηθεί για λίγους μήνες:\n\n• Οι άδειες καταργούνται για την προστασία των δεδομένων σας\n• Οι ειδοποιήσεις διακόπτονται για την εξοικονόμηση μπαταρίας\n• Τα προσωρινά αρχεία καταργούνται για την απελευθέρωση χώρου\n\nΓια να επιτρέψετε ξανά τις άδειες και τις ειδοποιήσεις, ανοίξτε την εφαρμογή."</string>
- <string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Εάν μια εφαρμογή δεν χρησιμοποιηθεί για έναν μήνα:\n\n• Οι άδειες καταργούνται για την προστασία των δεδομένων σας\n• Τα προσωρινά αρχεία καταργούνται για την απελευθέρωση χώρου\n\nΓια να επιτρέψετε ξανά τις άδειες, ανοίξτε την εφαρμογή."</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"Εάν μια εφαρμογή δεν χρησιμοποιηθεί για λίγους μήνες:\n\n• Οι άδειες καταργούνται για την προστασία των δεδομένων σας\n• Οι ειδοποιήσεις διακόπτονται για την εξοικονόμηση μπαταρίας\n• Τα προσωρινά αρχεία καταργούνται για την αποδέσμευση χώρου\n\nΓια να επιτρέψετε ξανά τις άδειες και τις ειδοποιήσεις, ανοίξτε την εφαρμογή."</string>
+ <string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Εάν μια εφαρμογή δεν χρησιμοποιηθεί για έναν μήνα:\n\n• Οι άδειες καταργούνται για την προστασία των δεδομένων σας\n• Τα προσωρινά αρχεία καταργούνται για την αποδέσμευση χώρου\n\nΓια να επιτρέψετε ξανά τις άδειες, ανοίξτε την εφαρμογή."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Τελευταίο άνοιγμα πάνω από # μήνα πριν}other{Τελευταίο άνοιγμα πάνω από # μήνες πριν}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"Τελευταίο άνοιγμα εφαρμογής <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="last_opened_summary_short" msgid="1646067226191176825">"Τελευταίο άνοιγμα <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -456,13 +458,13 @@
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Εμφάνιση εικονιδίου στη γραμμή κατάστασης όταν το μικρόφωνο χρησιμοποιείται για την ενεργοποίηση του φωνητικού βοηθού"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να έχει πρόσβαση σε φωτογραφίες και μέσα στη συσκευή σας;"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να έχει πρόσβαση στις επαφές σας;"</string>
- <string name="permgrouprequest_location" msgid="6990232580121067883">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string>
+ <string name="permgrouprequest_location" msgid="6990232580121067883">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Η εφαρμογή θα έχει πρόσβαση στην τοποθεσία μόνο κατά τη διάρκεια χρήσης της εφαρμογής"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να έχει πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Αυτή η εφαρμογή θέλει να έχει συνεχώς πρόσβαση στην τοποθεσία σας, ακόμη και όταν δεν χρησιμοποιείτε την εφαρμογή. "<annotation id="link">"Εγκρίνετε το αίτημα στις ρυθμίσεις."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Αλλαγή πρόσβασης στην τοποθεσία για την εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;;"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Αυτή η εφαρμογή θέλει να έχει συνεχώς πρόσβαση στην τοποθεσία σας, ακόμη και όταν δεν χρησιμοποιείτε την εφαρμογή. "<annotation id="link">"Εγκρίνετε το αίτημα στις ρυθμίσεις."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Να επιτρέπεται η εύρεση, η σύνδεση κι ο προσδιορισμός σχετικής τοποθεσίας των κοντινών συσκευών από &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;;"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η εύρεση, η σύνδεση κι ο προσδιορισμός της σχετικής τοποθεσίας των κοντινών συσκευών;"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Να επιτρέπεται η εύρεση, η σύνδεση κι ο προσδιορισμός σχετικής τοποθεσίας των κοντινών συσκευών από &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;; "<annotation id="link">"Έγκριση στις Ρυθμίσεις."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Αλλαγή της πρόσβασης της εφαρμογής <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> στην τοποθεσία από κατά προσέγγιση σε ακριβή;"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να έχει πρόσβαση στην κατά προσέγγιση τοποθεσία αυτής της συσκευής;"</string>
@@ -471,10 +473,10 @@
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να έχει πρόσβαση στο ημερολόγιό σας;"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η αποστολή και η προβολή μηνυμάτων SMS;"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να έχει πρόσβαση σε φωτογραφίες, μέσα και αρχεία στη συσκευή σας;"</string>
- <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε &lt;b&gt;φωτογραφίες, βίντεο, μουσική και ήχο&lt;/b&gt; της συσκευής;"</string>
+ <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε &lt;b&gt;φωτογραφίες, βίντεο, μουσική και ήχο&lt;/b&gt; της συσκευής;"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε &lt;b&gt;φωτογραφίες, βίντεο, μουσική, ήχο και άλλα αρχεία&lt;/b&gt; της συσκευής;"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στη μουσική και στα αρχεία ήχου αυτής της συσκευής;"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στις φωτογραφίες και τα βίντεο αυτής της συσκευής;"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Να επιτρέπεται στην &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στις φωτογραφίες και τα βίντεο αυτής της συσκευής;"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε περισσότερες φωτογραφίες και βίντεο αυτής της συσκευής;"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η εγγραφή ήχου;"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Αυτή η εφαρμογή θα μπορεί να εγγράφει ήχο μόνο όταν τη χρησιμοποιείτε"</string>
diff --git a/PermissionController/res/values-en-rAU/strings.xml b/PermissionController/res/values-en-rAU/strings.xml
index 77c6837ad..1ac5ad30a 100644
--- a/PermissionController/res/values-en-rAU/strings.xml
+++ b/PermissionController/res/values-en-rAU/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Allow all"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Always allow all"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Allow limited access"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Select photos and videos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Select more"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Don\'t select more"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"App permissions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Unused apps"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Edit selected photos for this app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No unused apps"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Zero unused apps"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Recent permission decisions"</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"These apps can view your screen, actions and inputs, perform actions, and control the display."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"Default digital assistant app"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"Digital assistant app"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"Assist apps can help you based on information from the screen that you’re viewing. Some apps support both launcher and voice input services to give you integrated assistance."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"Assist apps can help you, based on information from the screen that you’re viewing. Some apps support both Launcher and voice input services to give you integrated assistance."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Default browser app"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Browser app"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Apps that give you access to the Internet and display links that you tap"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"Security and privacy status. <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"Security settings"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"Permissions"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Security &amp; privacy"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Security and privacy"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Check status"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Your privacy controls"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"More settings"</string>
diff --git a/PermissionController/res/values-en-rCA/strings.xml b/PermissionController/res/values-en-rCA/strings.xml
index ef5fb9086..be0a1448d 100644
--- a/PermissionController/res/values-en-rCA/strings.xml
+++ b/PermissionController/res/values-en-rCA/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Allow all"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Always allow all"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Allow limited access"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Select photos and videos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Select more"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Don’t select more"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"App permissions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Unused apps"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Edit selected photos for this app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No unused apps"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 unused apps"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Recent permission decisions"</string>
@@ -419,7 +421,7 @@
<string name="ongoing_usage_dialog_separator" msgid="1715181526581520068">", "</string>
<string name="ongoing_usage_dialog_last_separator" msgid="4170995004748832163">" and "</string>
<string name="default_app_search_keyword" msgid="8330125736889689743">"default apps"</string>
- <string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Microphone &amp; Camera"</string>
+ <string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Microphone and camera"</string>
<string name="settings_button" msgid="4414988414732479636">"Settings"</string>
<string name="default_apps" msgid="5119201969348748639">"Default apps"</string>
<string name="no_default_apps" msgid="2593466527182950231">"No default apps"</string>
diff --git a/PermissionController/res/values-en-rGB/strings.xml b/PermissionController/res/values-en-rGB/strings.xml
index 9feaad707..db2cd1beb 100644
--- a/PermissionController/res/values-en-rGB/strings.xml
+++ b/PermissionController/res/values-en-rGB/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Allow all"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Always allow all"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Allow limited access"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Select photos and videos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Select more"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Don\'t select more"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"App permissions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Unused apps"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Edit selected photos for this app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No unused apps"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Zero unused apps"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Recent permission decisions"</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"These apps can view your screen, actions and inputs, perform actions, and control the display."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"Default digital assistant app"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"Digital assistant app"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"Assist apps can help you based on information from the screen that you’re viewing. Some apps support both launcher and voice input services to give you integrated assistance."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"Assist apps can help you, based on information from the screen that you’re viewing. Some apps support both Launcher and voice input services to give you integrated assistance."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Default browser app"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Browser app"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Apps that give you access to the Internet and display links that you tap"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"Security and privacy status. <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"Security settings"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"Permissions"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Security &amp; privacy"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Security and privacy"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Check status"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Your privacy controls"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"More settings"</string>
diff --git a/PermissionController/res/values-en-rIN/strings.xml b/PermissionController/res/values-en-rIN/strings.xml
index 9feaad707..db2cd1beb 100644
--- a/PermissionController/res/values-en-rIN/strings.xml
+++ b/PermissionController/res/values-en-rIN/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Allow all"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Always allow all"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Allow limited access"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Select photos and videos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Select more"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Don\'t select more"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"App permissions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Unused apps"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Edit selected photos for this app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No unused apps"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Zero unused apps"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Recent permission decisions"</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"These apps can view your screen, actions and inputs, perform actions, and control the display."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"Default digital assistant app"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"Digital assistant app"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"Assist apps can help you based on information from the screen that you’re viewing. Some apps support both launcher and voice input services to give you integrated assistance."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"Assist apps can help you, based on information from the screen that you’re viewing. Some apps support both Launcher and voice input services to give you integrated assistance."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Default browser app"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Browser app"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Apps that give you access to the Internet and display links that you tap"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"Security and privacy status. <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"Security settings"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"Permissions"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Security &amp; privacy"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Security and privacy"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Check status"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Your privacy controls"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"More settings"</string>
diff --git a/PermissionController/res/values-en-rXC/strings.xml b/PermissionController/res/values-en-rXC/strings.xml
index c712a6f2d..a47553076 100644
--- a/PermissionController/res/values-en-rXC/strings.xml
+++ b/PermissionController/res/values-en-rXC/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎More info‎‏‎‎‏‎"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‎Allow all‎‏‎‎‏‎"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‏‎Always allow all‎‏‎‎‏‎"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎Allow limited access‎‏‎‎‏‎"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‎‎Select photos and videos‎‏‎‎‏‎"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‏‎Select more‎‏‎‎‏‎"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎Don’t select more‎‏‎‎‏‎"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‏‎‎‏‎‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎Apps‎‏‎‎‏‎"</string>
<string name="app_permissions" msgid="3369917736607944781">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‎‏‎App permissions‎‏‎‎‏‎"</string>
<string name="unused_apps" msgid="2058057455175955094">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎Unused apps‎‏‎‎‏‎"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‎‎Edit selected photos for this app‎‏‎‎‏‎"</string>
<string name="no_unused_apps" msgid="12809387670415295">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎No unused apps‎‏‎‎‏‎"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎0 unused apps‎‏‎‎‏‎"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‎‎‎‎‎‎Recent permission decisions‎‏‎‎‏‎"</string>
diff --git a/PermissionController/res/values-es-rUS-v34/strings.xml b/PermissionController/res/values-es-rUS-v34/strings.xml
index e180c0b7c..06ef5deda 100644
--- a/PermissionController/res/values-es-rUS-v34/strings.xml
+++ b/PermissionController/res/values-es-rUS-v34/strings.xml
@@ -22,6 +22,6 @@
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"Administra el acceso de las apps a Health Connect"</string>
<string name="location_settings" msgid="8863940440881290182">"Acceso a la ubicación"</string>
- <string name="mic_toggle_description" msgid="1504101620086616040">"Para apps y servicios. Aunque se desactive este parámetro de configuración, es posible que se sigan compartiendo los datos del micrófono cuando llames a un número de emergencia."</string>
+ <string name="mic_toggle_description" msgid="1504101620086616040">"Para apps y servicios. Aunque se desactive este parámetro de configuración, es posible que se sigan compartiendo los datos del micrófono cuando llames a un número de emergencia"</string>
<string name="location_settings_subtitle" msgid="6846532794702613851">"Para apps y servicios"</string>
</resources>
diff --git a/PermissionController/res/values-es-rUS/strings.xml b/PermissionController/res/values-es-rUS/strings.xml
index a59575bd3..7e6c10e08 100644
--- a/PermissionController/res/values-es-rUS/strings.xml
+++ b/PermissionController/res/values-es-rUS/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Más información"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permitir todo"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Permitir todo siempre"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Permitir el acceso limitado"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Seleccionar fotos y videos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Seleccionar más"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"No seleccionar más"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"Permisos de la app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps que no usas"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Editar las fotos seleccionadas para esta app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No hay ninguna app sin usar"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 apps en desuso"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Acciones recientes de permisos"</string>
@@ -68,7 +70,7 @@
<string name="auto_permission_manager_summary" msgid="9157438376234301354">"Administra el acceso a datos del calendario, llamadas y mucho más"</string>
<string name="granted_permission_decision" msgid="7824827491551861365">"Le diste a <xliff:g id="APP_NAME">%1$s</xliff:g> acceso a <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="denied_permission_decision" msgid="5308961501779563781">"Le negaste a <xliff:g id="APP_NAME">%1$s</xliff:g> el acceso a <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
- <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Hoy}=1{Hace 1 día}many{Hace # días}other{Hace # días}}"</string>
+ <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Hoy}=1{Hace 1 día}many{Hace # de días}other{Hace # días}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Inhabilitar app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Si inhabilitas esta app, es posible que Android y otras apps ya no funcionen correctamente. Ten en cuenta que no puedes borrar esta app, ya que vino preinstalada en el dispositivo. Si la inhabilitas, se desactivará y se ocultará en tu dispositivo."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Administrador de permisos"</string>
@@ -139,15 +141,15 @@
<string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string>
<string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string>
<string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string>
- <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# día}many{# días}other{# días}}"</string>
- <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string>
+ <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# día}many{# de días}other{# días}}"</string>
+ <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}many{# de horas}other{# horas}}"</string>
<string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string>
<string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# segundo}many{# segundos}other{# segundos}}"</string>
<string name="permission_usage_any_permission" msgid="6358023078298106997">"Cualquier permiso"</string>
<string name="permission_usage_any_time" msgid="3802087027301631827">"Cualquier momento"</string>
- <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Último día (#)}many{Últimos # días}other{Últimos # días}}"</string>
- <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Última hora (#)}many{Últimas # horas}other{Últimas # horas}}"</string>
- <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Último minuto (#)}many{Últimos # minutos}other{Últimos # minutos}}"</string>
+ <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Último día (#)}many{Últimos # de días}other{Últimos # días}}"</string>
+ <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Última hora (#)}many{Últimas # de horas}other{Últimas # horas}}"</string>
+ <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Último minuto (#)}many{Últimos # de minutos}other{Últimos # minutos}}"</string>
<string name="no_permission_usages" msgid="9119517454177289331">"Ningún uso de permisos"</string>
<string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Acceso más reciente en cualquier momento"</string>
<string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Acceso más reciente en los últimos 7 días"</string>
@@ -161,9 +163,9 @@
<string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Uso de permisos en la última hora"</string>
<string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Uso de permisos en los últimos 15 minutos"</string>
<string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Uso de permisos en el último minuto"</string>
- <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{No se usó en el último día (#)}many{No se usó en los últimos # días}other{No se usó en los últimos # días}}"</string>
- <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{No se usó en la última hora (#)}many{No se usó en las últimas # horas}other{No se usó en las últimas # horas}}"</string>
- <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{En uso por parte de 1 app}many{En uso por parte de # apps}other{En uso por parte de # apps}}"</string>
+ <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{No se usó en el último día (#)}many{No se usó en los últimos # de días}other{No se usó en los últimos # días}}"</string>
+ <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{No se usó en la última hora (#)}many{No se usó en las últimas # de horas}other{No se usó en las últimas # horas}}"</string>
+ <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{En uso por parte de 1 app}many{En uso por parte de # de apps}other{En uso por parte de # apps}}"</string>
<string name="permission_usage_view_details" msgid="6675335735468752787">"Ver todo en el panel"</string>
<string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrado por: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"Quitar filtro"</string>
@@ -193,7 +195,7 @@
<string name="app_permission_button_deny" msgid="6016454069832050300">"No permitir"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Ubicación precisa"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Ubicación aproximada"</string>
- <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usar la ubicación precisa"</string>
+ <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Ubicación precisa"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Cuando la ubicación precisa está desactivada, las apps acceden a la ubicación aproximada"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Permiso de <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Acceso a <xliff:g id="PERM">%1$s</xliff:g> para esta app"</string>
@@ -253,17 +255,17 @@
<string name="ask_header" msgid="2633816846459944376">"Preguntar siempre"</string>
<string name="denied_header" msgid="903209608358177654">"Sin permiso"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ver más apps 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>
- <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string>
- <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string>
- <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}many{# segundos}other{# segundos}}"</string>
+ <string name="days" msgid="609563020985571393">"{count,plural, =1{1 día}many{# de días}other{# días}}"</string>
+ <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}many{# de horas}other{# horas}}"</string>
+ <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# de minutos}other{# minutos}}"</string>
+ <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}many{# de segundos}other{# segundos}}"</string>
<string name="permission_reminders" msgid="6528257957664832636">"Recordatorios de permisos"</string>
<string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 app en desuso"</string>
<string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> apps que no usas"</string>
<string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Se quitaron los permisos para proteger tu privacidad. Presiona para revisar el cambio"</string>
<string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Se quitaron los permisos de las apps que no se usan"</string>
<string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Hace varios meses que no usas algunas apps. Presiona para verlas."</string>
- <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# app en desuso}many{# apps en desuso}other{# apps en desuso}}"</string>
+ <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# app en desuso}many{# de apps en desuso}other{# apps en desuso}}"</string>
<string name="unused_apps_notification_content" msgid="9195026773244581246">"Se quitaron los permisos y archivos temporales, y se detuvieron las notificaciones. Presiona para revisar estas opciones."</string>
<string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Revisar apps a las que se les quitaron permisos"</string>
<string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Se quitaron los permisos y los archivos temporales, y se detuvieron las notificaciones de las apps que no usaste durante un tiempo."</string>
@@ -272,7 +274,7 @@
<string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Mientras conducías, le otorgaste acceso a <xliff:g id="APP">%1$s</xliff:g> a <xliff:g id="PERMISSION">%2$s</xliff:g>"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Mientras conducías, le otorgaste acceso a <xliff:g id="APP">%1$s</xliff:g> a <xliff:g id="PERMISSION_1">%2$s</xliff:g> y <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"Mientras conducías, le otorgaste <xliff:g id="COUNT">%1$d</xliff:g> permisos a <xliff:g id="APP">%2$s</xliff:g>"</string>
- <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_0">%1$s</xliff:g> y otra app}many{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_1">%1$s</xliff:g> y otras # apps}other{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_1">%1$s</xliff:g> y otras # apps}}"</string>
+ <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_0">%1$s</xliff:g> y otra app}many{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_1">%1$s</xliff:g> y otras # de apps}other{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_1">%1$s</xliff:g> y otras # apps}}"</string>
<string name="go_to_settings" msgid="1053735612211228335">"Ir a Configuración"</string>
<string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Hace varios meses que no usas algunas apps"</string>
<string name="permissions_removed_category_title" msgid="1064754271178447643">"Apps a las que se les quitaron permisos"</string>
@@ -340,17 +342,17 @@
<string name="no_apps_allowed" msgid="7718822655254468631">"No se le otorgó permiso a ninguna app"</string>
<string name="no_apps_allowed_full" msgid="8011716991498934104">"Ninguna app tiene el permiso para todos los archivos"</string>
<string name="no_apps_allowed_scoped" msgid="4908850477787659501">"Ninguna app tiene el permiso solo para contenido multimedia"</string>
- <string name="no_apps_denied" msgid="7663435886986784743">"No hay permisos rechazados"</string>
+ <string name="no_apps_denied" msgid="7663435886986784743">"No hay apps rechazadas"</string>
<string name="car_permission_selected" msgid="180837028920791596">"Seleccionado"</string>
<string name="settings" msgid="5409109923158713323">"Configuración"</string>
<string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"<xliff:g id="SERVICE_NAME">%s</xliff:g> tiene acceso completo a tu dispositivo"</string>
<string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"<xliff:g id="NUM_SERVICES">%s</xliff:g> apps de accesibilidad tienen acceso completo a tu dispositivo"</string>
<string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> puede ver tu pantalla, lo que haces y lo que introduces, así como realizar acciones y controlar la pantalla."</string>
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Estas apps pueden ver tu pantalla, lo que haces y lo que escribes. También pueden realizar acciones y controlar la pantalla."</string>
- <string name="role_assistant_label" msgid="4727586018198208128">"App de asistente digital pred."</string>
+ <string name="role_assistant_label" msgid="4727586018198208128">"App de asistente digital predeterminada"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"App de asistente digital"</string>
<string name="role_assistant_description" msgid="6622458130459922952">"Las aplicaciones de asistencia pueden brindarte ayuda en función de la pantalla que estás viendo. Para ofrecerte asistencia integrada, algunas aplicaciones son compatibles con los servicios de selector y entrada de voz."</string>
- <string name="role_browser_label" msgid="2877796144554070207">"Navegador predet."</string>
+ <string name="role_browser_label" msgid="2877796144554070207">"Navegador predeterminado"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"App de navegador"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Apps que te permiten acceder a Internet y ver los vínculos que presionas"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"¿Quieres establecer <xliff:g id="APP_NAME">%1$s</xliff:g> como tu app de navegador predeterminada?"</string>
@@ -362,7 +364,7 @@
<string name="role_dialer_request_description" msgid="6288839625724909320">"Esta app obtendrá acceso a la cámara, los contactos, micrófono, teléfono y SMS"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"marcador"</string>
<string name="role_sms_label" msgid="8456999857547686640">"App de SMS predeterminada"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"App de SMS"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"app de SMS"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Apps que te permiten usar tu número de teléfono para enviar y recibir mensajes de texto cortos, fotos, videos y mucho más"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"¿Quieres establecer <xliff:g id="APP_NAME">%1$s</xliff:g> como app de SMS predeterminada?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Se le otorgará acceso a esta app a tu cámara, contactos, archivos y contenido multimedia, micrófono, teléfono y SMS"</string>
@@ -427,7 +429,7 @@
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Abrir vínculos"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Predeterminadas de trabajo"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ninguna"</string>
- <string name="default_app_system_default" msgid="6218386768175513760">"(Predeterminada de sistema)"</string>
+ <string name="default_app_system_default" msgid="6218386768175513760">"(Predeterminada del sistema)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Sin apps"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Seleccionada"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Se seleccionó: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
@@ -446,7 +448,7 @@
<string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> solicita subir un informe de errores de este dispositivo generado el <xliff:g id="DATE">%2$s</xliff:g> a las <xliff:g id="TIME">%3$s</xliff:g>. Los informes de errores pueden incluir información personal sobre tu dispositivo o registrada por apps, como nombres de usuario, datos de ubicación, identificadores del dispositivo y datos de red. Solo debes compartir los informes de errores con personas y apps de confianza. ¿Permitir que <xliff:g id="APP_NAME_1">%4$s</xliff:g> suba un informe de errores?"</string>
<string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Se produjo un error al procesar el informe de errores para <xliff:g id="APP_NAME">%1$s</xliff:g>. Por lo tanto, no es posible compartir los datos detallados de depuración. Lamentamos la interrupción."</string>
<string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Permitir"</string>
- <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Denegar"</string>
+ <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Rechazar"</string>
<string name="adjust_user_sensitive_title" msgid="4196724451314280527">"Configuración avanzada"</string>
<string name="menu_adjust_user_sensitive" msgid="6497923610654425780">"Configuración avanzada"</string>
<string name="adjust_user_sensitive_globally_title" msgid="8649190949066029174">"Mostrar uso de las apps del sistema"</string>
@@ -456,7 +458,7 @@
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mostrar ícono en la barra de estado cuando se use el micrófono para activar la función \"Asistente de voz\""</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a las fotos y el contenido multimedia del dispositivo?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tus contactos?"</string>
- <string name="permgrouprequest_location" msgid="6990232580121067883">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación de este dispositivo?"</string>
+ <string name="permgrouprequest_location" msgid="6990232580121067883">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación de este dispositivo?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"La app solo tendrá acceso a la ubicación cuando esté en uso"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación de este dispositivo?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Es posible que esta app quiera acceder a tu ubicación todo el tiempo, incluso cuando no la uses. "<annotation id="link">"Permite el acceso en Configuración."</annotation></string>
@@ -465,7 +467,7 @@
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; encuentre dispositivos cercanos, se conecte a ellos y determine su ubicación relativa?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; encuentre dispositivos cercanos, se conecte a ellos y determine su ubicación relativa? "<annotation id="link">"Hazlo en Configuración."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"¿Quieres cambiar el acceso a la ubicación de <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> de aproximada a precisa?"</string>
- <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"¿Deseas permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación aproximada de este dispositivo?"</string>
+ <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación aproximada de este dispositivo?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Precisa"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Aproximada"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tu calendario?"</string>
@@ -483,7 +485,7 @@
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"¿Cambiar el acceso al micrófono de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Esta app quiere grabar audio todo el tiempo, incluso cuando no la uses. "<annotation id="link">"Permite el acceso en Configuración."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tu actividad física?"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tome fotos y grabe videos?"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tome fotos y grabe videos?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"La app solo podrá tomar fotos y grabar videos cuando esté en uso"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tome fotos y grabe videos?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Es posible que esta app quiera tomar fotos y grabar videos todo el tiempo, incluso cuando no la estés usando. "<annotation id="link">"Permite el acceso en Configuración."</annotation></string>
@@ -493,10 +495,10 @@
<string name="permgrouprequest_phone" msgid="1829234136997316752">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga y administre las llamadas telefónicas?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los datos de sensores de tus signos vitales?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Esta app quiere acceder a los datos de sensores de tus signos vitales todo el tiempo, incluso cuando no la usas. Para realizar este cambio, "<annotation id="link">"ve a configuración."</annotation></string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los datos de los sensores de tus signos vitales?"</string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los datos de los sensores de tus signos vitales?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Para permitir que esta app acceda a datos del sensor corporal todo el tiempo, incluso cuando no la uses, "<annotation id="link">"ve a la configuración."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"¿Quieres seguir permitiendo que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a datos del sensor corporal mientras está en uso?"</string>
- <string name="permgrouprequest_notifications" msgid="6396739062335106181">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; te envíe notificaciones?"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; te envíe notificaciones?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Permisos controlados"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> tiene acceso a la ubicación"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Tu organización permite que <xliff:g id="APP_NAME">%1$s</xliff:g> acceda a tu ubicación"</string>
@@ -585,7 +587,7 @@
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Mostrar acceso a portapapeles"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Muestra un mensaje cuando las apps accedan a texto, imágenes y otro contenido que hayas copiado"</string>
<string name="show_password_title" msgid="2877269286984684659">"Mostrar contraseñas"</string>
- <string name="show_password_summary" msgid="1110166488865981610">"Mostrar caracteres brevemente mientras escribes"</string>
+ <string name="show_password_summary" msgid="1110166488865981610">"Muestra los caracteres brevemente mientras escribes"</string>
<string name="permission_rationale_message_location" msgid="2153841534298068414">"Esta app indicó que podría compartir datos de ubicación con terceros"</string>
<string name="permission_rationale_location_title" msgid="2404797182678793506">"Uso compartido de datos y ubicación"</string>
<string name="permission_rationale_data_sharing_source_title" msgid="6874604543125814316">"De dónde proviene la información de uso compartido de datos"</string>
@@ -614,8 +616,8 @@
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Más información sobre el uso compartido de datos"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Tus datos de ubicación ahora se comparten con terceros"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Tus datos de ubicación ahora se comparten con terceros para publicidad o marketing"</string>
- <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Se actualizó en el último día}=1{Se actualizó en el último día}many{Se actualizó en los últimos # de días}other{Se actualizó en los últimos # días}}"</string>
- <string name="no_updates_at_this_time" msgid="9031085635689982935">"No hay novedades por el momento"</string>
+ <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Se actualizó en el último día}=1{Se actualizó en el último día}many{Se actualizó hace # de días}other{Se actualizó hace # días}}"</string>
+ <string name="no_updates_at_this_time" msgid="9031085635689982935">"No hay actualizaciones por el momento"</string>
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Actualizaciones del uso compartido de datos"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Algunas apps cambiaron la forma en que podrían compartir tus datos de ubicación"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Configuración"</string>
diff --git a/PermissionController/res/values-es-v33/strings.xml b/PermissionController/res/values-es-v33/strings.xml
index 69b1ce6f5..0104103fe 100644
--- a/PermissionController/res/values-es-v33/strings.xml
+++ b/PermissionController/res/values-es-v33/strings.xml
@@ -29,7 +29,7 @@
<string name="safety_center_entry_content_description" msgid="3639565652938224321">"<xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>."</string>
<string name="safety_center_more_issues_card_title" msgid="7425844746197493312">"Más alertas"</string>
<string name="safety_center_dismissed_issues_card_title" msgid="2340129842725145733">"Alertas ignoradas"</string>
- <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Ampliar para ver una alerta más}many{Ampliar para ver # alertas más}other{Ampliar para ver # alertas más}}"</string>
+ <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Amplía para ver una alerta más}many{Amplía para ver # alertas más}other{Amplía para ver # alertas más}}"</string>
<string name="safety_center_issue_card_prefix_content_description" msgid="1447445289637043544">"Alerta. <xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>"</string>
<string name="safety_center_resolved_issue_fallback" msgid="8548932070610766651">"Acción completada"</string>
<string name="safety_center_qs_status_summary" msgid="5193925895830451177">"Comprueba los ajustes que pueden proteger más tu dispositivo"</string>
diff --git a/PermissionController/res/values-es/strings.xml b/PermissionController/res/values-es/strings.xml
index cb76d3560..3669f050e 100644
--- a/PermissionController/res/values-es/strings.xml
+++ b/PermissionController/res/values-es/strings.xml
@@ -29,11 +29,12 @@
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"Aplicación no encontrada"</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"No permitir"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"No permitir y no volver a preguntar"</string>
- <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantener \"Mientras la aplicación se esté usando\""</string>
+ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantener \"Cuando la aplicación esté en uso\""</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantener \"Solo esta vez\""</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Más información"</string>
- <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permitir todos"</string>
+ <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permitir acceso a todos"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Permitir todos siempre"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Permitir acceso limitado"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Seleccionar fotos y vídeos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Seleccionar más"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"No seleccionar más"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplicaciones"</string>
<string name="app_permissions" msgid="3369917736607944781">"Permisos de aplicaciones"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplicaciones no usadas"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Cambia las fotos seleccionadas para esta aplicación"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No hay aplicaciones no usadas"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 aplicaciones no usadas"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Decisiones recientes de permisos"</string>
@@ -193,7 +195,7 @@
<string name="app_permission_button_deny" msgid="6016454069832050300">"No permitir"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Ubicación precisa"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Ubicación aproximada"</string>
- <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usar ubicación precisa"</string>
+ <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usar ubic. precisa"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Cuando la ubicación precisa está desactivada, las aplicaciones pueden consultar tu ubicación aproximada"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Permiso de <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Acceso a <xliff:g id="PERM">%1$s</xliff:g> para esta aplicación"</string>
@@ -347,8 +349,8 @@
<string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"<xliff:g id="NUM_SERVICES">%s</xliff:g> aplicaciones de accesibilidad tienen acceso completo a tu dispositivo"</string>
<string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> puede ver tu pantalla, lo que haces y lo que introduces; realizar acciones; y controlar la pantalla."</string>
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Estas aplicaciones pueden ver tu pantalla, lo que haces y lo que introduces; realizar acciones; y controlar la pantalla."</string>
- <string name="role_assistant_label" msgid="4727586018198208128">"Asistente predeterminado"</string>
- <string name="role_assistant_short_label" msgid="3369003713187703399">"Asistente digital"</string>
+ <string name="role_assistant_label" msgid="4727586018198208128">"Aplicación de asistente digital predeterminada"</string>
+ <string name="role_assistant_short_label" msgid="3369003713187703399">"Aplicación de asistente digital"</string>
<string name="role_assistant_description" msgid="6622458130459922952">"Las aplicaciones de asistencia te ayudan según la información que aparezca en la pantalla. Algunas aplicaciones admiten tanto el menú de aplicaciones como los servicios de entrada de voz para ofrecerte asistencia integrada."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Aplicación de navegador predeterminada"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Aplicación de navegador"</string>
@@ -491,8 +493,8 @@
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Esta aplicación debe hacer fotografías y grabar vídeos en todo momento, aunque no la estés usando. "<annotation id="link">"Puedes darle este permiso en Ajustes."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los registros de llamadas del teléfono?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga y gestione llamadas?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los datos del sensor sobre tus constantes vitales?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Esta aplicación quiere acceder a los datos de sensores de tus constantes vitales todo el tiempo, incluso cuando no la uses. Para hacer este cambio, "<annotation id="link">"ve a los ajustes"</annotation>"."</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tus datos de constantes vitales del sensor?"</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Esta aplicación quiere acceder a los datos de sensores de tus constantes vitales siempre, incluso cuando no la uses. Para hacer este cambio, "<annotation id="link">"ve a los ajustes"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los datos de sensores de tus constantes vitales?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Para permitir que esta aplicación acceda a datos de sensores corporales todo el tiempo, incluso cuando no la uses, "<annotation id="link">"ve a los ajustes"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"¿Seguir permitiendo que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los datos de los sensores corporales mientras se usa?"</string>
@@ -598,7 +600,7 @@
<string name="permission_rationale_location_settings_title" msgid="7204145004850190953">"Tus datos de ubicación"</string>
<string name="permission_rationale_permission_settings_message" msgid="631286040979660267">"Cambia el acceso de esta aplicación en la "<annotation id="link">"configuración de privacidad"</annotation></string>
<string name="permission_rationale_purpose_app_functionality" msgid="8397736681065841405">"Funcionalidad de la aplicación"</string>
- <string name="permission_rationale_purpose_analytics" msgid="2070800501189620712">"Analítica"</string>
+ <string name="permission_rationale_purpose_analytics" msgid="2070800501189620712">"Estadísticas"</string>
<string name="permission_rationale_purpose_developer_communications" msgid="6453047018892062374">"Comunicaciones del desarrollador"</string>
<string name="permission_rationale_purpose_advertising" msgid="7156966429245180236">"Publicidad o marketing"</string>
<string name="permission_rationale_purpose_fraud_prevention_security" msgid="4262104770357031902">"Prevención de fraudes, seguridad y cumplimiento"</string>
@@ -610,12 +612,12 @@
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Cambios en los datos compartidos de ubicación"</string>
<string name="data_sharing_updates_summary" msgid="764113985772233889">"Consulta las aplicaciones que han cambiado cómo comparten tus datos de ubicación"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Estas aplicaciones han cambiado cómo pueden compartir tus datos de ubicación. Puede que no los hayan compartido antes, o que ahora lo hagan con fines publicitarios o de marketing."</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Los desarrolladores de estas aplicaciones han proporcionado información a una tienda de aplicaciones sobre sus formas de compartir datos. Es posible que la actualicen con el tiempo.\n\nLas formas en que se comparten los datos pueden variar en función de la versión de la aplicación, el modo en que la utilices, el país donde la uses y tu edad."</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Los desarrolladores de estas aplicaciones han proporcionado información a una tienda de aplicaciones sobre sus formas de compartir datos. Es posible que la actualicen con el tiempo.\n\nLas formas en que se comparten los datos pueden variar en función de la versión de la aplicación, el modo en que la utilices, la zona donde la uses y tu edad."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Más información sobre cómo se comparten los datos"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Tus datos de ubicación ahora se comparten con terceros"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Tus datos de ubicación ahora se comparten con terceros para fines de publicidad o marketing"</string>
<string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Se ha actualizado en el último día}=1{Se ha actualizado en el último día}many{Se ha actualizado en los últimos # días}other{Se ha actualizado en los últimos # días}}"</string>
- <string name="no_updates_at_this_time" msgid="9031085635689982935">"No hay novedades por el momento"</string>
+ <string name="no_updates_at_this_time" msgid="9031085635689982935">"No hay cambios por el momento"</string>
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Cambios en cómo se comparten los datos"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Algunas aplicaciones han cambiado cómo pueden compartir tus datos de ubicación"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Ajustes"</string>
diff --git a/PermissionController/res/values-et/strings.xml b/PermissionController/res/values-et/strings.xml
index 027b554e0..fc85231da 100644
--- a/PermissionController/res/values-et/strings.xml
+++ b/PermissionController/res/values-et/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Lisateave"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Anna juurdepääs kõigile"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Luba alati kõik"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Luba piiratud juurdepääs"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Valige fotod ja videod"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Valige rohkem"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Ära vali rohkem"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Rakendused"</string>
<string name="app_permissions" msgid="3369917736607944781">"Rakenduse load"</string>
<string name="unused_apps" msgid="2058057455175955094">"Kasutamata rakendused"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Selle rakenduse jaoks valitud fotode muutmine"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Kasutamata rakendusi pole"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 kasutamata rakendust"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Hiljutised lubade otsused"</string>
@@ -203,7 +205,7 @@
<string name="unused_apps_category_title" msgid="2988455616845243901">"Kasutamata rakenduse seaded"</string>
<string name="auto_revoke_label" msgid="5068393642936571656">"Eemalda load, kui rakendust ei kasutata"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Eemalda load ja vabasta ruumi"</string>
- <string name="unused_apps_label_v2" msgid="7058776770056517980">"Tegevusetuna rakenduse tegevuste peatamine"</string>
+ <string name="unused_apps_label_v2" msgid="7058776770056517980">"Kasutamata rakenduse tegevuste peatamine"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Eemaldatakse load, kustutatakse ajutised failid ja peatatakse märguanded"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Teie andmete kaitsmiseks eemaldatakse selle rakenduse load, kui seda mõne kuu jooksul ei kasutata."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Teie andmete kaitsmiseks eemaldatakse selle rakenduse järgmised load, kui rakendust mõne kuu jooksul ei kasutata: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
@@ -361,7 +363,7 @@
<string name="role_dialer_request_title" msgid="5959618560705912058">"Kas soovite määrata rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> telefoni vaikerakenduseks?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"Sellele rakendusele antakse juurdepääs teie kaamerale, kontaktidele, mikrofonile, telefonile ja SMS-idele"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"helistamine"</string>
- <string name="role_sms_label" msgid="8456999857547686640">"SMS-vaikerakendus"</string>
+ <string name="role_sms_label" msgid="8456999857547686640">"SMS-i vaikerakendus"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"SMS-rakendus"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Rakendused, millel on luba kasutada teie telefoninumbrit lühisõnumite, fotode, videote jms saatmiseks ja vastuvõtmiseks"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Kas soovite määrata rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> SMS-i vaikerakenduseks?"</string>
@@ -472,7 +474,7 @@
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; saata ja vaadata SMS-sõnumeid?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs seadmes olevatele fotodele, meediale ja failidele?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Kas anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;gt seadmes juurdep. &lt;b&gt;fotodele, videotele, muusikale ja helidele&lt;/b&gt;?"</string>
- <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdep. &lt;b&gt;foto-, video-, muusika-, heli- ja muudele failidele&lt;/b&gt;?"</string>
+ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Kas anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs &lt;b&gt;foto-, video-, muusika-, heli- ja muudele failidele&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Kas anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; selles seadmes juurdepääs muusikale ja helifailidele?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Kas anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; selles seadmes juurdepääs fotodele ja videotele?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Kas anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; selles seadmes juurdepääs rohkematele fotodele ja videotele?"</string>
@@ -491,9 +493,9 @@
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"See rakendus soovib pildistada ja videoid salvestada mis tahes ajal (ka siis, kui te rakendust ei kasuta). "<annotation id="link">"Lubage see seadetes."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääseda juurde teie telefoni kõnelogidele?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; teha ja hallata telefonikõnesid?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs anduri andmetele teie eluliste näitajate kohta?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"See rakendus soovib pidevat juurdepääsu teie elulistele näitajatele (ka siis, kui te rakendust ei kasuta). Selle muudatuse tegemiseks "<annotation id="link">"avage seaded."</annotation></string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs anduri andmetele teie eluliste näitajate kohta?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs anduri andmetele teie tervisenäitajate kohta?"</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"See rakendus soovib pidevat juurdepääsu teie tervisenäitajatele (ka siis, kui te rakendust ei kasuta). Selle muudatuse tegemiseks "<annotation id="link">"avage seaded."</annotation></string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs anduri andmetele teie tervisenäitajate kohta?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Selleks et anda rakendusele pidev juurdepääs kehaanduri andmetele (ka siis, kui rakendust ei kasutata), "<annotation id="link">"avage seaded."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Kas lubada jätkuvalt rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääseda juurde kehaanduri andmetele, kui rakendust kasutatakse?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; teile märguandeid saata?"</string>
diff --git a/PermissionController/res/values-eu/strings.xml b/PermissionController/res/values-eu/strings.xml
index aaa711b18..c10bc77af 100644
--- a/PermissionController/res/values-eu/strings.xml
+++ b/PermissionController/res/values-eu/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Datu gehiago"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Eman guztirako baimena"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Eman guztirako baimena beti"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Eman sarbide mugatua"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Hautatu argazkiak eta bideoak"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Hautatu beste batzuk"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Ez hautatu gehiago"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikazioak"</string>
<string name="app_permissions" msgid="3369917736607944781">"Aplikazio-baimenak"</string>
<string name="unused_apps" msgid="2058057455175955094">"Erabiltzen ez diren aplikazioak"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Editatu aplikazio honetarako hautatutako argazkiak"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ez dago erabiltzen ez duzun aplikaziorik"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Erabiltzen ez diren 0 aplikazio"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Azkenaldian baimenen inguruan hartutako erabakiak"</string>
@@ -150,7 +152,7 @@
<string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Azken # minutuan}other{Azken # minutuetan}}"</string>
<string name="no_permission_usages" msgid="9119517454177289331">"Ez da eskatu baimenik"</string>
<string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Orain arteko azken sarbidea"</string>
- <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Azken zazpi egunetako azken sarbidea"</string>
+ <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Azken 7 egunetako azken sarbidea"</string>
<string name="permission_usage_list_title_last_day" msgid="8730907824567238461">"Azken 24 orduetan erabilitakoak"</string>
<string name="permission_usage_list_title_last_hour" msgid="6624161487623223716">"Azken ordubeteko azken sarbidea"</string>
<string name="permission_usage_list_title_last_15_minutes" msgid="8615062016024296833">"Azken 15 minutuetako azken sarbidea"</string>
@@ -172,7 +174,7 @@
<string name="filter_by_time" msgid="6667864816999691642">"Iragazi orduaren arabera"</string>
<string name="sort_spinner_most_permissions" msgid="1704349738096822836">"Baimen gehien erabili dituztenak"</string>
<string name="sort_spinner_most_accesses" msgid="5283913004357220161">"Erabilienak"</string>
- <string name="sort_spinner_recent" msgid="7513845273076525203">"Azkenak"</string>
+ <string name="sort_spinner_recent" msgid="7513845273076525203">"Azkenaldikoak"</string>
<string name="sort_by_app" msgid="4055799843051138087">"Ordenatu erabileraren arabera"</string>
<string name="sort_by_time" msgid="5435045320002150456">"Ordenatu orduaren arabera"</string>
<string name="item_separator" msgid="4030255389809224513">", "</string>
@@ -200,7 +202,7 @@
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Ikusi <xliff:g id="APP">%1$s</xliff:g> aplikazioaren baimen guztiak"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Ikusi baimen hau duten aplikazio guztiak"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Erakutsi laguntzaileak mikrofonoa erabiltzeko duen baimena"</string>
- <string name="unused_apps_category_title" msgid="2988455616845243901">"Erabili gabeko aplikazio-ezarpenak"</string>
+ <string name="unused_apps_category_title" msgid="2988455616845243901">"Erabili gabeko aplikazioen ezarpenak"</string>
<string name="auto_revoke_label" msgid="5068393642936571656">"Kendu baimenak aplikazioa erabiltzen ez bada"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Kendu baimenak eta egin tokia"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pausatu aplikazioko jarduerak, erabiltzen ez bada"</string>
@@ -352,7 +354,7 @@
<string name="role_assistant_description" msgid="6622458130459922952">"Ikusten ari zaren pantailako informazioaren araberako laguntza eskain diezazukete laguntza-aplikazioek. Zenbait aplikaziok abiarazlea eta ahots bidezko zerbitzuak onartzen dituzte laguntza integratua eskaintzeko."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Arakatzaile lehenetsia"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Arakatzaile-aplikazioa"</string>
- <string name="role_browser_description" msgid="3465253637499842671">"Interneterako sarbidea ematen dizuten eta sakatzen dituzun estekak bistaratzen dituzten aplikazioak"</string>
+ <string name="role_browser_description" msgid="3465253637499842671">"Interneteko sarbidea ematen dizuten eta sakatzen dituzun estekak bistaratzen dituzten aplikazioak"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"<xliff:g id="APP_NAME">%1$s</xliff:g> ezarri nahi duzu arakatzaile-aplikazio lehenetsi gisa?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"Ez du behar baimenik"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"Telefono lehenetsia"</string>
@@ -361,8 +363,8 @@
<string name="role_dialer_request_title" msgid="5959618560705912058">"<xliff:g id="APP_NAME">%1$s</xliff:g> ezarri nahi duzu telefono-aplikazio lehenetsi gisa?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"Zure kamera, kontaktuak, mikrofonoa, telefonoa eta SMSak erabiltzeko baimena emango zaio aplikazioari"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"markagailua"</string>
- <string name="role_sms_label" msgid="8456999857547686640">"SMSetarako aplik. lehenetsia"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"SMSetarako aplikazioa"</string>
+ <string name="role_sms_label" msgid="8456999857547686640">"SMSetarako aplikazio lehenetsia"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"SMSetarako aplikazio"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Telefono-zenbakiaren bidez testu-mezu laburrak, argazkiak, bideoak eta beste zenbait gauza bidaltzea eta jasotzea ahalbidetzen dizuten aplikazioak"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g> ezarri nahi duzu SMSetarako aplikazio lehenetsi gisa?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Zure kamera, kontaktuak, fitxategiak eta multimedia-edukia, mikrofonoa, telefonoa eta SMSak erabiltzeko baimena emango zaio aplikazioari"</string>
@@ -375,7 +377,7 @@
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"ice"</string>
<string name="role_home_label" msgid="3871847846649769412">"Hasierako aplikazio lehenetsia"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Hasierako aplikazioa"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Android-eko gailuko hasierako pantailak ordezten dituzten aplikazioak (\"abiarazle\" ere deitzen zaie). Gailuko eduki eta eginbideetarako sarbidea ematen dute."</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Android-eko gailuko orri nagusiak ordezten dituzten aplikazioak (\"abiarazle\" ere deitzen zaie). Gailuko eduki eta eginbideetarako sarbidea ematen dute."</string>
<string name="role_home_request_title" msgid="738136983453341081">"<xliff:g id="APP_NAME">%1$s</xliff:g> ezarri nahi duzu hasierako aplikazio lehenetsi gisa?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Ez du behar baimenik"</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"abiarazlea"</string>
@@ -443,7 +445,7 @@
<string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak arazketa-informazioa kargatu nahi du."</string>
<string name="incident_report_dialog_title" msgid="669104389325204095">"Arazketa-datuak partekatu nahi dituzu?"</string>
<string name="incident_report_dialog_intro" msgid="5897733669850951832">"Arazo bat hauteman du sistemak."</string>
- <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="DATE">%2$s</xliff:g> (<xliff:g id="TIME">%3$s</xliff:g>) datan sortutako akatsen txostena kargatzeko baimena eskatzen ari da <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Akatsen txostenek zure gailuari buruzkoa den edo aplikazioek erregistratu duten informazio pertsonala dute; adibidez, erabiltzaile-izenak, kokapenari buruzko datuak, gailuaren identifikatzaileak eta sareari buruzko informazioa. Informazio hori izateko fidagarriak iruditzen zaizkizun pertsona eta aplikazioekin soilik partekatu beharko zenituzke akatsen txostenak. <xliff:g id="APP_NAME_1">%4$s</xliff:g> aplikazioari akatsen txostena kargatzeko baimena eman nahi diozu?"</string>
+ <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="DATE">%2$s</xliff:g> (<xliff:g id="TIME">%3$s</xliff:g>) datan sortutako akatsen txostena kargatzeko baimena eskatzen ari da <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Akatsen txostenek zure gailuari buruzkoa den edo aplikazioek erregistratu duten informazio pertsonala dute; adibidez, erabiltzaile-izenak, kokapenari buruzko datuak, gailu-identifikatzaileak eta sareari buruzko informazioa. Informazio hori izateko fidagarriak iruditzen zaizkizun pertsona eta aplikazioekin soilik partekatu beharko zenituzke akatsen txostenak. <xliff:g id="APP_NAME_1">%4$s</xliff:g> aplikazioari akatsen txostena kargatzeko baimena eman nahi diozu?"</string>
<string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Errore bat gertatu da <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren akatsen txostena prozesatzean; beraz, ezin izan dira partekatu arazketa-datu xehatuak. Barkatu eragozpenak."</string>
<string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Eman baimena"</string>
<string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Ukatu"</string>
@@ -492,7 +494,7 @@
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Telefonoko deien erregistroa erabiltzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Telefono-deiak egiteko eta kudeatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Bizi-konstanteei buruzko sentsorearen datuak erabiltzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Aplikazioak bizi-konstanteei buruzko sentsoreen datuak atzitu nahi ditu, baita aplikazioa erabiltzen ari ez zarenean ere. Aldaketa hori egiteko, "<annotation id="link">"joan ezarpenetara."</annotation></string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Aplikazioak bizi-konstanteei buruzko sentsoreen datuak atzitzeko baimena behar du, baita aplikazioa erabiltzen ari ez zarenean ere. Aldaketa hori egiteko, "<annotation id="link">"joan ezarpenetara."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Bizi-konstanteei buruzko sentsoreen datuak erabiltzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Aplikazioari gorputz-sentsoreen datuak beti erabiltzeko baimena emateko (baita aplikazioa erabiltzen ari ez zarenean ere), "<annotation id="link">"joan ezarpenetara."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erabili bitartean gorputz-sentsoreen datuak erabiltzeko baimena eman nahi diozu aplikazio horri?"</string>
@@ -575,14 +577,14 @@
<string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Berrikusi kokapena atzeko planoan erabiltzeko baimena duen aplikazioa"</string>
<string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"Aplikazioak beti erabil dezake kokapena, nahiz eta itxita egon.\n\nSegurtasun- eta larrialdi-aplikazio batzuek atzeko planoan kokapena erabiltzeko baimena behar dute behar bezala funtzionatzeko."</string>
<string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"Aldatu da sarbidea"</string>
- <string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Ikusi kokapenaren azken erabilera"</string>
+ <string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Ikusi kokapenaren azkenaldiko erabilera"</string>
<string name="privacy_controls_title" msgid="7605929972256835199">"Pribatutasun-ezarpenak"</string>
<string name="camera_toggle_title" msgid="1251201397431837666">"Kamera erabiltzeko baimena"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"Mikrofonoa erabiltzeko baimena"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Aplikazio eta zerbitzuetarako"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Aplikazio eta zerbitzuetarako. Ezarpena desaktibatuta badago, baliteke mikrofonoaren bidez lortutako datuak larrialdietarako zenbaki batera deitzen duzunean partekatzea."</string>
<string name="location_settings_subtitle" msgid="2328360561197430695">"Ikusi kokapena atzi dezaketen aplikazioak eta zerbitzuak"</string>
- <string name="show_clip_access_notification_title" msgid="5168467637351109096">"Erakutsi arbela atzitzen denean"</string>
+ <string name="show_clip_access_notification_title" msgid="5168467637351109096">"Erakutsi arbela erabiltzen denean"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Erakutsi mezu bat aplikazio batek kopiatu dituzun testuak, irudiak edo edukiak atzitzen dituenean"</string>
<string name="show_password_title" msgid="2877269286984684659">"Erakutsi pasahitzak"</string>
<string name="show_password_summary" msgid="1110166488865981610">"Idatzi ahala, erakutsi karaktereak laburki"</string>
diff --git a/PermissionController/res/values-fa/strings.xml b/PermissionController/res/values-fa/strings.xml
index a5c05ca32..d9ec5dd69 100644
--- a/PermissionController/res/values-fa/strings.xml
+++ b/PermissionController/res/values-fa/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="6098036489833144040">"کنترل‌کننده اجازه"</string>
<string name="ok" msgid="1936281769725676272">"تأیید"</string>
- <string name="permission_search_keyword" msgid="1214451577494730543">"مجوزها"</string>
+ <string name="permission_search_keyword" msgid="1214451577494730543">"اجازه‌ها"</string>
<string name="cancel" msgid="8943320028373963831">"لغو"</string>
<string name="back" msgid="6249950659061523680">"برگشت"</string>
<string name="available" msgid="6007778121920339498">"دردسترس"</string>
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"اطلاعات بیشتر"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"مجاز بودن همه"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"همه موارد همیشه مجازاند"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"مجاز کردن دسترسی محدود"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"انتخاب عکس و ویدیو"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"انتخاب موارد بیشتر"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"دیگر چیزی انتخاب نشود"</string>
@@ -49,17 +50,18 @@
<string name="permission_revoked_all" msgid="3397649017727222283">"همه مجوزها غیرفعال است"</string>
<string name="permission_revoked_none" msgid="9213345075484381180">"هیچ‌ موردی غیرفعال نیست"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"اجازه دادن"</string>
- <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"همیشه مجاز است"</string>
+ <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"همیشه مجاز"</string>
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"هنگام استفاده از برنامه"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"تغییر به مکان دقیق"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"حفظ مکان تقریبی"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"فقط این بار"</string>
- <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"همیشه مجاز است"</string>
+ <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"همیشه مجاز"</string>
<string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"اجازه دادن برای مدیریت همه فایل‌ها"</string>
<string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"اجازه دادن برای دسترسی به فایل‌های رسانه"</string>
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"برنامه‌ها"</string>
<string name="app_permissions" msgid="3369917736607944781">"اجازه‌های برنامه"</string>
<string name="unused_apps" msgid="2058057455175955094">"برنامه‌های استفاده‌نشده"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"ویرایش عکس‌های انتخاب‌شده برای این برنامه"</string>
<string name="no_unused_apps" msgid="12809387670415295">"برنامه استفاده‌نشده‌ای موجود نیست"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"برنامه استفاده‌نشده‌ای وجود ندارد"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"تصمیم‌های اخیر درباره اجازه‌ها"</string>
@@ -107,11 +109,11 @@
<!-- no translation found for background_access_chooser_dialog_choices:0 (1351721623256561996) -->
<!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) -->
<!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) -->
- <string name="permission_access_always" msgid="1474641821883823446">"همیشه مجاز است"</string>
- <string name="permission_access_only_foreground" msgid="7801170728159326195">"فقط هنگام استفاده از برنامه مجاز است"</string>
+ <string name="permission_access_always" msgid="1474641821883823446">"همیشه مجاز"</string>
+ <string name="permission_access_only_foreground" msgid="7801170728159326195">"مجاز فقط هنگام استفاده از برنامه"</string>
<string name="permission_access_never" msgid="4647014230217936900">"اجازه ندادن"</string>
<string name="loading" msgid="4789365003890741082">"درحال بارگیری…"</string>
- <string name="all_permissions" msgid="6911125611996872522">"همه مجوزها"</string>
+ <string name="all_permissions" msgid="6911125611996872522">"همه اجازه‌ها"</string>
<string name="other_permissions" msgid="2901186127193849594">"سایر قابلیت‌های برنامه"</string>
<string name="permission_request_title" msgid="8790310151025020126">"درخواست اجازه"</string>
<string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
@@ -143,7 +145,7 @@
<string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ساعت}one{# ساعت}other{# ساعت}}"</string>
<string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# دقیقه}one{# دقیقه}other{# دقیقه}}"</string>
<string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# ثانیه}one{# ثانیه}other{# ثانیه}}"</string>
- <string name="permission_usage_any_permission" msgid="6358023078298106997">"همه مجوزها"</string>
+ <string name="permission_usage_any_permission" msgid="6358023078298106997">"همه اجازه‌ها"</string>
<string name="permission_usage_any_time" msgid="3802087027301631827">"هر زمانی"</string>
<string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{# روز گذشته}one{# روز گذشته}other{# روز گذشته}}"</string>
<string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# ساعت گذشته}one{# ساعت گذشته}other{# ساعت گذشته}}"</string>
@@ -158,9 +160,9 @@
<string name="permission_usage_bar_chart_title_any_time" msgid="2845251288192246754">"پراستفاده‌ترین مجوز در هرزمانی"</string>
<string name="permission_usage_bar_chart_title_last_7_days" msgid="5796577162176938349">"پراستفاده‌ترین مجوز در ۷ روز گذشته"</string>
<string name="permission_usage_bar_chart_title_last_day" msgid="7950805735777472871">"پراستفاده‌ترین مجوز در ۲۴ ساعت گذشته"</string>
- <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"پراستفاده‌ترین مجوزها در ۱ ساعت گذشته"</string>
+ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"پراستفاده‌ترین اجازه‌ها در ۱ ساعت گذشته"</string>
<string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"پراستفاده‌ترین مجوزها در ۱۵ دقیقه گذشته"</string>
- <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"پراستفاده‌ترین مجوزها در ۱ دقیقه گذشته"</string>
+ <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"پراستفاده‌ترین اجازه‌ها در ۱ دقیقه گذشته"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{در # روز گذشته استفاده نشده است}one{در # روز گذشته استفاده نشده است}other{در # روز گذشته استفاده نشده است}}"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{در # ساعت گذشته استفاده نشده است}one{در # ساعت گذشته استفاده نشده است}other{در # ساعت گذشته استفاده نشده است}}"</string>
<string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{یک برنامه از آن استفاده کرده است}one{# برنامه از آن استفاده کرده است}other{# برنامه از آن استفاده کرده است}}"</string>
@@ -168,9 +170,9 @@
<string name="app_permission_usage_filter_label" msgid="7182861154638631550">"فیلترشده براساس: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"حذف فیلتر"</string>
<string name="filter_by_title" msgid="7300368602759958031">"فیلتر کردن براساس"</string>
- <string name="filter_by_permissions" msgid="7613462963111282568">"فیلتر کردن براساس مجوزها"</string>
+ <string name="filter_by_permissions" msgid="7613462963111282568">"فیلتر کردن براساس اجازه‌ها"</string>
<string name="filter_by_time" msgid="6667864816999691642">"فیلتر براساس زمان"</string>
- <string name="sort_spinner_most_permissions" msgid="1704349738096822836">"بیشترین مجوزها"</string>
+ <string name="sort_spinner_most_permissions" msgid="1704349738096822836">"بیشترین اجازه‌ها"</string>
<string name="sort_spinner_most_accesses" msgid="5283913004357220161">"بیشترین تعداد دسترسی"</string>
<string name="sort_spinner_recent" msgid="7513845273076525203">"اخیر"</string>
<string name="sort_by_app" msgid="4055799843051138087">"به‌ترتیب استفاده برنامه"</string>
@@ -186,8 +188,8 @@
<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_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>
+ <string name="app_permission_button_allow_always" msgid="4573292371734011171">"همیشه مجاز"</string>
+ <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"مجاز فقط هنگام استفاده از برنامه"</string>
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"همه موارد همیشه مجازاند"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"هربار پرسیده شود"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"اجازه ندادن"</string>
@@ -260,11 +262,11 @@
<string name="permission_reminders" msgid="6528257957664832636">"یادآوری‌های مجوز"</string>
<string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"۱ برنامه استفاده‌نشده"</string>
<string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> برنامه استفاده‌نشده"</string>
- <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"برای محافظت از حریم خصوصی شما، اجازه‌ها برداشته شد. برای مرور، ضربه بزنید"</string>
+ <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"برای محافظت از حریم خصوصی شما، اجازه‌ها برداشته شد. برای مرور، تک‌ضرب بزنید"</string>
<string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"اجازه‌های مربوط به برنامه‌های استفاده‌نشده برداشته شد"</string>
- <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"برخی از برنامه‌ها به‌مدت چند ماه استفاده نشده‌اند. برای مرور، ضربه بزنید."</string>
+ <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"برخی از برنامه‌ها به‌مدت چند ماه استفاده نشده‌اند. برای مرور، تک‌ضرب بزنید."</string>
<string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# برنامه استفاده‌نشده}one{# برنامه استفاده‌نشده}other{# برنامه استفاده‌نشده}}"</string>
- <string name="unused_apps_notification_content" msgid="9195026773244581246">"اجازه‌ها و فایل‌های موقت حذف شده‌اند و اعلان‌ها متوقف شده‌اند. برای مرور، ضربه بزنید."</string>
+ <string name="unused_apps_notification_content" msgid="9195026773244581246">"اجازه‌ها و فایل‌های موقت حذف شده‌اند و اعلان‌ها متوقف شده‌اند. برای مرور، تک‌ضرب بزنید."</string>
<string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"مرور برنامه‌هایی که اجازه‌های آن‌ها برداشته شده است"</string>
<string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"اجازه‌ها و فایل‌های موقت مربوط به برنامه‌هایی که مدتی از آن‌ها استفاده نکرده‌اید برداشته شدند و اعلان‌ها متوقف شدند."</string>
<string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"مرور برنامه‌ها"</string>
@@ -281,7 +283,7 @@
<string name="months_ago" msgid="1766026492610646354">"<xliff:g id="COUNT">%1$d</xliff:g> ماه قبل"</string>
<string name="auto_revoke_preference_summary" msgid="5517958331781391481">"برای محافظت از حریم خصوصی شما، اجازه‌ها برداشته شد"</string>
<string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"<xliff:g id="APP_NAME">%s</xliff:g> به موقعیت مکانی شما در پس‌زمینه دسترسی دارد"</string>
- <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"این برنامه همیشه می‌تواند به مکانتان دسترسی داشته باشد. برای تغییر دادن، ضربه بزنید."</string>
+ <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"این برنامه همیشه می‌تواند به مکانتان دسترسی داشته باشد. برای تغییر دادن، تک‌ضرب بزنید."</string>
<string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"مرور برنامهٔ دارای دسترسی به اعلان‌ها"</string>
<string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> می‌تواند اعلان‌ها را رد کند، روی آن‌ها کنش انجام دهد، و به محتوای داخل آن‌ها دسترسی داشته باشد"</string>
<string name="notification_listener_warning_card_content" msgid="7840973324284115893">"این برنامه می‌تواند اعلان‌ها را رد کند، روی آن‌ها کنش انجام دهد، و به محتوای داخل آن‌ها دسترسی داشته باشد. برخی برنامه‌ها به این نوع دسترسی نیاز دارند تا طبق برنامه کار کنند."</string>
@@ -296,12 +298,12 @@
<string name="accessibility_remove_access_success_label" msgid="4380995302917014670">"دسترسی برداشته شد"</string>
<string name="safety_center_notification_app_label" msgid="2457720616141926534">"‏سیستم Android"</string>
<string name="auto_revoke_after_notification_title" msgid="5417761027669887431">"برای محافظت از حریم خصوصی، اجازه‌های برنامه برداشته شدند"</string>
- <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"<xliff:g id="APP_NAME">%s</xliff:g> طی چند ماه استفاده نشده است. برای مرور، ضربه بزنید."</string>
- <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> و ۱ برنامه دیگر طی چند ماه استفاده نشده‌اند. برای مرور، ضربه بزنید."</string>
- <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> و <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> برنامه دیگر طی چند ماه استفاده نشده‌اند. برای مرور، ضربه بزنید."</string>
+ <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"<xliff:g id="APP_NAME">%s</xliff:g> طی چند ماه استفاده نشده است. برای مرور، تک‌ضرب بزنید."</string>
+ <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> و ۱ برنامه دیگر طی چند ماه استفاده نشده‌اند. برای مرور، تک‌ضرب بزنید."</string>
+ <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> و <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> برنامه دیگر طی چند ماه استفاده نشده‌اند. برای مرور، تک‌ضرب بزنید."</string>
<string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"۱ برنامه استفاده نشده است"</string>
<string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> برنامه استفاده نشده‌اند"</string>
- <string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"برای محافظت از حریم خصوصی، اجازه‌ها برداشته خواهند شد. برای مرور، ضربه بزنید."</string>
+ <string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"برای محافظت از حریم خصوصی، اجازه‌ها برداشته خواهند شد. برای مرور، تک‌ضرب بزنید."</string>
<string name="unused_apps_title" msgid="8589298917717872239">"برنامه‌های استفاده‌نشده"</string>
<string name="unused_apps_subtitle_after" msgid="2034267519506357898">"اجازه‌ها از این برنامه‌ها برداشته شد"</string>
<string name="unused_apps_subtitle_before" msgid="5233302577076132427">"اجازه‌ها از این برنامه‌ها برداشته خواهند شد"</string>
@@ -407,9 +409,9 @@
<string name="phone_call_uses_microphone" msgid="233569591461187177">"‏در &lt;b&gt;تماس تلفنی&lt;/b&gt; از میکروفون استفاده می‌شود"</string>
<string name="phone_call_uses_microphone_and_camera" msgid="6291898755681748189">"‏در &lt;b&gt;تماس تصویری&lt;/b&gt; از دوربین و میکروفون استفاده می‌شود"</string>
<string name="phone_call_uses_camera" msgid="2048417022147857418">"‏در &lt;b&gt;تماس تصویری&lt;/b&gt; از دوربین استفاده می‌شود"</string>
- <string name="system_uses_microphone" msgid="576672130318877143">"سرویس سیستم به میکروفون دسترسی دارد"</string>
- <string name="system_uses_microphone_and_camera" msgid="5124478304275138804">"سرویس سیستم به دوربین و میکروفون دسترسی دارد"</string>
- <string name="system_uses_camera" msgid="1911223105234441470">"سرویس سیستم به دوربین دسترسی دارد"</string>
+ <string name="system_uses_microphone" msgid="576672130318877143">"خدمات سیستم به میکروفون دسترسی دارد"</string>
+ <string name="system_uses_microphone_and_camera" msgid="5124478304275138804">"خدمات سیستم به دوربین و میکروفون دسترسی دارد"</string>
+ <string name="system_uses_camera" msgid="1911223105234441470">"خدمات سیستم به دوربین دسترسی دارد"</string>
<string name="other_use" msgid="6564855051022776692">"استفاده‌های دیگر:"</string>
<string name="ongoing_usage_dialog_ok" msgid="103556809118460072">"متوجه شدم"</string>
<string name="ongoing_usage_dialog_title" msgid="683836493556628569">"استفاده اخیر از <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
@@ -450,53 +452,53 @@
<string name="adjust_user_sensitive_title" msgid="4196724451314280527">"تنظیمات پیشرفته"</string>
<string name="menu_adjust_user_sensitive" msgid="6497923610654425780">"تنظیمات پیشرفته"</string>
<string name="adjust_user_sensitive_globally_title" msgid="8649190949066029174">"نمایش استفادهٔ برنامه سیستم"</string>
- <string name="adjust_user_sensitive_globally_summary" msgid="129467818433773912">"نمایش استفاده برنامه سیستم از مجوزها در نوار وضعیت، داشبورد، و جاهای دیگر"</string>
+ <string name="adjust_user_sensitive_globally_summary" msgid="129467818433773912">"نمایش استفاده برنامه سیستم از اجازه‌ها در نوار وضعیت، داشبورد، و جاهای دیگر"</string>
<string name="adjust_user_sensitive_per_app_header" msgid="4543506440989005648">"برجسته کردن استفاده برای موارد زیر"</string>
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"نمایش دستیار تشخیص راه‌انداز"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"نمایش نماد مربوطه در نوار وضعیت وقتی از میکروفون برای فعال کردن دستیار صوتی استفاده می‌شود"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; اجازه داده شود به عکس‌ها و رسانه‌های موجود در دستگاهتان دسترسی پیدا کند؟"</string>
- <string name="permgrouprequest_contacts" msgid="8391550064551053695">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود به مخاطبین شما دسترسی پیدا کند؟"</string>
- <string name="permgrouprequest_location" msgid="6990232580121067883">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود به مکان این دستگاه دسترسی پیدا کند؟"</string>
+ <string name="permgrouprequest_contacts" msgid="8391550064551053695">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به مخاطبینتان می‌دهید؟"</string>
+ <string name="permgrouprequest_location" msgid="6990232580121067883">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به مکان این دستگاه می‌دهید؟"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"این برنامه فقط وقتی از آن استفاده می‌کنید، به مکان دسترسی خواهد داشت"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود به مکان این دستگاه دسترسی پیدا کند؟"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"این برنامه ممکن است بخواهد مرتب به مکانتان دسترسی داشته باشد، حتی زمانی‌که از برنامه استفاده نمی‌کنید. "<annotation id="link">"در تنظیمات به آن اجازه دهید."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"‏دسترسی به مکان برای &lt;b&amp;gt؛<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;gt تغییر کند؟"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"این برنامه می‌خواهد مرتب به مکانتان دسترسی داشته باشد، حتی زمانی‌که از برنامه استفاده نمی‌کنید. "<annotation id="link">"در تنظیمات به آن اجازه دهید."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود دستگاه‌های اطراف را پیدا کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را مشخص کند؟"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید دستگاه‌های اطراف را پیدا کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را مشخص کند؟"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود دستگاه‌های اطراف را پیدا کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را مشخص کند؟ "<annotation id="link">"در تنظیمات اجازه دهید."</annotation></string>
- <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"دسترسی <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> به مکان از حالت تقریبی به دقیق تغییر کند؟"</string>
- <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"‏به &lt;b&amp;gt؛<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;gt؛ اجازه داده شود به مکان تقریبی این دستگاه دسترسی پیدا کند؟"</string>
+ <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"دسترسی مکان <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> را از حالت تقریبی به دقیق تغییر می‌دهید؟"</string>
+ <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به مکان تقریبی این دستگاه را می‌دهید؟"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"دقیق"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"تقریبی"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به تقویمتان داده شود؟"</string>
- <string name="permgrouprequest_sms" msgid="5672063688745420991">"‏به «&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt;» اجازه داده شود پیامک ارسال و مشاهده کند؟"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به تقویمتان می‌دهید؟"</string>
+ <string name="permgrouprequest_sms" msgid="5672063688745420991">"‏به «&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt;» اجازه ارسال و مشاهده پیامک را می‌دهید؟"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"‏به برنامه &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; اجازه داده شود به عکس‌ها، رسانه، و فایل‌های موجود در دستگاهتان دسترسی داشته باشد؟"</string>
- <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به &lt;b&gt;عکس‌ها، ویدیوها، موسیقی، و فایل‌های صوتی&lt;/b&gt; این دستگاه دسترسی داشته باشد؟"</string>
- <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به &lt;b&gt;عکس‌ها، ویدیوها، موسیقی، صوت، و فایل‌های دیگر&lt;/b&gt; این دستگاه دسترسی داشته باشد؟"</string>
- <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به فایل‌های موسیقی و صوتی در این دستگاه دسترسی داشته باشد؟"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به عکس‌ها و ویدیوهای این دستگاه دسترسی داشته باشد؟"</string>
- <string name="permgrouprequest_more_photos" msgid="128933814654231321">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به عکس‌ها و ویدیوهای بیشتری در این دستگاه دسترسی داشته باشد؟"</string>
- <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏به &lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود صدا ضبط کند؟"</string>
+ <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به &lt;b&gt;عکس‌ها، ویدیوها، موسیقی، و فایل‌های صوتی&lt;/b&gt; این دستگاه را می‌دهید؟"</string>
+ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به &lt;b&gt;عکس‌ها، ویدیوها، موسیقی، صوت، و فایل‌های دیگر&lt;/b&gt; این دستگاه را می‌دهید؟"</string>
+ <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به فایل‌های موسیقی و صوتی در این دستگاه را می‌دهید؟"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به عکس‌ها و ویدیوهای این دستگاه را می‌دهید؟"</string>
+ <string name="permgrouprequest_more_photos" msgid="128933814654231321">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به عکس‌ها و ویدیوهای بیشتری در این دستگاه را می‌دهید؟"</string>
+ <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏به &lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه ضبط صدا می‌دهید؟"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"این برنامه فقط وقتی از آن استفاده می‌کنید، می‌تواند صدا ضبط کند"</string>
- <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏به &lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود صدا ضبط کند؟"</string>
+ <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏به &lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه ضبط صدا می‌دهید؟"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"این برنامه ممکن است بخواهد مرتب صدا ضبط کند، حتی زمانی‌که از برنامه استفاده نمی‌کنید. "<annotation id="link">"در تنظیمات به آن اجازه دهید."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"‏دسترسی به میکروفون برای &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; تغییر کند؟"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"این برنامه می‌خواهد مرتب صدا ضبط کند، حتی زمانی‌که از برنامه استفاده نمی‌کنید. "<annotation id="link">"در تنظیمات به آن اجازه دهید."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به فعالیت فیزیکی‌تان دسترسی پیدا کند؟"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود عکس بگیرد و ویدیو ضبط کند؟"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید عکس بگیرد و ویدیو ضبط کند؟"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"این برنامه فقط وقتی از آن استفاده می‌کنید، می‌تواند عکس و فیلم بگیرد"</string>
- <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود عکس بگیرد و ویدیو ضبط کند؟"</string>
+ <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید عکس بگیرد و ویدیو ضبط کند؟"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"این برنامه ممکن است بخواهد مرتب عکس و فیلم بگیرد، حتی زمانی‌که از برنامه استفاده نمی‌کنید. "<annotation id="link">"در تنظیمات به آن اجازه دهید."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"‏دسترسی به دوربین برای &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; تغییر کند؟"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"این برنامه می‌خواهد مرتب عکس و فیلم بگیرد، حتی زمانی‌که از برنامه استفاده نمی‌کنید. "<annotation id="link">"در تنظیمات به آن اجازه دهید."</annotation></string>
- <string name="permgrouprequest_calllog" msgid="2065327180175371397">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود به گزارش تماس‌های تلفنی شما دسترسی داشته باشد؟"</string>
- <string name="permgrouprequest_phone" msgid="1829234136997316752">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; اجازه داده شود تماس‌های تلفنی برقرار کند و آن‌ها را مدیریت کند؟"</string>
+ <string name="permgrouprequest_calllog" msgid="2065327180175371397">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به گزارش تماس‌های تلفنی‌تان می‌دهید؟"</string>
+ <string name="permgrouprequest_phone" msgid="1829234136997316752">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; اجازه برقراری تماس‌های تلفنی و مدیریت آن‌ها را می‌دهید"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; اجازه داده شود به داده‌های حسگر مربوط به علائم حیاتی شما دسترسی پیدا کند؟"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"این برنامه می‌خواهد به داده‌های حسگر از علائم حیاتی شما همیشه دسترسی داشته باشد، حتی زمانی که از برنامه استفاده نمی‌کنید. برای انجام این تغییر، "<annotation id="link">"به تنظیمات بروید."</annotation></string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به داده‌های علائم حیاتی‌تان از حسگر دسترسی داشته باشد؟"</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"این برنامه می‌خواهد به داده‌های حسگر از علائم حیاتی شما همیشه دسترسی داشته باشد، حتی زمانی‌که از برنامه استفاده نمی‌کنید. برای انجام این تغییر، "<annotation id="link">"به تنظیمات بروید."</annotation></string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دسترسی به داده‌های علائم حیاتی‌تان از حسگر را می‌دهید؟"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"برای اینکه این برنامه بتواند همیشه و حتی زمانی که از برنامه استفاده نمی‌کنید به داده‌های حسگر بدن دسترسی داشته باشد، "<annotation id="link">"به تنظیمات بروید."</annotation></string>
- <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; همچنان اجازه داده شود درحین استفاده، به داده‌های حسگر بدن دسترسی داشته باشد؟"</string>
- <string name="permgrouprequest_notifications" msgid="6396739062335106181">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود اعلان ارسال کند؟"</string>
+ <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; همچنان اجازه دسترسی به داده‌های حسگر بدن هنگام استفاده از برنامه را می‌دهید؟"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه ارسال اعلان می‌دهید؟"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"مجوزهای کنترل‌شده"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> به مکان دسترسی دارد"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"سازمان شما به <xliff:g id="APP_NAME">%1$s</xliff:g> اجازه داده است به مکانتان دسترسی داشته باشد"</string>
@@ -524,12 +526,12 @@
<string name="safety_center_rescan_button" msgid="4517514567809409596">"اسکن کردن دستگاه"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"رد کردن"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"این هشدار رد شود؟"</string>
- <string name="safety_center_issue_card_dismiss_confirmation_message" msgid="3775418736671093563">"هرزمان خواستید تنظیمات امنیت و حریم خصوصی را مرور کنید تا محافظت بیشتری اضافه کنید"</string>
+ <string name="safety_center_issue_card_dismiss_confirmation_message" msgid="3775418736671093563">"هرزمان خواستید تنظیمات ایمنی و حریم خصوصی را مرور کنید تا محافظت بیشتری اضافه کنید"</string>
<string name="safety_center_issue_card_confirm_dismiss_button" msgid="5884137843083634556">"رد شدن"</string>
<string name="safety_center_issue_card_cancel_dismiss_button" msgid="2874578798877712346">"لغو"</string>
<string name="safety_center_entries_category_title" msgid="34356964062813115">"تنظیمات"</string>
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"وضعیت حریم خصوصی و امنیت. <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
- <string name="security_settings" msgid="3808106921175271317">"تنظیمات امنیتی"</string>
+ <string name="security_settings" msgid="3808106921175271317">"تنظیمات ایمنی"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"اجازه‌ها"</string>
<string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"امنیت و حریم خصوصی"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"بررسی وضعیت"</string>
diff --git a/PermissionController/res/values-fi/strings.xml b/PermissionController/res/values-fi/strings.xml
index 519ce8bc3..34bb7a374 100644
--- a/PermissionController/res/values-fi/strings.xml
+++ b/PermissionController/res/values-fi/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Lisätietoja"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Salli kaikki"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Salli aina kaikki"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Salli rajoitettu pääsy"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Valitse kuvia ja videoita"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Valitse lisää"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Älä valitse enempää"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Sovell."</string>
<string name="app_permissions" msgid="3369917736607944781">"Sovellusluvat"</string>
<string name="unused_apps" msgid="2058057455175955094">"Käyttämättömät sovellukset"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Muuta sovelluksen kuvavalintaa"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ei käyttämättömiä sovelluksia"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 käyttämätöntä sovellusta"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Viimeaikaiset lupapäätökset"</string>
@@ -71,7 +73,7 @@
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Tänään}=1{1 päivä sitten}other{# päivää sitten}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Poista sovellus käytöstä"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Jos poistat sovelluksen käytöstä, Android ja muut sovellukset eivät välttämättä enää toimi oikein. Muista, ettet voi poistaa sovellusta, sillä se tuli laitteesi mukana. Poistamalla sovelluksen käytöstä suljet sen ja piilotat sen laitteella."</string>
- <string name="app_permission_manager" msgid="3903811137630909550">"Lupien ylläpito"</string>
+ <string name="app_permission_manager" msgid="3903811137630909550">"Lupienhallinta"</string>
<string name="never_ask_again" msgid="4728762438198560329">"Älä kysy enää"</string>
<string name="no_permissions" msgid="3881676756371148563">"Ei lupia"</string>
<string name="additional_permissions" msgid="5801285469338873430">"Lisäluvat"</string>
@@ -194,9 +196,9 @@
<string name="precise_image_description" msgid="6349638632303619872">"Tarkka sijainti"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Likimääräinen sijainti"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Käytä tarkkaa sijaintia"</string>
- <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kun tarkka sijainti ei ole päällä, sovellukset voivat nähdä likimääräisen sijaintisi"</string>
- <string name="app_permission_title" msgid="2090897901051370711">"Lupa (<xliff:g id="PERM">%1$s</xliff:g>)"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"Sovellus pyytää pääsyä näihin: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kun tarkka sijainti ei ole päällä, sovellukset voivat käyttää likimääräistä sijaintiasi"</string>
+ <string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g>lupa"</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"Sovellus pyytää <xliff:g id="PERM">%1$s</xliff:g>-lupaa"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Näytä kaikki luvat, jotka <xliff:g id="APP">%1$s</xliff:g> on saanut"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Näytä kaikki sovellukset, joilla on tämä lupa"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Näytä Assistantin mikrofonin käyttö"</string>
@@ -399,7 +401,7 @@
<string name="role_companion_device_computer_description" msgid="416099879217066377">"Tämä palvelu jakaa kuvat, median ja ilmoitukset puhelimeltasi muille laitteille."</string>
<string name="role_notes_label" msgid="7451627001058089536">"Oletusmuistiinpanosovellus"</string>
<string name="role_notes_short_label" msgid="8796604147546125285">"Muistiinpanosovellus"</string>
- <string name="role_notes_description" msgid="8496852798616883551">"Sovellukset, joilla voit ottaa muistiinpanoja laitteellasi"</string>
+ <string name="role_notes_description" msgid="8496852798616883551">"Sovellukset, joilla voit tehdä muistiinpanoja laitteellasi"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"muistiinpanot"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Nykyinen oletus"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Älä kysy uudelleen"</string>
diff --git a/PermissionController/res/values-fr-rCA-car/strings.xml b/PermissionController/res/values-fr-rCA-car/strings.xml
index 02b0a45be..5be1f89db 100644
--- a/PermissionController/res/values-fr-rCA-car/strings.xml
+++ b/PermissionController/res/values-fr-rCA-car/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="unused_apps_page_summary" msgid="7505839764289846511">"Si une application n\'est pas utilisée pendant quelques mois :\n\n• Les autorisations sont retirées pour protéger vos données.\n• Les fichiers temporaires sont retirés pour libérer de l\'espace de stockage."</string>
+ <string name="unused_apps_page_summary" msgid="7505839764289846511">"Si une appli n\'est pas utilisée pendant quelques mois :\n\n• Les autorisations sont retirées pour protéger vos données.\n• Les fichiers temporaires sont retirés pour libérer de l\'espace de stockage."</string>
</resources>
diff --git a/PermissionController/res/values-fr-rCA-television/strings.xml b/PermissionController/res/values-fr-rCA-television/strings.xml
index 84836a119..f4ec46457 100644
--- a/PermissionController/res/values-fr-rCA-television/strings.xml
+++ b/PermissionController/res/values-fr-rCA-television/strings.xml
@@ -17,11 +17,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="grant_dialog_button_deny_dont_ask_again" msgid="747769682501286250">"Refuser et ne plus demander"</string>
- <string name="grant_dialog_how_to_change" msgid="997462845048160559">"Vous pourrez modifier ce choix plus tard dans le menu Paramètres &gt; Applications"</string>
+ <string name="grant_dialog_how_to_change" msgid="997462845048160559">"Vous pourrez modifier ce choix plus tard dans le menu Paramètres &gt; Applis"</string>
<string name="current_permission_template" msgid="6240787325714651204">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
- <string name="preference_show_system_apps" msgid="4262140518693221093">"Afficher les applications système"</string>
- <string name="app_permissions_decor_title" msgid="7438716722786036814">"Autorisations des applications"</string>
- <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Autorisations des applications"</string>
+ <string name="preference_show_system_apps" msgid="4262140518693221093">"Afficher les applis système"</string>
+ <string name="app_permissions_decor_title" msgid="7438716722786036814">"Autorisations des applis"</string>
+ <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Autorisations des applis"</string>
<string name="permission_apps_decor_title" msgid="2811550489429789828">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
<string name="additional_permissions_decor_title" msgid="5113847982502484225">"Autorisations supplémentaires"</string>
<string name="system_apps_decor_title" msgid="4402004958937474803">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-fr-rCA-v33/strings.xml b/PermissionController/res/values-fr-rCA-v33/strings.xml
index 808e1bcd9..a2f81bcea 100644
--- a/PermissionController/res/values-fr-rCA-v33/strings.xml
+++ b/PermissionController/res/values-fr-rCA-v33/strings.xml
@@ -16,9 +16,9 @@
<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">"Cette application sera autorisée à vous envoyer des notifications; elle aura accès à votre appareil photo, à vos contacts, à votre microphone, à votre téléphone et à vos messages texte"</string>
- <string name="role_sms_request_description" msgid="1506966389698625395">"Cette application sera autorisée à vous envoyer des notifications; elle aura accès à votre appareil photo, à vos contacts, à vos fichiers, à votre microphone, à votre téléphone et à vos messages texte"</string>
- <string name="permission_description_summary_storage" msgid="1917071243213043858">"Les applications possédant cette autorisation peuvent accéder à tous les fichiers sur cet appareil"</string>
+ <string name="role_dialer_request_description" msgid="6188305064871543419">"Cette appli sera autorisée à vous envoyer des notifications; elle aura accès à votre appareil photo, à vos contacts, à votre microphone, à votre téléphone et à vos messages texte"</string>
+ <string name="role_sms_request_description" msgid="1506966389698625395">"Cette appli sera autorisée à vous envoyer des notifications; elle aura accès à votre appareil photo, à vos contacts, à vos fichiers, à votre microphone, à votre téléphone et à vos messages texte"</string>
+ <string name="permission_description_summary_storage" msgid="1917071243213043858">"Les applis possédant cette autorisation peuvent accéder à tous les fichiers sur cet appareil"</string>
<string name="work_policy_title" msgid="832967780713677409">"Infos sur votre politique de travail"</string>
<string name="work_policy_summary" msgid="3886113358084963931">"Les paramètres sont gérés par votre administrateur informatique"</string>
<string name="safety_center_entry_group_expand_action" msgid="5358289574941779652">"Développer et afficher la liste"</string>
diff --git a/PermissionController/res/values-fr-rCA-v34/strings.xml b/PermissionController/res/values-fr-rCA-v34/strings.xml
index 347b04cc8..6027cd3fc 100644
--- a/PermissionController/res/values-fr-rCA-v34/strings.xml
+++ b/PermissionController/res/values-fr-rCA-v34/strings.xml
@@ -20,8 +20,8 @@
<string name="security_privacy_brand_name" msgid="7303621734258440812">"Sécurité et confidentialité"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Commandes"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Connexion santé"</string>
- <string name="health_connect_summary" msgid="815473513776882296">"Gérez l\'accès des applications aux données relatives à la santé"</string>
+ <string name="health_connect_summary" msgid="815473513776882296">"Gérez l\'accès des applis aux données relatives à la santé"</string>
<string name="location_settings" msgid="8863940440881290182">"Accès à la position"</string>
- <string name="mic_toggle_description" msgid="1504101620086616040">"Pour les applications et les services. Si ce paramètre est désactivé, il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Pour les applications et les services"</string>
+ <string name="mic_toggle_description" msgid="1504101620086616040">"Pour les applis et les services. Si ce paramètre est désactivé, il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence"</string>
+ <string name="location_settings_subtitle" msgid="6846532794702613851">"Pour les applis et les services"</string>
</resources>
diff --git a/PermissionController/res/values-fr-rCA-watch/strings.xml b/PermissionController/res/values-fr-rCA-watch/strings.xml
index 8e92df18e..05fa34d11 100644
--- a/PermissionController/res/values-fr-rCA-watch/strings.xml
+++ b/PermissionController/res/values-fr-rCA-watch/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="grant_dialog_button_deny_dont_ask_again" msgid="5709879604352260492">"Refuser et ne plus demander"</string>
<string name="current_permission_template" msgid="6634462553790549887">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
- <string name="preference_show_system_apps" msgid="1055740303992024300">"Afficher les applications système"</string>
+ <string name="preference_show_system_apps" msgid="1055740303992024300">"Afficher les applis système"</string>
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Inchangeable"</string>
<string name="generic_yes" msgid="2489207724988649846">"Oui"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Annuler"</string>
diff --git a/PermissionController/res/values-fr-rCA/strings.xml b/PermissionController/res/values-fr-rCA/strings.xml
index 3e71c4d16..441baf430 100644
--- a/PermissionController/res/values-fr-rCA/strings.xml
+++ b/PermissionController/res/values-fr-rCA/strings.xml
@@ -26,7 +26,7 @@
<string name="on" msgid="280241003226755921">"Activé"</string>
<string name="off" msgid="1438489226422866263">"Désactivé"</string>
<string name="uninstall_or_disable" msgid="4496612999740858933">"Désinstaller ou désactiver"</string>
- <string name="app_not_found_dlg_title" msgid="6029482906093859756">"Application non trouvée"</string>
+ <string name="app_not_found_dlg_title" msgid="6029482906093859756">"Appli non trouvée"</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"Ne pas autoriser"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Ne pas autoriser et ne plus demander"</string>
<string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Garder « Pendant l\'utilisation de l\'appli »"</string>
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"En savoir plus"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Tout autoriser"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Toujours tout autoriser"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Autoriser un accès limité"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Sélectionner des photos et des vidéos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Sélectionner d\'autres photos"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Ne pas en sélectionner d\'autres"</string>
@@ -42,7 +43,7 @@
<string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> sur <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
<string name="permission_warning_template" msgid="2247087781222679458">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
<string name="permission_add_background_warning_template" msgid="1812914855915092273">"Toujours autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
- <string name="allow_permission_foreground_only" msgid="116465816039675404">"Seulement durant l\'utilisation de l\'application"</string>
+ <string name="allow_permission_foreground_only" msgid="116465816039675404">"Seulement durant l\'utilisation de l\'appli"</string>
<string name="allow_permission_always" msgid="5194342531206054051">"Toujours"</string>
<string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"Ne pas autoriser et ne plus demander"</string>
<string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g> autorisation(s) désactivée(s)"</string>
@@ -50,18 +51,19 @@
<string name="permission_revoked_none" msgid="9213345075484381180">"aucune autorisation désactivée"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"Autoriser"</string>
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"Toujours autoriser"</string>
- <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Lorsque vous utilisez l\'application"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Lorsque vous utilisez l\'appli"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"Activer la position exacte"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"Garder la position approximative"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"Uniquement cette fois-ci"</string>
<string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"Toujours autoriser"</string>
<string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"Autoriser à gérer tous les fichiers"</string>
<string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"Autoriser l\'accès aux fichiers multimédias"</string>
- <string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Applications"</string>
- <string name="app_permissions" msgid="3369917736607944781">"Autorisations des applications"</string>
- <string name="unused_apps" msgid="2058057455175955094">"Applications non utilisées"</string>
- <string name="no_unused_apps" msgid="12809387670415295">"Aucune application inutilisée"</string>
- <string name="zero_unused_apps" msgid="9024448554157499748">"Aucune application inutilisée"</string>
+ <string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Applis"</string>
+ <string name="app_permissions" msgid="3369917736607944781">"Autorisations des applis"</string>
+ <string name="unused_apps" msgid="2058057455175955094">"Applis non utilisées"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Modifiez une sélection de photos pour cette appli"</string>
+ <string name="no_unused_apps" msgid="12809387670415295">"Aucune appli inutilisée"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Aucune appli inutilisée"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Décisions d\'autorisation récentes"</string>
<string name="review_permission_decisions_view_all" msgid="90391040431566130">"Afficher toutes les décisions d\'autorisation récentes"</string>
<string name="review_permission_decisions_empty" msgid="8120775336417279806">"Aucune décision d\'autorisation récente"</string>
@@ -69,37 +71,37 @@
<string name="granted_permission_decision" msgid="7824827491551861365">"Vous avez autorisé <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder à <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="denied_permission_decision" msgid="5308961501779563781">"Vous avez refusé l\'accès de <xliff:g id="APP_NAME">%1$s</xliff:g> aux <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Aujourd’hui}=1{Il y a 1 jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string>
- <string name="app_disable_dlg_positive" msgid="7418444149981904940">"Désactiver l\'application"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"Si vous désactivez cette application, Android et d\'autres applications risquent de ne plus fonctionner correctement. Gardez à l\'esprit que vous ne pouvez pas supprimer cette application, étant donné qu\'elle était préinstallée sur votre appareil. En la désactivant, elle ne sera plus active et elle sera masquée sur votre appareil."</string>
+ <string name="app_disable_dlg_positive" msgid="7418444149981904940">"Désactiver l\'appli"</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"Si vous désactivez cette appli, Android et d\'autres applis risquent de ne plus fonctionner correctement. Gardez à l\'esprit que vous ne pouvez pas supprimer cette appli, étant donné qu\'elle était préinstallée sur votre appareil. En la désactivant, elle ne sera plus active et elle sera masquée sur votre appareil."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gestionnaire des autorisations"</string>
<string name="never_ask_again" msgid="4728762438198560329">"Ne plus demander"</string>
<string name="no_permissions" msgid="3881676756371148563">"Aucune autorisation"</string>
<string name="additional_permissions" msgid="5801285469338873430">"Autorisations supplémentaires"</string>
- <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Ouvrir l\'information sur l\'application"</string>
+ <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Ouvrir l\'information sur l\'appli"</string>
<string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{# autre}one{# autre}many{# autres}other{# autres}}"</string>
- <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Cette application a été conçue pour une version antérieure d\'Android. Si vous n\'accordez pas l\'autorisation, il se peut qu\'elle ne fonctionne plus correctement."</string>
- <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Cette application a été conçue pour une ancienne version d\'Android. Si vous acceptez cette autorisation, l\'accès à tout l\'espace de stockage (y compris les photos, les vidéos, les fichiers musicaux et audio ainsi que d\'autres fichiers) sera autorisé."</string>
- <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"Cette application a été conçue pour une ancienne version d\'Android. Si vous refusez cette autorisation, l\'accès à tout l\'espace de stockage (y compris les photos, les vidéos, les fichiers musicaux et audio ainsi que d\'autres fichiers) sera refusé."</string>
+ <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Cette appli a été conçue pour une version antérieure d\'Android. Si vous n\'accordez pas l\'autorisation, il se peut qu\'elle ne fonctionne plus correctement."</string>
+ <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Cette appli a été conçue pour une ancienne version d\'Android. Si vous acceptez cette autorisation, l\'accès à tout l\'espace de stockage (y compris les photos, les vidéos, les fichiers musicaux et audio ainsi que d\'autres fichiers) sera autorisé."</string>
+ <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"Cette appli a été conçue pour une ancienne version d\'Android. Si vous refusez cette autorisation, l\'accès à tout l\'espace de stockage (y compris les photos, les vidéos, les fichiers musicaux et audio ainsi que d\'autres fichiers) sera refusé."</string>
<string name="default_permission_description" msgid="4624464917726285203">"effectuer une action inconnue"</string>
- <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_0">%1$d</xliff:g> application(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
- <string name="app_permissions_group_summary2" msgid="4329922444840521150">"<xliff:g id="COUNT_0">%1$d</xliff:g> application(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+ <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_0">%1$d</xliff:g> appli(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+ <string name="app_permissions_group_summary2" msgid="4329922444840521150">"<xliff:g id="COUNT_0">%1$d</xliff:g> appli(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
<string name="menu_show_system" msgid="4254021607027872504">"Afficher le système"</string>
<string name="menu_hide_system" msgid="3855390843744028465">"Masquer le système"</string>
<string name="menu_show_7_days_data" msgid="8979611198508523706">"Afficher les 7 derniers jours"</string>
<string name="menu_show_24_hours_data" msgid="8228054833323380780">"Afficher les dernières 24 heures"</string>
<string name="manage_permission" msgid="2895385393037061964">"Gérer les autorisations"</string>
- <string name="no_apps" msgid="2412612731628386816">"Aucune application"</string>
+ <string name="no_apps" msgid="2412612731628386816">"Aucune appli"</string>
<string name="location_settings" msgid="3624412509133422562">"Paramètres de localisation"</string>
<string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> est un fournisseur de services de localisation pour cet appareil. L\'accès à la position peut être modifié dans le menu des paramètres de localisation."</string>
<string name="system_warning" msgid="1173400963234358816">"Si vous refusez cette autorisation, il est possible que cela touche certaines fonctionnalités de base de votre appareil."</string>
- <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Cette application a été conçue pour une ancienne version d\'Android. Si vous refusez à cette application l\'accès aux photos et aux vidéos, l\'accès aux fichiers musicaux et audio sera également refusé."</string>
- <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Cette application a été conçue pour une ancienne version d\'Android. Si vous refusez à cette application l\'accès aux fichiers musicaux et audio, l\'accès aux photos et aux vidéos sera également refusé."</string>
- <string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"Si vous refusez cette autorisation, certaines fonctionnalités de votre appareil gérées par cette application risquent de ne plus fonctionner correctement."</string>
+ <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Cette appli a été conçue pour une ancienne version d\'Android. Si vous refusez à cette appli l\'accès aux photos et aux vidéos, l\'accès aux fichiers musicaux et audio sera également refusé."</string>
+ <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Cette appli a été conçue pour une ancienne version d\'Android. Si vous refusez à cette appli l\'accès aux fichiers musicaux et audio, l\'accès aux photos et aux vidéos sera également refusé."</string>
+ <string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"Si vous refusez cette autorisation, certaines fonctionnalités de votre appareil gérées par cette appli risquent de ne plus fonctionner correctement."</string>
<string name="permission_summary_enforced_by_policy" msgid="4443598170942950519">"Activé conformément à la politique"</string>
<string name="permission_summary_disabled_by_policy_background_only" msgid="221995005556362660">"L\'accès en arrière-plan est désactivé par la politique"</string>
<string name="permission_summary_enabled_by_policy_background_only" msgid="8287675974767104279">"L\'accès en arrière-plan est activé par la politique"</string>
<string name="permission_summary_enabled_by_policy_foreground_only" msgid="3844582916889767831">"L\'accès en avant-plan est activé par la politique"</string>
- <string name="permission_summary_enforced_by_admin" msgid="822702574117248700">"Contrôlé par l\'administrateur"</string>
+ <string name="permission_summary_enforced_by_admin" msgid="822702574117248700">"Contrôlée par l\'administrateur"</string>
<string name="permission_summary_disabled_by_admin_background_only" msgid="3127091456731845646">"L\'accès en arrière-plan est désactivé par l\'administrateur"</string>
<string name="permission_summary_enabled_by_admin_background_only" msgid="9132423838440275757">"L\'accès en arrière-plan est activé par l\'administrateur"</string>
<string name="permission_summary_enabled_by_admin_foreground_only" msgid="1298432715610745358">"L\'accès en avant-plan est activé par l\'administrateur"</string>
@@ -108,21 +110,21 @@
<!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) -->
<!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) -->
<string name="permission_access_always" msgid="1474641821883823446">"Toujours autoriser"</string>
- <string name="permission_access_only_foreground" msgid="7801170728159326195">"Autoriser si l\'application est utilisée"</string>
+ <string name="permission_access_only_foreground" msgid="7801170728159326195">"Autoriser uniquement lorsque l\'appli est utilisée"</string>
<string name="permission_access_never" msgid="4647014230217936900">"Ne pas autoriser"</string>
<string name="loading" msgid="4789365003890741082">"Chargement en cours…"</string>
<string name="all_permissions" msgid="6911125611996872522">"Toutes les autorisations"</string>
- <string name="other_permissions" msgid="2901186127193849594">"Autres autorisations de l\'application"</string>
+ <string name="other_permissions" msgid="2901186127193849594">"Autres autorisations de l\'appli"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Demande d\'autorisation"</string>
<string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
<string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Les actions d\'installation et de désinstallation ne sont pas prises en charge par Android Wear."</string>
- <string name="permission_review_title_template_install" msgid="1284337937156289081">"Définissez les autorisations d\'accès de l\'application « <xliff:g id="APP_NAME">%1$s</xliff:g> »"</string>
- <string name="permission_review_title_template_update" msgid="3232333580548588657">"L\'application « <xliff:g id="APP_NAME">%1$s</xliff:g> » a été mise à jour. Définissez ses autorisations d\'accès."</string>
+ <string name="permission_review_title_template_install" msgid="1284337937156289081">"Définissez les autorisations d\'accès de l\'appli « <xliff:g id="APP_NAME">%1$s</xliff:g> »"</string>
+ <string name="permission_review_title_template_update" msgid="3232333580548588657">"L\'appli « <xliff:g id="APP_NAME">%1$s</xliff:g> » a été mise à jour. Définissez ses autorisations d\'accès."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Annuler"</string>
<string name="review_button_continue" msgid="2527918375047602199">"Continuer"</string>
<string name="new_permissions_category" msgid="552995090178417611">"Nouvelles autorisations"</string>
<string name="current_permissions_category" msgid="4292990083585728880">"Autorisations actuelles"</string>
- <string name="message_staging" msgid="9110563899955511866">"Pré-production de l\'application en cours…"</string>
+ <string name="message_staging" msgid="9110563899955511866">"Pré-production de l\'appli en cours…"</string>
<string name="app_name_unknown" msgid="1319665005754048952">"Inconnu"</string>
<string name="permission_usage_title" msgid="1568233336351734538">"Tableau de bord de confidentialité"</string>
<string name="auto_permission_usage_summary" msgid="7335667266743337075">"Voir les applis qui ont récemment utilisé des autorisations"</string>
@@ -130,19 +132,19 @@
<string name="perm_usage_adv_info_title" msgid="3357831829538873708">"Voir plus d\'autorisations"</string>
<string name="perm_usage_adv_info_summary_2_items" msgid="3702175198750127822">"<xliff:g id="PERMGROUP_0">%1$s</xliff:g>, <xliff:g id="PERMGROUP_1">%2$s</xliff:g>"</string>
<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> et <xliff:g id="NUM">%3$s</xliff:g> autres"</string>
- <string name="permission_group_usage_subtitle_24h" msgid="5120155996322114181">"Chronologie des moments où les applications ont utilisé votre <xliff:g id="PERMGROUP">%1$s</xliff:g> au cours des dernières 24 heures"</string>
- <string name="permission_group_usage_subtitle_7d" msgid="1465828402260324654">"Chronologie des moments où les applications ont utilisé votre <xliff:g id="PERMGROUP">%1$s</xliff:g> au cours des 7 derniers jours"</string>
- <string name="permission_usage_access_dialog_subtitle" msgid="4171772805196955753">"Lorsque cette application a utilisé votre autorisation de <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string>
+ <string name="permission_group_usage_subtitle_24h" msgid="5120155996322114181">"Chronologie des moments où les applis ont utilisé votre <xliff:g id="PERMGROUP">%1$s</xliff:g> au cours des dernières 24 heures"</string>
+ <string name="permission_group_usage_subtitle_7d" msgid="1465828402260324654">"Chronologie des moments où les applis ont utilisé votre <xliff:g id="PERMGROUP">%1$s</xliff:g> au cours des 7 derniers jours"</string>
+ <string name="permission_usage_access_dialog_subtitle" msgid="4171772805196955753">"Lorsque cette appli a utilisé votre autorisation de <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string>
<string name="permission_usage_access_dialog_learn_more" msgid="7121468469493184613">"En savoir plus"</string>
<string name="learn_more_content_description" msgid="8673699744544502539">"En savoir plus sur <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string>
- <string name="manage_permission_summary" msgid="4117555482684114317">"Contrôlez l\'accès par les applications à votre groupe <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string>
+ <string name="manage_permission_summary" msgid="4117555482684114317">"Contrôlez l\'accès par les applis à votre groupe <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string>
<string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string>
<string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string>
<string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string>
<string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# jour}one{# jour}many{# de jours}other{# jours}}"</string>
<string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# heure}one{# heure}many{# d\'heures}other{# heures}}"</string>
<string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}one{# min}many{# de minutes}other{# min}}"</string>
- <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}one{# s}many{# de secondes}other{# s}}"</string>
+ <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}one{# s}many{# s}other{# s}}"</string>
<string name="permission_usage_any_permission" msgid="6358023078298106997">"Toute autorisation"</string>
<string name="permission_usage_any_time" msgid="3802087027301631827">"À tout moment"</string>
<string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Depuis # jour}one{Depuis # jour}many{Depuis # de jours}other{Depuis # jours}}"</string>
@@ -163,7 +165,7 @@
<string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Utilisation des autorisat. dans la dernière minute"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Aucune utilisation depuis # jour}one{Aucune utilisation depuis # jour}many{Aucune utilisation depuis # de jours}other{Aucune utilisation depuis # jours}}"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Aucune utilisation depuis # heure}one{Aucune utilisation depuis # heure}many{Aucune utilisation depuis # d\'heures}other{Aucune utilisation depuis # heures}}"</string>
- <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Utilisation : 1 application}one{Utilisation : # application}many{Utilisation : # applications}other{Utilisation : # applications}}"</string>
+ <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Utilisation : 1 appli}one{Utilisation : # appli}many{Utilisation : # applis}other{Utilisation : # applis}}"</string>
<string name="permission_usage_view_details" msgid="6675335735468752787">"Tout afficher dans le tableau de bord"</string>
<string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtré par : <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"Retirer le filtre"</string>
@@ -187,29 +189,29 @@
<string name="app_permission_button_allow_all_files" msgid="1792232272599018825">"Autoriser à gérer tous les fichiers"</string>
<string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Autoriser à accéder aux éléments multimédias seulement"</string>
<string name="app_permission_button_allow_always" msgid="4573292371734011171">"Toujours autoriser"</string>
- <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Autoriser uniquement lorsque l\'appli est en cours d\'utilisation"</string>
+ <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Autoriser uniquement lorsque l\'appli est utilisée"</string>
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Toujours tout autoriser"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Toujours demander"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Ne pas autoriser"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Position exacte"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Position approximative"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Utiliser la position exacte"</string>
- <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Si la position exacte est désactivée, les applications ont accès à votre position approximative"</string>
+ <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Si la position exacte est désactivée, les applis ont accès à votre position approximative"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Autorisation : <xliff:g id="PERM">%1$s</xliff:g>"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"Accès pour cette application : <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"Accès pour cette appli : <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Afficher toutes les autorisations pour <xliff:g id="APP">%1$s</xliff:g>"</string>
- <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Afficher toutes les applications qui possèdent cette autorisation"</string>
+ <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Afficher toutes les applis qui possèdent cette autorisation"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Afficher l\'usage du microphone de l\'assistant"</string>
- <string name="unused_apps_category_title" msgid="2988455616845243901">"Paramètres des applications inutilisées"</string>
- <string name="auto_revoke_label" msgid="5068393642936571656">"Retirer les autorisations si l\'application est inutilisée"</string>
+ <string name="unused_apps_category_title" msgid="2988455616845243901">"Paramètres des applis inutilisées"</string>
+ <string name="auto_revoke_label" msgid="5068393642936571656">"Retirer les autorisations si l\'appli est inutilisée"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Retirer autorisations et libérer espace"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Suspendre l\'activité appli si inutilisée"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Retirer les autorisations, supprimer les fichiers temporaires et arrêter les notifications"</string>
- <string name="auto_revoke_summary" msgid="5867548789805911683">"Afin de protéger vos données, les autorisations pour cette application seront retirées si elle n\'est pas utilisée pendant quelques mois."</string>
- <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Afin de protéger vos données, si l\'application n\'est pas utilisée pendant quelques mois, les autorisations suivantes seront supprimées : <xliff:g id="PERMS">%1$s</xliff:g>"</string>
- <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Afin de protéger vos données, les autorisations ont été supprimées pour les applications que vous n\'avez pas utilisées depuis quelques mois."</string>
- <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"Pour accorder les autorisations de nouveau, ouvrez l\'application."</string>
- <string name="auto_revoke_disabled" msgid="8697684442991567188">"La suppression automatique des autorisations est actuellement désactivée pour cette application."</string>
+ <string name="auto_revoke_summary" msgid="5867548789805911683">"Afin de protéger vos données, les autorisations pour cette appli seront retirées si elle n\'est pas utilisée pendant quelques mois."</string>
+ <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Afin de protéger vos données, si l\'appli n\'est pas utilisée pendant quelques mois, les autorisations suivantes seront supprimées : <xliff:g id="PERMS">%1$s</xliff:g>"</string>
+ <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Afin de protéger vos données, les autorisations ont été supprimées pour les applis que vous n\'avez pas utilisées depuis quelques mois."</string>
+ <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"Pour accorder les autorisations de nouveau, ouvrez l\'appli."</string>
+ <string name="auto_revoke_disabled" msgid="8697684442991567188">"La suppression automatique des autorisations est actuellement désactivée pour cette appli."</string>
<string name="auto_revocable_permissions_none" msgid="8334929619113991466">"Aucune autorisation révocable automatiquement n\'est présentement accordée"</string>
<string name="auto_revocable_permissions_one" msgid="5299112369449458176">"L\'autorisation <xliff:g id="PERM">%1$s</xliff:g> sera supprimée."</string>
<string name="auto_revocable_permissions_two" msgid="4874067408752041716">"Les autorisations <xliff:g id="PERM_0">%1$s</xliff:g> et <xliff:g id="PERM_1">%2$s</xliff:g> seront supprimées."</string>
@@ -218,29 +220,29 @@
<string name="auto_revoked_app_summary_one" msgid="7093213590301252970">"L\'autorisation <xliff:g id="PERMISSION_NAME">%s</xliff:g> a été supprimée"</string>
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"Les autorisations <xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g> et <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g> ont été supprimées"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"L\'autorisation <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> et <xliff:g id="NUMBER">%2$s</xliff:g> autres ont été supprimées"</string>
- <string name="unused_apps_page_title" msgid="6986983535677572559">"Applications non utilisées"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"Si une application n\'est pas utilisée pendant quelques mois :\n\n• les autorisations sont retirées pour protéger vos données;\n• les notifications sont arrêtées pour économiser la pile;\n• les fichiers temporaires sont retirés pour libérer de l\'espace de stockage.\n\nPour permettre d\'accéder de nouveau aux autorisations et aux notifications, ouvrez l\'application."</string>
- <string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Si une application n\'est pas utilisée pendant un mois :\n\n• Les autorisations sont retirées pour protéger vos données.\n• Les fichiers temporaires sont retirés pour libérer de l\'espace de stockage.\n\nPour réactiver les autorisations, ouvrez l\'application."</string>
+ <string name="unused_apps_page_title" msgid="6986983535677572559">"Applis non utilisées"</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"Si une appli n\'est pas utilisée pendant quelques mois :\n\n• les autorisations sont retirées pour protéger vos données;\n• les notifications sont arrêtées pour économiser la pile;\n• les fichiers temporaires sont retirés pour libérer de l\'espace de stockage.\n\nPour permettre d\'accéder de nouveau aux autorisations et aux notifications, ouvrez l\'appli."</string>
+ <string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Si une appli n\'est pas utilisée pendant un mois :\n\n• Les autorisations sont retirées pour protéger vos données.\n• Les fichiers temporaires sont retirés pour libérer de l\'espace de stockage.\n\nPour réactiver les autorisations, ouvrez l\'appli."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Dernière ouverture : il y a plus de # mois}one{Dernière ouverture : il y a plus de # mois}many{Dernière ouverture : il y a plus de # de mois}other{Dernière ouverture : il y a plus de # mois}}"</string>
- <string name="last_opened_summary" msgid="5248984030024968808">"Dernière ouverture de l\'application le <xliff:g id="DATE">%s</xliff:g>"</string>
+ <string name="last_opened_summary" msgid="5248984030024968808">"Dernière ouverture de l\'appli le <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="last_opened_summary_short" msgid="1646067226191176825">"Dernière ouverture : <xliff:g id="DATE">%s</xliff:g>"</string>
- <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"Si vous autorisez la gestion de tous les fichiers, cette application pourra accéder à tous les fichiers enregistrés dans l\'espace de stockage partagé sur cet appareil ou sur des appareils de stockage connectés, les modifier et les supprimer. L\'application pourra accéder à des fichiers sans vous demander la permission."</string>
- <string name="special_file_access_dialog" msgid="583804114020740610">"Autoriser cette application à accéder aux fichiers qui se trouvent sur cet appareil ou sur des appareils de stockage connectés et à les modifier et les supprimer. Cette application pourra accéder à des fichiers sans vous demander la permission."</string>
- <string name="permission_description_summary_generic" msgid="5401399408814903391">"Les applications ayant cette autorisation peuvent <xliff:g id="DESCRIPTION">%1$s</xliff:g>"</string>
- <string name="permission_description_summary_activity_recognition" msgid="2652850576497070146">"Les applications qui possèdent cette autorisation peuvent accéder aux données relatives à vos activités physiques, comme la marche, le vélo, la conduite, le nombre de pas effectués et plus encore"</string>
- <string name="permission_description_summary_calendar" msgid="103329982944411010">"Les applications qui possèdent cette autorisation peuvent accéder à votre agenda"</string>
- <string name="permission_description_summary_call_log" msgid="7321437186317577624">"Les applications qui possèdent cette autorisation peuvent lire et écrire des journaux d\'appels téléphoniques"</string>
- <string name="permission_description_summary_camera" msgid="108004375101882069">"Les applications qui possèdent cette autorisation peuvent prendre des photos et filmer des vidéos"</string>
- <string name="permission_description_summary_contacts" msgid="2337798886460408996">"Les applications qui possèdent cette autorisation peuvent accéder à vos contacts"</string>
- <string name="permission_description_summary_location" msgid="2817531799933480694">"Les applications qui possèdent cette autorisation peuvent accéder à la position de cet appareil"</string>
- <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Les applications qui possèdent cette autorisation peuvent trouver les appareils à proximité, s\'y connecter et déterminer leur position relative"</string>
- <string name="permission_description_summary_microphone" msgid="630834800308329907">"Les applications qui possèdent cette autorisation peuvent enregistrer de l\'audio"</string>
- <string name="permission_description_summary_phone" msgid="4515277217435233619">"Les applications qui possèdent cette autorisation peuvent faire des appels téléphoniques et les gérer"</string>
- <string name="permission_description_summary_sensors" msgid="1836045815643119949">"Les applications qui possèdent cette autorisation peuvent accéder aux données des capteurs concernant vos signes vitaux"</string>
- <string name="permission_description_summary_sms" msgid="725999468547768517">"Les applications qui possèdent cette autorisation peuvent envoyer et afficher des messages texte"</string>
- <string name="permission_description_summary_storage" msgid="6575759089065303346">"Les applications qui possèdent cette autorisation peuvent accéder aux photos, aux médias et aux fichiers de votre appareil"</string>
- <string name="permission_description_summary_read_media_aural" msgid="3354728149930482199">"Les applications possédant cette autorisation peuvent accéder aux fichiers musicaux et audio sur cet appareil"</string>
- <string name="permission_description_summary_read_media_visual" msgid="4991801977881732641">"Les applications possédant cette autorisation peuvent accéder aux photos et aux vidéos sur cet appareil"</string>
+ <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"Si vous autorisez la gestion de tous les fichiers, cette appli pourra accéder à tous les fichiers enregistrés dans l\'espace de stockage partagé sur cet appareil ou sur des appareils de stockage connectés, les modifier et les supprimer. L\'appli pourra accéder à des fichiers sans vous demander la permission."</string>
+ <string name="special_file_access_dialog" msgid="583804114020740610">"Autoriser cette appli à accéder aux fichiers qui se trouvent sur cet appareil ou sur des appareils de stockage connectés et à les modifier et les supprimer. Cette appli pourra accéder à des fichiers sans vous demander la permission."</string>
+ <string name="permission_description_summary_generic" msgid="5401399408814903391">"Les applis ayant cette autorisation peuvent <xliff:g id="DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="permission_description_summary_activity_recognition" msgid="2652850576497070146">"Les applis qui possèdent cette autorisation peuvent accéder aux données relatives à vos activités physiques, comme la marche, le vélo, la conduite, le nombre de pas effectués et plus encore"</string>
+ <string name="permission_description_summary_calendar" msgid="103329982944411010">"Les applis qui possèdent cette autorisation peuvent accéder à votre agenda"</string>
+ <string name="permission_description_summary_call_log" msgid="7321437186317577624">"Les applis qui possèdent cette autorisation peuvent lire et écrire des journaux d\'appels téléphoniques"</string>
+ <string name="permission_description_summary_camera" msgid="108004375101882069">"Les applis qui possèdent cette autorisation peuvent prendre des photos et filmer des vidéos"</string>
+ <string name="permission_description_summary_contacts" msgid="2337798886460408996">"Les applis qui possèdent cette autorisation peuvent accéder à vos contacts"</string>
+ <string name="permission_description_summary_location" msgid="2817531799933480694">"Les applis qui possèdent cette autorisation peuvent accéder à la position de cet appareil"</string>
+ <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Les applis qui possèdent cette autorisation peuvent trouver les appareils à proximité, s\'y connecter et déterminer leur position relative"</string>
+ <string name="permission_description_summary_microphone" msgid="630834800308329907">"Les applis qui possèdent cette autorisation peuvent enregistrer de l\'audio"</string>
+ <string name="permission_description_summary_phone" msgid="4515277217435233619">"Les applis qui possèdent cette autorisation peuvent faire des appels téléphoniques et les gérer"</string>
+ <string name="permission_description_summary_sensors" msgid="1836045815643119949">"Les applis qui possèdent cette autorisation peuvent accéder aux données des capteurs concernant vos signes vitaux"</string>
+ <string name="permission_description_summary_sms" msgid="725999468547768517">"Les applis qui possèdent cette autorisation peuvent envoyer et afficher des messages texte"</string>
+ <string name="permission_description_summary_storage" msgid="6575759089065303346">"Les applis qui possèdent cette autorisation peuvent accéder aux photos, aux médias et aux fichiers de votre appareil"</string>
+ <string name="permission_description_summary_read_media_aural" msgid="3354728149930482199">"Les applis possédant cette autorisation peuvent accéder aux fichiers musicaux et audio sur cet appareil"</string>
+ <string name="permission_description_summary_read_media_visual" msgid="4991801977881732641">"Les applis possédant cette autorisation peuvent accéder aux photos et aux vidéos sur cet appareil"</string>
<string name="app_permission_most_recent_summary" msgid="4292074449384040590">"Dernier accès : <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
<string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"Autorisation présentement refusée/Dernier accès : <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
<string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Aucun accès"</string>
@@ -253,65 +255,65 @@
<string name="ask_header" msgid="2633816846459944376">"Toujours demander"</string>
<string name="denied_header" msgid="903209608358177654">"Non autorisées"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Afficher d\'autres applis pouvant accéder à tous les fichiers"</string>
- <string name="days" msgid="609563020985571393">"{count,plural, =1{1 jour}one{# jour}many{# jours}other{# jours}}"</string>
+ <string name="days" msgid="609563020985571393">"{count,plural, =1{1 jour}one{# jour}many{# de jours}other{# jours}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# heure}one{# heure}many{# d\'heures}other{# heures}}"</string>
<string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minute}one{# minute}many{# de minutes}other{# minutes}}"</string>
- <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# seconde}one{# seconde}many{# de secondes}other{# secondes}}"</string>
+ <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# seconde}one{# seconde}many{# de secondes}other{# secondes}}"</string>
<string name="permission_reminders" msgid="6528257957664832636">"Rappels d\'autorisation"</string>
- <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 application non utilisée"</string>
- <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applications non utilisées"</string>
+ <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 appli non utilisée"</string>
+ <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applis non utilisées"</string>
<string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Les autorisations ont été supprimées afin de protéger votre confidentialité. Touchez pour examiner"</string>
<string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Autoris. suppr. pour applis non utilisées"</string>
- <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Certaines applications n\'ont pas été utilisées depuis quelques mois. Touchez pour examiner."</string>
- <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# application non utilisée}one{# application non utilisée}many{# applications non utilisées}other{# applications non utilisées}}"</string>
+ <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Certaines applis n\'ont pas été utilisées depuis quelques mois. Touchez pour examiner."</string>
+ <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# appli non utilisée}one{# appli non utilisée}many{# applis non utilisées}other{# applis non utilisées}}"</string>
<string name="unused_apps_notification_content" msgid="9195026773244581246">"Les autorisations et les fichiers temporaires ont été retirés, et les notifications ont été arrêtées. Touchez pour examiner."</string>
- <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Voir les applications qui n\'ont plus leurs autorisations"</string>
- <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Pour les applications que vous n\'avez pas utilisées depuis un moment, les autorisations et les fichiers temporaires ont été retirés, et les notifications ont été désactivées."</string>
- <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"Voir les applications"</string>
+ <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Voir les applis qui n\'ont plus leurs autorisations"</string>
+ <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Pour les applis que vous n\'avez pas utilisées depuis un moment, les autorisations et les fichiers temporaires ont été retirés, et les notifications ont été désactivées."</string>
+ <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"Voir les applis"</string>
<string name="post_drive_permission_decision_reminder_title" msgid="1290697371418139976">"Vérifiez les autorisations récentes"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Pendant la conduite, vous avez donné à <xliff:g id="APP">%1$s</xliff:g> l\'accès à <xliff:g id="PERMISSION">%2$s</xliff:g>"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Pendant la conduite, vous avez donné à <xliff:g id="APP">%1$s</xliff:g> l\'accès à <xliff:g id="PERMISSION_1">%2$s</xliff:g> et à <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"Pendant la conduite, vous avez accordé <xliff:g id="COUNT">%1$d</xliff:g> autorisations à <xliff:g id="APP">%2$s</xliff:g>"</string>
- <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_0">%1$s</xliff:g> et à # autre application}one{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autre application}many{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autres applications}other{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autres applications}}"</string>
+ <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_0">%1$s</xliff:g> et à # autre appli}one{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autre appli}many{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autres applis}other{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autres applis}}"</string>
<string name="go_to_settings" msgid="1053735612211228335">"Accéder aux paramètres"</string>
- <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Certaines applications n\'ont pas été utilisées depuis quelques mois"</string>
+ <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Certaines applis n\'ont pas été utilisées depuis quelques mois"</string>
<string name="permissions_removed_category_title" msgid="1064754271178447643">"Autorisations supprimées"</string>
<string name="permission_removed_page_title" msgid="2627436155091001209">"Autorisations supprimées"</string>
- <string name="all_unused_apps_category_title" msgid="755663524704745414">"Toutes les applications non utilisées"</string>
+ <string name="all_unused_apps_category_title" msgid="755663524704745414">"Toutes les applis non utilisées"</string>
<string name="months_ago" msgid="1766026492610646354">"Il y a <xliff:g id="COUNT">%1$d</xliff:g> mois"</string>
<string name="auto_revoke_preference_summary" msgid="5517958331781391481">"Les autorisations ont été supprimées afin de protéger votre confidentialité"</string>
- <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"L\'application <xliff:g id="APP_NAME">%s</xliff:g> a accédé à votre position en arrière-plan"</string>
- <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Cette application peut toujours accéder à votre position. Touchez l\'écran pour modifier cela."</string>
- <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Voir l\'application ayant accès à vos notifications"</string>
+ <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"L\'appli <xliff:g id="APP_NAME">%s</xliff:g> a accédé à votre position en arrière-plan"</string>
+ <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Cette appli peut toujours accéder à votre position. Touchez l\'écran pour modifier cela."</string>
+ <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Voir l\'appli ayant accès à vos notifications"</string>
<string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> peut ignorer et gérer le contenu de vos notifications de même qu\'y accéder"</string>
- <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Cette application peut ignorer le contenu de vos notifications, y accéder et le gérer. Certaines applications ont besoin de cet accès pour fonctionner correctement."</string>
+ <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Cette appli peut ignorer le contenu de vos notifications, y accéder et le gérer. Certaines applis ont besoin de cet accès pour fonctionner correctement."</string>
<string name="notification_listener_remove_access_button_label" msgid="7101898782417817097">"Retirer l\'accès"</string>
<string name="notification_listener_review_app_button_label" msgid="3433073281029143924">"Afficher d\'autres options"</string>
<string name="notification_listener_remove_access_success_label" msgid="2477611529875633107">"Accès retiré"</string>
- <string name="accessibility_access_reminder_notification_title" msgid="2971317234668807566">"Vérifier l\'application ayant un accès complet à l\'appareil"</string>
- <string name="accessibility_access_reminder_notification_content" msgid="7389454158175306720">"<xliff:g id="APP_NAME">%s</xliff:g> peut voir votre écran et effectuer des actions sur votre appareil. Les applications d\'accessibilité ont besoin de ce type d\'accès pour fonctionner correctement."</string>
- <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"Cette application peut voir votre écran et effectuer des actions sur votre appareil. Les applications d\'accessibilité ont besoin de ce type d\'accès pour fonctionner correctement, mais vérifiez d\'abord l\'application et assurez-vous qu\'elle est fiable."</string>
+ <string name="accessibility_access_reminder_notification_title" msgid="2971317234668807566">"Vérifier l\'appli ayant un accès complet à l\'appareil"</string>
+ <string name="accessibility_access_reminder_notification_content" msgid="7389454158175306720">"<xliff:g id="APP_NAME">%s</xliff:g> peut voir votre écran et effectuer des actions sur votre appareil. Les applis d\'accessibilité ont besoin de ce type d\'accès pour fonctionner correctement."</string>
+ <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"Cette appli peut voir votre écran et effectuer des actions sur votre appareil. Les applis d\'accessibilité ont besoin de ce type d\'accès pour fonctionner correctement, mais vérifiez d\'abord l\'appli et assurez-vous qu\'elle est fiable."</string>
<string name="accessibility_remove_access_button_label" msgid="44145801526711640">"Retirer l\'accès"</string>
- <string name="accessibility_show_all_apps_button_label" msgid="960067249326392280">"Voir les applications ayant un accès complet"</string>
+ <string name="accessibility_show_all_apps_button_label" msgid="960067249326392280">"Voir les applis ayant un accès complet"</string>
<string name="accessibility_remove_access_success_label" msgid="4380995302917014670">"Accès retiré"</string>
<string name="safety_center_notification_app_label" msgid="2457720616141926534">"Système Android"</string>
<string name="auto_revoke_after_notification_title" msgid="5417761027669887431">"Autorisations supprimées pour protéger votre confidentialité"</string>
- <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"L\'application <xliff:g id="APP_NAME">%s</xliff:g> n\'a pas été utilisée depuis plusieurs mois. Touchez pour examiner."</string>
- <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> et une autre application n\'ont pas été utilisées depuis plusieurs mois. Touchez pour examiner."</string>
- <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> et <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> autres applications n\'ont pas été utilisées depuis plusieurs mois. Touchez pour examiner."</string>
- <string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"Une application est inutilisée"</string>
- <string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applications sont inutilisées"</string>
+ <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"L\'appli <xliff:g id="APP_NAME">%s</xliff:g> n\'a pas été utilisée depuis plusieurs mois. Touchez pour examiner."</string>
+ <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> et une autre appli n\'ont pas été utilisées depuis plusieurs mois. Touchez pour examiner."</string>
+ <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> et <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> autres applis n\'ont pas été utilisées depuis plusieurs mois. Touchez pour examiner."</string>
+ <string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"Une appli est inutilisée"</string>
+ <string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applis sont inutilisées"</string>
<string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"Les autorisations seront supprimées pour protéger votre confidentialité. Touchez pour examiner."</string>
- <string name="unused_apps_title" msgid="8589298917717872239">"Applications non utilisées"</string>
+ <string name="unused_apps_title" msgid="8589298917717872239">"Applis non utilisées"</string>
<string name="unused_apps_subtitle_after" msgid="2034267519506357898">"Les autorisations ont été supprimées pour"</string>
<string name="unused_apps_subtitle_before" msgid="5233302577076132427">"Les autorisations seront supprimées pour"</string>
<string name="unused_permissions_subtitle_two" msgid="2207266295008423015">"<xliff:g id="PERM_NAME_0">%1$s</xliff:g> et <xliff:g id="PERM_NAME_1">%2$s</xliff:g>"</string>
<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> et <xliff:g id="NUMBER_OF_PERMISSIONS">%3$s</xliff:g> autres"</string>
- <string name="unused_app_permissions_removed_summary" msgid="6779039455326071033">"Pour protéger vos données, les autorisations ont été supprimées pour les applications que vous n\'avez pas utilisées depuis plusieurs mois"</string>
- <string name="unused_app_permissions_removed_summary_some" msgid="5080490037831563441">"Pour protéger vos données, les autorisations ont été supprimées pour certaines applications que vous n\'avez pas utilisées depuis plusieurs mois"</string>
- <string name="one_unused_app_summary" msgid="7831913934488881991">"1 application n\'a pas été utilisée depuis quelques mois"</string>
- <string name="num_unused_apps_summary" msgid="1870719749940571227">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applications n\'ont pas été utilisées depuis quelques mois"</string>
- <string name="permission_subtitle_only_in_foreground" msgid="9068389431267377564">"Uniquement lorsque l\'application est en cours d\'utilisation"</string>
+ <string name="unused_app_permissions_removed_summary" msgid="6779039455326071033">"Pour protéger vos données, les autorisations ont été supprimées pour les applis que vous n\'avez pas utilisées depuis plusieurs mois"</string>
+ <string name="unused_app_permissions_removed_summary_some" msgid="5080490037831563441">"Pour protéger vos données, les autorisations ont été supprimées pour certaines applis que vous n\'avez pas utilisées depuis plusieurs mois"</string>
+ <string name="one_unused_app_summary" msgid="7831913934488881991">"1 appli n\'a pas été utilisée depuis quelques mois"</string>
+ <string name="num_unused_apps_summary" msgid="1870719749940571227">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applis n\'ont pas été utilisées depuis quelques mois"</string>
+ <string name="permission_subtitle_only_in_foreground" msgid="9068389431267377564">"Uniquement lorsque l\'appli est en cours d\'utilisation"</string>
<string name="permission_subtitle_media_only" msgid="8917869683764720717">"Éléments multimédias"</string>
<string name="permission_subtitle_all_files" msgid="4982613338298067862">"Tous les fichiers"</string>
<string name="permission_subtitle_background" msgid="8916750995309083180">"Autorisée en permanence"</string>
@@ -322,7 +324,7 @@
<string name="app_perms_content_provider_7d" msgid="3215454898257814868">"Dernier accès : au cours des 7 derniers jours"</string>
<string name="app_perms_24h_access_background" msgid="3413674718969576843">"Dernier accès : <xliff:g id="TIME_DATE">%1$s</xliff:g> • Autorisée en permanence"</string>
<string name="app_perms_24h_access_yest_background" msgid="9174750810998076725">"Dernier accès : hier à <xliff:g id="TIME_DATE">%1$s</xliff:g> • Autorisée en permanence"</string>
- <string name="app_perms_7d_access_background" msgid="408099213372185627">"Dernier accès : <xliff:g id="TIME_DATE_0">%1$s</xliff:g> à <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Autorisée en permanence"</string>
+ <string name="app_perms_7d_access_background" msgid="408099213372185627">"Dernier accès : <xliff:g id="TIME_DATE_0">%1$s</xliff:g> à <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Autorisée en permanence"</string>
<string name="app_perms_content_provider_24h_background" msgid="3825902995186961496">"Dernier accès : au cours des dernières 24 h • Autorisée en permanence"</string>
<string name="app_perms_content_provider_7d_background" msgid="4818839672116463542">"Dernier accès : au cours des 7 derniers j. • Autorisée en permanence"</string>
<string name="app_perms_24h_access_media_only" msgid="6651699644199132054">"Dernier accès : <xliff:g id="TIME_DATE">%1$s</xliff:g> • Fichiers multimédias"</string>
@@ -337,71 +339,71 @@
<string name="app_perms_content_provider_7d_all_files" msgid="7962416229708835558">"Dernier accès : au cours des 7 derniers jours • Tous les fichiers"</string>
<string name="no_permissions_allowed" msgid="6081976856354669209">"Aucune autorisation accordée"</string>
<string name="no_permissions_denied" msgid="8159923922804043282">"Aucune autorisation refusée"</string>
- <string name="no_apps_allowed" msgid="7718822655254468631">"Aucune application autorisée"</string>
- <string name="no_apps_allowed_full" msgid="8011716991498934104">"Aucune application autorisée pour tous les fichiers"</string>
- <string name="no_apps_allowed_scoped" msgid="4908850477787659501">"Aucune application autorisée pour uniquement le contenu multimédia"</string>
- <string name="no_apps_denied" msgid="7663435886986784743">"Aucune application refusée"</string>
+ <string name="no_apps_allowed" msgid="7718822655254468631">"Aucune appli autorisée"</string>
+ <string name="no_apps_allowed_full" msgid="8011716991498934104">"Aucune appli autorisée pour tous les fichiers"</string>
+ <string name="no_apps_allowed_scoped" msgid="4908850477787659501">"Aucune appli autorisée pour uniquement le contenu multimédia"</string>
+ <string name="no_apps_denied" msgid="7663435886986784743">"Aucune appli refusée"</string>
<string name="car_permission_selected" msgid="180837028920791596">"Sélectionnée"</string>
<string name="settings" msgid="5409109923158713323">"Paramètres"</string>
<string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"<xliff:g id="SERVICE_NAME">%s</xliff:g> a un accès complet à votre appareil"</string>
- <string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"<xliff:g id="NUM_SERVICES">%s</xliff:g> applications d\'accessibilité ont un accès complet à votre appareil"</string>
+ <string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"<xliff:g id="NUM_SERVICES">%s</xliff:g> applis d\'accessibilité ont un accès complet à votre appareil"</string>
<string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> peut voir votre écran, vos actions et ce que vous entrez; il peut également effectuer des actions et contrôler l\'écran."</string>
- <string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Ces applications peuvent voir votre écran, vos actions et ce que vous entrez; elles peuvent également effectuer des actions et contrôler l\'écran."</string>
+ <string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Ces applis peuvent voir votre écran, vos actions et ce que vous entrez; elles peuvent également effectuer des actions et contrôler l\'écran."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"Assistant numérique par défaut"</string>
- <string name="role_assistant_short_label" msgid="3369003713187703399">"Application d\'assistant numérique"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"Les applications d\'assistance peuvent vous aider en fonction de l\'information affichée à l\'écran. Certaines applications sont compatibles à la fois avec le lanceur d\'applications et les services d\'entrée vocale, vous permettant de bénéficier d\'une assistance intégrée."</string>
+ <string name="role_assistant_short_label" msgid="3369003713187703399">"Appli d\'assistant numérique"</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"Les applis d\'assistance peuvent vous aider en fonction de l\'information affichée à l\'écran. Certaines applis sont compatibles à la fois avec le lanceur d\'applis et les services d\'entrée vocale, vous permettant de bénéficier d\'une assistance intégrée."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Appli de navigation par défaut"</string>
- <string name="role_browser_short_label" msgid="6745009127123292296">"Application de navigateur"</string>
- <string name="role_browser_description" msgid="3465253637499842671">"Applications qui vous donnent accès à Internet et qui affichent des liens que vous pouvez toucher"</string>
- <string name="role_browser_request_title" msgid="2895200507835937192">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application par défaut pour la navigation?"</string>
+ <string name="role_browser_short_label" msgid="6745009127123292296">"Appli de navigateur"</string>
+ <string name="role_browser_description" msgid="3465253637499842671">"Applis qui vous donnent accès à Internet et qui affichent des liens que vous pouvez toucher"</string>
+ <string name="role_browser_request_title" msgid="2895200507835937192">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli par défaut pour la navigation?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"Aucune autorisation nécessaire"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"Appli de téléphone par défaut"</string>
- <string name="role_dialer_short_label" msgid="7186888549465352489">"Application de téléphonie"</string>
- <string name="role_dialer_description" msgid="8768708633696539612">"Applications qui vous permettent de faire et de recevoir des appels téléphoniques sur votre appareil"</string>
- <string name="role_dialer_request_title" msgid="5959618560705912058">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application de téléphonie par défaut?"</string>
- <string name="role_dialer_request_description" msgid="6288839625724909320">"Cette application aura accès à votre appareil photo, à vos contacts, à votre microphone, à votre téléphone et à vos messages texte"</string>
+ <string name="role_dialer_short_label" msgid="7186888549465352489">"Appli Téléphone"</string>
+ <string name="role_dialer_description" msgid="8768708633696539612">"Applis qui vous permettent de faire et de recevoir des appels téléphoniques sur votre appareil"</string>
+ <string name="role_dialer_request_title" msgid="5959618560705912058">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli de téléphonie par défaut?"</string>
+ <string name="role_dialer_request_description" msgid="6288839625724909320">"Cette appli aura accès à votre appareil photo, à vos contacts, à votre microphone, à votre téléphone et à vos messages texte"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"composeur"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Appli de mess. texte par défaut"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"Application de messagerie texte"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Applications qui vous permettent d\'utiliser votre numéro de téléphone pour envoyer et recevoir des messages texte, des photos, des vidéos et plus encore"</string>
- <string name="role_sms_request_title" msgid="7953552109601185602">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application de messagerie texte par défaut?"</string>
- <string name="role_sms_request_description" msgid="2691004766132144886">"Cette application aura accès à votre appareil photo, à vos contacts, à vos fichiers, à vos documents, à votre microphone, à votre téléphone et à vos messages texte"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"Appli de messagerie texte"</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Applis qui vous permettent d\'utiliser votre numéro de téléphone pour envoyer et recevoir des messages texte, des photos, des vidéos et plus encore"</string>
+ <string name="role_sms_request_title" msgid="7953552109601185602">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli de messagerie texte par défaut?"</string>
+ <string name="role_sms_request_description" msgid="2691004766132144886">"Cette appli aura accès à votre appareil photo, à vos contacts, à vos fichiers, à vos documents, à votre microphone, à votre téléphone et à vos messages texte"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"message texte, envoyer des messages, messages, messagerie"</string>
- <string name="role_emergency_label" msgid="7028825857206842366">"Application d\'urgence par défaut"</string>
- <string name="role_emergency_short_label" msgid="2388431453335350348">"Application d\'urgence"</string>
- <string name="role_emergency_description" msgid="5051840234887686630">"Applications qui vous permettent d\'enregistrer vos données médicales et de les rendre accessibles aux intervenants d\'urgence; pour recevoir des alertes sur des événements météorologiques graves et des désastres naturels; pour avertir d\'autres personnes lorsque vous avez besoin d\'aide"</string>
- <string name="role_emergency_request_title" msgid="8469579020654348567">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application par défaut pour les urgences?"</string>
+ <string name="role_emergency_label" msgid="7028825857206842366">"Appli d\'urgence par défaut"</string>
+ <string name="role_emergency_short_label" msgid="2388431453335350348">"Appli d\'urgence"</string>
+ <string name="role_emergency_description" msgid="5051840234887686630">"Applis qui vous permettent d\'enregistrer vos données médicales et de les rendre accessibles aux intervenants d\'urgence; pour recevoir des alertes sur des événements météorologiques graves et des désastres naturels; pour avertir d\'autres personnes lorsque vous avez besoin d\'aide"</string>
+ <string name="role_emergency_request_title" msgid="8469579020654348567">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli par défaut pour les urgences?"</string>
<string name="role_emergency_request_description" msgid="131645948770262850">"Aucune autorisation nécessaire"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"En cas d\'urgence"</string>
<string name="role_home_label" msgid="3871847846649769412">"Appli d\'accueil par défaut"</string>
- <string name="role_home_short_label" msgid="8544733747952272337">"Application sur l\'écran d\'accueil"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Applications qui remplacent les écrans d\'accueil sur votre appareil Android et qui vous donnent accès au contenu et aux fonctionnalités de votre appareil"</string>
- <string name="role_home_request_title" msgid="738136983453341081">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application d\'écran d\'accueil par défaut?"</string>
+ <string name="role_home_short_label" msgid="8544733747952272337">"Appli sur l\'écran d\'accueil"</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Applis qui remplacent les écrans d\'accueil sur votre appareil Android et qui vous donnent accès au contenu et aux fonctionnalités de votre appareil"</string>
+ <string name="role_home_request_title" msgid="738136983453341081">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli d\'écran d\'accueil par défaut?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Aucune autorisation nécessaire"</string>
- <string name="role_home_search_keywords" msgid="3830755001192666285">"lanceur d\'applications"</string>
+ <string name="role_home_search_keywords" msgid="3830755001192666285">"lanceur d\'applis"</string>
<string name="role_call_redirection_label" msgid="5785304207206147590">"Appli redirect. appel par déf."</string>
<string name="role_call_redirection_short_label" msgid="7568143419571217757">"Appeler l\'appli. de redirection"</string>
- <string name="role_call_redirection_description" msgid="6091669882014664420">"Applications qui vous permettent de transférer des appels sortants vers un autre numéro de téléphone"</string>
- <string name="role_call_redirection_request_title" msgid="2816244455003562925">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application par défaut pour la redirection des appels?"</string>
+ <string name="role_call_redirection_description" msgid="6091669882014664420">"Applis qui vous permettent de transférer des appels sortants vers un autre numéro de téléphone"</string>
+ <string name="role_call_redirection_request_title" msgid="2816244455003562925">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli par défaut pour la redirection des appels?"</string>
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"Aucune autorisation nécessaire"</string>
<string name="role_call_screening_label" msgid="883935222060878724">"Appli d\'identif. et de filtrage par défaut"</string>
<string name="role_call_screening_short_label" msgid="2048465565063130834">"Appli identif. et filtr. appels"</string>
- <string name="role_call_screening_description" msgid="2349431420497468981">"Applications qui vous permettent d\'identifier les appelants et de bloquer les appels indésirables, les appels automatisés et les numéros indésirables"</string>
- <string name="role_call_screening_request_title" msgid="7358309224566977290">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application par défaut pour l\'identification et le filtrage des appelants?"</string>
+ <string name="role_call_screening_description" msgid="2349431420497468981">"Applis qui vous permettent d\'identifier les appelants et de bloquer les appels indésirables, les appels automatisés et les numéros indésirables"</string>
+ <string name="role_call_screening_request_title" msgid="7358309224566977290">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli par défaut pour l\'identification et le filtrage des appelants?"</string>
<string name="role_call_screening_request_description" msgid="7338511921032446006">"Aucune autorisation nécessaire"</string>
- <string name="role_automotive_navigation_label" msgid="2701890757955474751">"Application de navigation par défaut"</string>
- <string name="role_automotive_navigation_short_label" msgid="5165823092506922457">"Application de navigation"</string>
- <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Applications prenant en charge la recherche par points d\'intérêt et la navigation avec itinéraire détaillé"</string>
- <string name="role_automotive_navigation_request_title" msgid="7525693151489384300">"Définir l\'application <xliff:g id="APP_NAME">%1$s</xliff:g> en tant qu\'application de navigation par défaut?"</string>
+ <string name="role_automotive_navigation_label" msgid="2701890757955474751">"Appli de navigation par défaut"</string>
+ <string name="role_automotive_navigation_short_label" msgid="5165823092506922457">"Appli de navigation"</string>
+ <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Applis prenant en charge la recherche par points d\'intérêt et la navigation avec itinéraire détaillé"</string>
+ <string name="role_automotive_navigation_request_title" msgid="7525693151489384300">"Définir l\'appli <xliff:g id="APP_NAME">%1$s</xliff:g> en tant qu\'appli de navigation par défaut?"</string>
<string name="role_automotive_navigation_request_description" msgid="7073023813249245540">"Aucune autorisation nécessaire"</string>
<string name="role_watch_description" msgid="267003778693177779">"<xliff:g id="APP_NAME">%1$s</xliff:g> aura l\'autorisation d\'interagir avec vos notifications et d\'accéder aux autorisations pour votre téléphone, vos messages texte, vos contacts et votre agenda."</string>
- <string name="role_app_streaming_description" msgid="7341638576226183992">"<xliff:g id="APP_NAME">%1$s</xliff:g> aura l\'autorisation d\'interagir avec vos notifications et de diffuser vos applications à l\'appareil connecté."</string>
+ <string name="role_app_streaming_description" msgid="7341638576226183992">"<xliff:g id="APP_NAME">%1$s</xliff:g> aura l\'autorisation d\'interagir avec vos notifications et de diffuser vos applis à l\'appareil connecté."</string>
<string name="role_companion_device_computer_description" msgid="416099879217066377">"Ce service partage vos photos, vos contenus multimédias et vos notifications de votre téléphone vers d\'autres appareils."</string>
<string name="role_notes_label" msgid="7451627001058089536">"Appli prise de notes par défaut"</string>
- <string name="role_notes_short_label" msgid="8796604147546125285">"Application de prise de notes"</string>
- <string name="role_notes_description" msgid="8496852798616883551">"Applications qui vous permettent de prendre des notes sur votre appareil"</string>
+ <string name="role_notes_short_label" msgid="8796604147546125285">"Appli de prise de notes"</string>
+ <string name="role_notes_description" msgid="8496852798616883551">"Applis qui vous permettent de prendre des notes sur votre appareil"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"remarques"</string>
- <string name="request_role_current_default" msgid="738722892438247184">"Application par défaut actuelle"</string>
+ <string name="request_role_current_default" msgid="738722892438247184">"Appli par défaut actuelle"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ne plus me demander"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Définir par défaut"</string>
<string name="phone_call_uses_microphone" msgid="233569591461187177">"Le micro est utilisé pour un &lt;b&gt;appel téléphonique&lt;/b&gt;"</string>
@@ -418,53 +420,53 @@
<string name="ongoing_usage_dialog_title_mic_camera" msgid="9079747867228772797">"Utilisation récente du microphone et de l\'appareil photo"</string>
<string name="ongoing_usage_dialog_separator" msgid="1715181526581520068">", "</string>
<string name="ongoing_usage_dialog_last_separator" msgid="4170995004748832163">" et "</string>
- <string name="default_app_search_keyword" msgid="8330125736889689743">"applications par défaut"</string>
+ <string name="default_app_search_keyword" msgid="8330125736889689743">"applis par défaut"</string>
<string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Microphone et appareil photo"</string>
<string name="settings_button" msgid="4414988414732479636">"Paramètres"</string>
- <string name="default_apps" msgid="5119201969348748639">"Applications par défaut"</string>
- <string name="no_default_apps" msgid="2593466527182950231">"Aucune application par défaut"</string>
- <string name="default_apps_more" msgid="4078194675848858093">"Autres applications par défaut"</string>
+ <string name="default_apps" msgid="5119201969348748639">"Applis par défaut"</string>
+ <string name="no_default_apps" msgid="2593466527182950231">"Aucune appli par défaut"</string>
+ <string name="default_apps_more" msgid="4078194675848858093">"Autres applis par défaut"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Ouverture des liens"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Par défaut pour util. profess."</string>
<string name="default_app_none" msgid="9084592086808194457">"Aucune"</string>
- <string name="default_app_system_default" msgid="6218386768175513760">"(Valeurs par défaut du système)"</string>
- <string name="default_app_no_apps" msgid="115720991680586885">"Aucune application"</string>
+ <string name="default_app_system_default" msgid="6218386768175513760">"(Paramètre(s) système par défaut)"</string>
+ <string name="default_app_no_apps" msgid="115720991680586885">"Aucune appli"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Sélectionnée"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Sélectionnée – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
- <string name="special_app_access_search_keyword" msgid="8032347212290774210">"accès spécial des applications"</string>
+ <string name="special_app_access_search_keyword" msgid="8032347212290774210">"accès spécial des applis"</string>
<string name="special_app_access" msgid="5019319067120213797">"Accès spécial des applis"</string>
<string name="no_special_app_access" msgid="6950277571805106247">"Aucun accès spécial pour applis"</string>
- <string name="special_app_access_no_apps" msgid="4102911722787886970">"Aucune application"</string>
+ <string name="special_app_access_no_apps" msgid="4102911722787886970">"Aucune appli"</string>
<string name="home_missing_work_profile_support" msgid="1756855847669387977">"Non compatible avec les profils professionnels"</string>
- <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Remarque : Si vous redémarrez votre appareil et que vous avez défini un verrouillage de l\'écran, cette application ne pourra pas démarrer tant que vous n\'avez pas déverrouillé votre appareil"</string>
- <string name="assistant_confirmation_message" msgid="7476540402884416212">"L\'assistant pourra accéder aux données des applications en cours d\'utilisation sur votre système, y compris les données visibles à l\'écran ou accessibles au sein des applications."</string>
+ <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Remarque : Si vous redémarrez votre appareil et que vous avez défini un verrouillage de l\'écran, cette appli ne pourra pas démarrer tant que vous n\'avez pas déverrouillé votre appareil"</string>
+ <string name="assistant_confirmation_message" msgid="7476540402884416212">"L\'assistant pourra accéder aux données des applis en cours d\'utilisation sur votre système, y compris les données visibles à l\'écran ou accessibles au sein des applis."</string>
<string name="incident_report_channel_name" msgid="3144954065936288440">"Partager les données de débogage"</string>
<string name="incident_report_notification_title" msgid="4635984625656519773">"Partager des données de débogage détaillées?"</string>
<string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> souhaite téléverser des données de débogage."</string>
<string name="incident_report_dialog_title" msgid="669104389325204095">"Partager les données de débogage?"</string>
<string name="incident_report_dialog_intro" msgid="5897733669850951832">"Le système a détecté un problème."</string>
- <string name="incident_report_dialog_text" msgid="5675553296891757523">"L\'application <xliff:g id="APP_NAME_0">%1$s</xliff:g> souhaite téléverser un rapport de bogue créé le <xliff:g id="DATE">%2$s</xliff:g> à <xliff:g id="TIME">%3$s</xliff:g> sur cet appareil. Les rapports de bogue contiennent des données personnelles relatives à votre appareil ou enregistrées par des applications, comme des noms d\'utilisateur, des données de localisation, des identifiants d\'appareils et des renseignements relatifs au réseau. Ne partagez les rapports de bogue qu\'avec des personnes et des applications que vous jugez fiables. Autoriser l\'application <xliff:g id="APP_NAME_1">%4$s</xliff:g> à téléverser un rapport de bogue?"</string>
+ <string name="incident_report_dialog_text" msgid="5675553296891757523">"L\'appli <xliff:g id="APP_NAME_0">%1$s</xliff:g> souhaite téléverser un rapport de bogue créé le <xliff:g id="DATE">%2$s</xliff:g> à <xliff:g id="TIME">%3$s</xliff:g> sur cet appareil. Les rapports de bogue contiennent des données personnelles relatives à votre appareil ou enregistrées par des applis, comme des noms d\'utilisateur, des données de localisation, des identifiants d\'appareils et des renseignements relatifs au réseau. Ne partagez les rapports de bogue qu\'avec des personnes et des applis que vous jugez fiables. Autoriser l\'appli <xliff:g id="APP_NAME_1">%4$s</xliff:g> à téléverser un rapport de bogue?"</string>
<string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Une erreur s\'est produite lors du traitement du rapport de bogue pour <xliff:g id="APP_NAME">%1$s</xliff:g>, alors le partage des données de débogage détaillées a été refusé. Nous sommes désolés de l\'interruption."</string>
<string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Autoriser"</string>
<string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Refuser"</string>
<string name="adjust_user_sensitive_title" msgid="4196724451314280527">"Paramètres avancés"</string>
<string name="menu_adjust_user_sensitive" msgid="6497923610654425780">"Paramètres avancés"</string>
- <string name="adjust_user_sensitive_globally_title" msgid="8649190949066029174">"Afficher l\'usage des applications système"</string>
- <string name="adjust_user_sensitive_globally_summary" msgid="129467818433773912">"Afficher l\'utilisation des autorisations par les applications système dans la barre d\'état, le tableau de bord et ailleurs"</string>
+ <string name="adjust_user_sensitive_globally_title" msgid="8649190949066029174">"Afficher l\'usage des applis système"</string>
+ <string name="adjust_user_sensitive_globally_summary" msgid="129467818433773912">"Afficher l\'utilisation des autorisations par les applis système dans la barre d\'état, le tableau de bord et ailleurs"</string>
<string name="adjust_user_sensitive_per_app_header" msgid="4543506440989005648">"Mettre en avant l\'utilisation pour les éléments suivants"</string>
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Afficher la détection des déclencheurs de l\'assistant"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Afficher l\'icône dans la barre d\'état lorsque le microphone est utilisé pour activer l\'assistant vocal"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Autoriser <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder aux photos et aux médias de votre appareil?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à vos contacts?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de cet appareil?"</string>
- <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"L\'application aura uniquement accès à la position lorsque vous l\'utilisez"</string>
+ <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"L\'appli aura uniquement accès à la position lorsque vous l\'utilisez"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de cet appareil?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Cette appli pourrait demander à accéder à votre position en tout temps, même si vous ne l\'utilisez pas. Accordez cette autorisation dans les "<annotation id="link">"paramètres"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Modifier l\'accès à la position pour « <xliff:g id="APP_NAME">%1$s</xliff:g> »?"</string>
- <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Cette application veut accéder à votre position en tout temps, même lorsque vous ne l\'utilisez pas. Accordez cette autorisation dans les "<annotation id="link">"paramètres"</annotation>"."</string>
+ <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Cette appli veut accéder à votre position en tout temps, même lorsque vous ne l\'utilisez pas. Accordez cette autorisation dans les "<annotation id="link">"paramètres"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à trouver les appareils à proximité, à s\'y connecter et à déterminer leur position relative?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à trouver les appareils à proximité, à s\'y connecter et à déterminer leur position relative? "<annotation id="link">"Accordez l\'autorisation dans les paramètres."</annotation></string>
- <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Modifier l\'accès à la position de l\'application <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> d\'approximative à exacte?"</string>
+ <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Modifier l\'accès à la position de l\'appli <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> d\'approximative à exacte?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position approximative de cet appareil?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exacte"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Approximative"</string>
@@ -477,24 +479,24 @@
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos et aux vidéos sur cet appareil?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à plus de photos et de vidéos sur cet appareil?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à enregistrer l\'audio?"</string>
- <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'application pourra uniquement enregistrer de l\'audio lorsque vous l\'utilisez"</string>
+ <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'appli pourra uniquement enregistrer de l\'audio lorsque vous l\'utilisez"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à enregistrer de l\'audio?"</string>
- <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Cette application pourrait demander à enregistrer de l\'audio en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string>
+ <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Cette appli pourrait demander à enregistrer de l\'audio en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Modifier l\'accès au microphone pour « <xliff:g id="APP_NAME">%1$s</xliff:g> »?"</string>
- <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Cette application veut enregistrer de l\'audio en tout temps, même si vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string>
- <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder à vos activités physiques?"</string>
+ <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Cette appli veut enregistrer de l\'audio en tout temps, même si vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string>
+ <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à vos activités physiques?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à prendre des photos et à enregistrer des vidéos?"</string>
- <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"L\'application pourra uniquement prendre des photos et enregistrer des vidéos lorsque vous l\'utilisez"</string>
+ <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"L\'appli pourra uniquement prendre des photos et enregistrer des vidéos lorsque vous l\'utilisez"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à prendre des photos et à enregistrer des vidéos?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Cette appli pourrait demander à prendre des photos et des vidéos en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Modifier l\'accès à l\'appareil photo pour « <xliff:g id="APP_NAME">%1$s</xliff:g> »?"</string>
- <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Cette application veut prendre des photos et enregistrer des vidéos en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string>
+ <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Cette appli veut prendre des photos et enregistrer des vidéos en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à vos journaux d\'appels?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à faire et à gérer des appels téléphoniques?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder aux données des capteurs pour vos signes vitaux?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Cette application souhaite pouvoir accéder en tout temps aux données des capteurs relatives à vos signes vitaux, même lorsque vous ne l\'utilisez pas. Pour effectuer ce changement, "<annotation id="link">"accédez aux paramètres"</annotation>"."</string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder aux données des capteurs relatives à vos signes vitaux?"</string>
- <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Pour permettre à cette application d\'accéder aux données des capteurs corporels en tout temps, même lorsque vous n\'utilisez pas l\'application, "<annotation id="link">"accédez aux paramètres"</annotation>"."</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Cette appli souhaite pouvoir accéder en tout temps aux données des capteurs relatives à vos signes vitaux, même lorsque vous ne l\'utilisez pas. Pour effectuer ce changement, "<annotation id="link">"accédez aux paramètres"</annotation>"."</string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux données des capteurs relatives à vos signes vitaux?"</string>
+ <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Pour permettre à cette appli d\'accéder aux données des capteurs corporels en tout temps, même lorsque vous n\'utilisez pas l\'appli, "<annotation id="link">"accédez aux paramètres"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Voulez-vous continuer à autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux données des capteurs corporels pendant l\'utilisation de l\'appli?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à vous envoyer des notifications?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Autorisations contrôlées"</string>
@@ -502,9 +504,9 @@
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Votre organisation permet à <xliff:g id="APP_NAME">%1$s</xliff:g> d\'accéder à votre position"</string>
<string name="other_permissions_label" msgid="8986184335503271992">"Autres autorisations"</string>
<string name="not_used_permissions_label" msgid="3939839426115141264">"Autorisations utilisées par le système"</string>
- <string name="not_used_permissions_description" msgid="7595514824169388718">"Autorisations utilisées par les applications système."</string>
+ <string name="not_used_permissions_description" msgid="7595514824169388718">"Autorisations utilisées par les applis système."</string>
<string name="additional_permissions_label" msgid="7693557637462569046">"Autorisations supplémentaires"</string>
- <string name="additional_permissions_description" msgid="2186611950890732112">"Autorisations définies par les applications."</string>
+ <string name="additional_permissions_description" msgid="2186611950890732112">"Autorisations définies par les applis."</string>
<string name="privdash_label_camera" msgid="1426440033626198096">"Appareil photo"</string>
<string name="privdash_label_microphone" msgid="8415035835803511693">"Microphone"</string>
<string name="privdash_label_location" msgid="6882400763866489291">"Position"</string>
@@ -512,12 +514,12 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Aucun"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Dernières\n24 heures"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7 derniers\njours"</string>
- <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est protégée par Android. Comme vos données sont traitées sur cet appareil, l\'utilisation de l\'autorisation de celle-ci ne s\'affiche pas sur la barre d\'état de votre tableau de bord de confidentialité."</string>
- <string name="exempt_info_label" msgid="6286190981253476699">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est protégée par Android. Comme vos données sont traitées sur cet appareil, l\'utilisation de l\'autorisation de celle-ci ne s\'affiche pas sur votre tableau de bord de confidentialité."</string>
+ <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"L\'appli <xliff:g id="APP_NAME">%1$s</xliff:g> est protégée par Android. Comme vos données sont traitées sur cet appareil, l\'utilisation de l\'autorisation de celle-ci ne s\'affiche pas sur la barre d\'état de votre tableau de bord de confidentialité."</string>
+ <string name="exempt_info_label" msgid="6286190981253476699">"L\'appli <xliff:g id="APP_NAME">%1$s</xliff:g> est protégée par Android. Comme vos données sont traitées sur cet appareil, l\'utilisation de l\'autorisation de celle-ci ne s\'affiche pas sur votre tableau de bord de confidentialité."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"L\'appareil photo de l\'appareil est bloqué"</string>
<string name="blocked_microphone_title" msgid="1631517143648232585">"Le microphone de l\'appareil est bloqué"</string>
<string name="blocked_location_title" msgid="2005608279812892383">"La localisation de l\'appareil est désactivée"</string>
- <string name="blocked_sensor_summary" msgid="4443707628305027375">"Pour les applications et les services"</string>
+ <string name="blocked_sensor_summary" msgid="4443707628305027375">"Pour les applis et les services"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Modifier"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sécurité et confidentialité"</string>
@@ -540,8 +542,8 @@
<string name="permissions_removed_qs" msgid="8957319130625294572">"L\'autorisation a été retirée"</string>
<string name="camera_usage_qs" msgid="4394233566086665994">"Consulter les utilisations récentes de l\'appareil photo"</string>
<string name="microphone_usage_qs" msgid="8527666682168170417">"Consulter les utilisations récentes du microphone"</string>
- <string name="remove_camera_qs" msgid="3649996161066883350">"Retirer l\'autorisation de cette application"</string>
- <string name="remove_microphone_qs" msgid="1276551965129953198">"Retirer l\'autorisation de cette application"</string>
+ <string name="remove_camera_qs" msgid="3649996161066883350">"Retirer l\'autorisation de cette appli"</string>
+ <string name="remove_microphone_qs" msgid="1276551965129953198">"Retirer l\'autorisation de cette appli"</string>
<string name="manage_service_qs" msgid="7862555549364153805">"Gérer le service"</string>
<string name="manage_permissions_qs" msgid="3780541819763475434">"Gérer les autorisations"</string>
<string name="active_call_usage_qs" msgid="8559974395932523391">"En cours d\'utilisation par un appel téléphonique"</string>
@@ -562,41 +564,41 @@
<string name="media_confirm_dialog_title_q_to_s_aural_deny" msgid="3128147568953297969">"L\'accès aux photos et aux vidéos ne sera pas non plus autorisé"</string>
<string name="media_confirm_dialog_title_q_to_s_visual_allow" msgid="6310682466493330434">"L\'accès aux fichiers musicaux et audio sera également autorisé"</string>
<string name="media_confirm_dialog_title_q_to_s_visual_deny" msgid="1123845663785900471">"L\'accès aux fichiers musicaux et audio ne sera pas non plus autorisé"</string>
- <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application peut accéder aux fichiers musicaux et audio, elle sera également autorisée à accéder aux photos, aux vidéos et à d\'autres fichiers."</string>
- <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application ne peut pas accéder aux fichiers musicaux et audio, elle ne sera pas non plus autorisée à accéder aux photos, aux vidéos et à d\'autres fichiers."</string>
- <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application peut accéder aux photos et aux vidéos, elle sera également autorisée à accéder aux fichiers musicaux et audio ainsi qu\'à d\'autres fichiers."</string>
- <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application ne peut pas accéder aux photos ni aux vidéos, elle ne sera pas non plus autorisée à accéder aux fichiers musicaux et audio ni à d\'autres fichiers."</string>
- <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application peut accéder aux fichiers musicaux et audio, elle sera également autorisée à accéder aux photos et aux vidéos."</string>
- <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application ne peut pas accéder aux fichiers musicaux ni audio, elle ne sera pas non plus autorisée à accéder aux photos ni aux vidéos."</string>
- <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application peut accéder aux photos et aux vidéos, elle sera également autorisée à accéder aux fichiers musicaux et audio."</string>
- <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application ne peut pas accéder aux fichiers musicaux ni audio, elle ne sera pas non plus autorisée à accéder aux photos ni aux vidéos."</string>
- <string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"Voir les applications ayant accès aux données de localisation en arrière-plan"</string>
- <string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> peut toujours accéder à votre position, même quand l\'application est fermée"</string>
- <string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Voir les applications ayant accès aux données de localisation en arrière-plan"</string>
- <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"Cette application peut toujours accéder à votre position, même lorsqu\'elle est fermée.\n\nCertaines applications de sécurité et d\'urgence requièrent l\'accès à votre position en arrière-plan pour fonctionner correctement."</string>
+ <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli peut accéder aux fichiers musicaux et audio, elle sera également autorisée à accéder aux photos, aux vidéos et à d\'autres fichiers."</string>
+ <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli ne peut pas accéder aux fichiers musicaux et audio, elle ne sera pas non plus autorisée à accéder aux photos, aux vidéos et à d\'autres fichiers."</string>
+ <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli peut accéder aux photos et aux vidéos, elle sera également autorisée à accéder aux fichiers musicaux et audio ainsi qu\'à d\'autres fichiers."</string>
+ <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli ne peut pas accéder aux photos ni aux vidéos, elle ne sera pas non plus autorisée à accéder aux fichiers musicaux et audio ni à d\'autres fichiers."</string>
+ <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli peut accéder aux fichiers musicaux et audio, elle sera également autorisée à accéder aux photos et aux vidéos."</string>
+ <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli ne peut pas accéder aux fichiers musicaux ni audio, elle ne sera pas non plus autorisée à accéder aux photos ni aux vidéos."</string>
+ <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli peut accéder aux photos et aux vidéos, elle sera également autorisée à accéder aux fichiers musicaux et audio."</string>
+ <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli ne peut pas accéder aux fichiers musicaux ni audio, elle ne sera pas non plus autorisée à accéder aux photos ni aux vidéos."</string>
+ <string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"Voir les applis ayant accès aux données de localisation en arrière-plan"</string>
+ <string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> peut toujours accéder à votre position, même quand l\'appli est fermée"</string>
+ <string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Voir les applis ayant accès aux données de localisation en arrière-plan"</string>
+ <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"Cette appli peut toujours accéder à votre position, même lorsqu\'elle est fermée.\n\nCertaines applis de sécurité et d\'urgence requièrent l\'accès à votre position en arrière-plan pour fonctionner correctement."</string>
<string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"Accès modifié"</string>
<string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Voir les utilisations récentes de la position"</string>
<string name="privacy_controls_title" msgid="7605929972256835199">"Paramètres de confidentialité"</string>
<string name="camera_toggle_title" msgid="1251201397431837666">"Accès à la caméra"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"Accès au microphone"</string>
- <string name="perm_toggle_description" msgid="7801326363741451379">"Pour les applications et les services"</string>
- <string name="mic_toggle_description" msgid="9163104307990677157">"Pour les applications et les services. Si ce paramètre est désactivé, il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"Voir les applications et les services qui ont accès à votre emplacement"</string>
+ <string name="perm_toggle_description" msgid="7801326363741451379">"Pour les applis et les services"</string>
+ <string name="mic_toggle_description" msgid="9163104307990677157">"Pour les applis et les services. Si ce paramètre est désactivé, il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence."</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"Voir les applis et les services qui ont accès à votre emplacement"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Afficher l\'accès au presse-papiers"</string>
- <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Un message s\'affiche lorsque les applications accèdent à du texte, à des images ou à d\'autres contenus que vous avez copiés"</string>
+ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Un message s\'affiche lorsque les applis accèdent à du texte, à des images ou à d\'autres contenus que vous avez copiés"</string>
<string name="show_password_title" msgid="2877269286984684659">"Afficher les mots de passe"</string>
<string name="show_password_summary" msgid="1110166488865981610">"Les caractères s\'affichent brièvement pendant la saisie"</string>
- <string name="permission_rationale_message_location" msgid="2153841534298068414">"Cette application indique qu\'elle peut partager des données de localisation avec des tiers"</string>
+ <string name="permission_rationale_message_location" msgid="2153841534298068414">"Cette appli indique qu\'elle peut partager des données de localisation avec des tiers"</string>
<string name="permission_rationale_location_title" msgid="2404797182678793506">"Partage des données et localisation"</string>
<string name="permission_rationale_data_sharing_source_title" msgid="6874604543125814316">"Provenance des renseignements sur le partage des données"</string>
- <string name="permission_rationale_data_sharing_device_manufacturer_message" msgid="7569261218145653185">"Le développeur a fourni des renseignements au fabricant de cet appareil sur la façon dont cette application partage les données. Le développeur peut mettre à jour ces renseignements au fil du temps."</string>
- <string name="permission_rationale_data_sharing_source_message" msgid="8330794595417986883">"Le développeur a fourni des renseignements à "<annotation id="link"><annotation id="install_source" example="App Store">"%1$s"</annotation></annotation>" sur la façon dont cette application partage les données. Le développeur peut mettre à jour ces renseignements au fil du temps."</string>
+ <string name="permission_rationale_data_sharing_device_manufacturer_message" msgid="7569261218145653185">"Le développeur a fourni des renseignements au fabricant de cet appareil sur la façon dont cette appli partage les données. Le développeur peut mettre à jour ces renseignements au fil du temps."</string>
+ <string name="permission_rationale_data_sharing_source_message" msgid="8330794595417986883">"Le développeur a fourni des renseignements à "<annotation id="link"><annotation id="install_source" example="App Store">"%1$s"</annotation></annotation>" sur la façon dont cette appli partage les données. Le développeur peut mettre à jour ces renseignements au fil du temps."</string>
<string name="permission_rationale_location_purpose_title" msgid="5115877143670012618">"L\'appli peut partager des données de loc. pour :"</string>
<string name="permission_rationale_permission_data_sharing_varies_title" msgid="9103718980919908316">"Les pratiques de partage de données peuvent varier"</string>
- <string name="permission_rationale_data_sharing_varies_message" msgid="4224469559084489222">"Les pratiques relatives aux données peuvent varier en fonction de la version de votre application, de son utilisation, de votre région et de votre âge. "<annotation id="link">"En savoir plus sur le partage des données"</annotation></string>
- <string name="permission_rationale_data_sharing_varies_message_without_link" msgid="4912763761399025094">"Les pratiques relatives aux données peuvent varier en fonction de la version de votre application, de son utilisation, de votre région et de votre âge."</string>
+ <string name="permission_rationale_data_sharing_varies_message" msgid="4224469559084489222">"Les pratiques relatives aux données peuvent varier en fonction de la version de votre appli, de son utilisation, de votre région et de votre âge. "<annotation id="link">"En savoir plus sur le partage des données"</annotation></string>
+ <string name="permission_rationale_data_sharing_varies_message_without_link" msgid="4912763761399025094">"Les pratiques relatives aux données peuvent varier en fonction de la version de votre appli, de son utilisation, de votre région et de votre âge."</string>
<string name="permission_rationale_location_settings_title" msgid="7204145004850190953">"Vos données de localisation"</string>
- <string name="permission_rationale_permission_settings_message" msgid="631286040979660267">"Modifiez les autorisations d\'accès de cette application dans les "<annotation id="link">"paramètres de confidentialité"</annotation></string>
+ <string name="permission_rationale_permission_settings_message" msgid="631286040979660267">"Modifiez les autorisations d\'accès de cette appli dans les "<annotation id="link">"paramètres de confidentialité"</annotation></string>
<string name="permission_rationale_purpose_app_functionality" msgid="8397736681065841405">"le fonctionnement de l\'appli;"</string>
<string name="permission_rationale_purpose_analytics" msgid="2070800501189620712">"Les analyses"</string>
<string name="permission_rationale_purpose_developer_communications" msgid="6453047018892062374">"les communications du développeur;"</string>
@@ -606,11 +608,11 @@
<string name="permission_rationale_purpose_account_management" msgid="2985772421946688879">"la gestion du compte."</string>
<string name="app_permission_rationale_message" msgid="8511466916077100713">"Sécurité des données"</string>
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"Les données de localisation peuvent être partagées"</string>
- <string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Cette application indique qu\'elle peut partager vos données de localisation avec des tiers"</string>
+ <string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Cette appli indique qu\'elle peut partager vos données de localisation avec des tiers"</string>
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Mises à jour des pratiques de partage des données pour la localisation"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"Passez en revue les applications qui ont changé la façon dont elles peuvent partager vos données de localisation"</string>
- <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Ces applications ont modifié la façon dont elles peuvent partager vos données de localisation. Elles peuvent ne pas les avoir partagées auparavant, ou peuvent maintenant les partager à des fins d\'annonces ou de marketing."</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Les développeurs de ces applications ont fourni des renseignements sur leurs pratiques de partage des données à une boutique d\'applications. Ils peuvent les mettre à jour au fil du temps.\n\nLes pratiques de partage des données peuvent varier en fonction de la version de votre application, de son utilisation, de votre région et de votre âge."</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"Passez en revue les applis qui ont changé la façon dont elles peuvent partager vos données de localisation"</string>
+ <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Ces applis ont modifié la façon dont elles peuvent partager vos données de localisation. Elles peuvent ne pas les avoir partagées auparavant, ou peuvent maintenant les partager à des fins d\'annonces ou de marketing."</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Les développeurs de ces applis ont fourni des renseignements sur leurs pratiques de partage des données à une boutique d\'applis. Ils peuvent les mettre à jour au fil du temps.\n\nLes pratiques de partage des données peuvent varier en fonction de la version de votre appli, de son utilisation, de votre région et de votre âge."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"En savoir plus sur le partage des données"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Vos données de localisation sont maintenant partagées avec des tiers"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Vos données de localisation sont maintenant partagées avec des tiers à des fins d\'annonces ou de marketing"</string>
diff --git a/PermissionController/res/values-fr-television/strings.xml b/PermissionController/res/values-fr-television/strings.xml
index d3ed1f493..f85d6cb97 100644
--- a/PermissionController/res/values-fr-television/strings.xml
+++ b/PermissionController/res/values-fr-television/strings.xml
@@ -21,7 +21,7 @@
<string name="current_permission_template" msgid="6240787325714651204">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
<string name="preference_show_system_apps" msgid="4262140518693221093">"Afficher les applications système"</string>
<string name="app_permissions_decor_title" msgid="7438716722786036814">"Autorisations des applications"</string>
- <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Autorisations de l\'application"</string>
+ <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Autorisations applis"</string>
<string name="permission_apps_decor_title" msgid="2811550489429789828">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
<string name="additional_permissions_decor_title" msgid="5113847982502484225">"Autorisations supplémentaires"</string>
<string name="system_apps_decor_title" msgid="4402004958937474803">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-fr/strings.xml b/PermissionController/res/values-fr/strings.xml
index 6af03ec38..64b850dae 100644
--- a/PermissionController/res/values-fr/strings.xml
+++ b/PermissionController/res/values-fr/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Plus d\'infos"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Tout autoriser"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Toujours autoriser"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Autoriser un accès limité"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Certaines photos et vidéos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Sélectionner plus"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Ne rien sélectionner de plus"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Applications"</string>
<string name="app_permissions" msgid="3369917736607944781">"Autorisations des applications"</string>
<string name="unused_apps" msgid="2058057455175955094">"Applications inutilisées"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Modifier les photos sélectionnées pour cette application"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Aucune appli inutilisée"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 appli inutilisée"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Décisions autorisations récentes"</string>
@@ -193,7 +195,7 @@
<string name="app_permission_button_deny" msgid="6016454069832050300">"Ne pas autoriser"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Position exacte"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Position approximative"</string>
- <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Utiliser la position exacte"</string>
+ <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Position exacte"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Si la position exacte est désactivée, les applis ont accès à votre position approximative"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Autorisation d\'accès à \"<xliff:g id="PERM">%1$s</xliff:g>\""</string>
<string name="app_permission_header" msgid="2951363137032603806">"Accès à \"<xliff:g id="PERM">%1$s</xliff:g>\" pour cette appli"</string>
@@ -375,7 +377,7 @@
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"en cas d\'urgence"</string>
<string name="role_home_label" msgid="3871847846649769412">"Appli d\'accueil par défaut"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Appli d\'accueil"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Applis, souvent appelées lanceurs, qui remplacent l\'écran d\'accueil de votre appareil Android et vous donnent accès au contenu de l\'appareil et à ses fonctionnalités"</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Applications, souvent appelées lanceurs d\'applications, qui remplacent l\'écran d\'accueil de votre appareil Android, et vous permettent d\'accéder au contenu de l\'appareil et à ses fonctionnalités"</string>
<string name="role_home_request_title" msgid="738136983453341081">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme votre application d\'écran d\'accueil par défaut ?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Aucune autorisation nécessaire"</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"lanceur d\'applications"</string>
@@ -456,7 +458,7 @@
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Afficher une icône dans la barre d\'état lorsque le micro est utilisé pour activer l\'assistance vocale"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos et contenus multimédias sur votre appareil ?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à vos contacts ?"</string>
- <string name="permgrouprequest_location" msgid="6990232580121067883">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de cet appareil ?"</string>
+ <string name="permgrouprequest_location" msgid="6990232580121067883">"Autoriser l\'appli &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de cet appareil ?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"L\'application n\'a accès à la position de l\'appareil que lorsqu\'elle est ouverte"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de cet appareil ?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Cette appli peut vouloir accéder à votre position en permanence, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez-la à le faire dans les paramètres."</annotation></string>
@@ -495,7 +497,7 @@
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Cette application veut accéder en permanence aux données des capteurs liées aux signes vitaux, même quand vous ne l\'utilisez pas. Pour autoriser ce changement, "<annotation id="link">"accédez aux paramètres."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux données des capteurs concernant vos signes vitaux ?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Pour autoriser cette appli à accéder aux données des capteurs corporels en permanence, même quand vous ne l\'utilisez pas, "<annotation id="link">"accédez aux paramètres"</annotation>"."</string>
- <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Continuer à autoriser <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder aux données des capteurs corporels seulement en cours d\'utilisation ?"</string>
+ <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Continuer à autoriser <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder aux données des capteurs corporels quand l\'appli est utilisée ?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à vous envoyer des notifications ?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Autorisations contrôlées"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> a accès à votre position"</string>
@@ -505,7 +507,7 @@
<string name="not_used_permissions_description" msgid="7595514824169388718">"Autorisations utilisées uniquement par les applications système."</string>
<string name="additional_permissions_label" msgid="7693557637462569046">"Autorisations supplémentaires"</string>
<string name="additional_permissions_description" msgid="2186611950890732112">"Autorisations définies par les applications."</string>
- <string name="privdash_label_camera" msgid="1426440033626198096">"Caméra"</string>
+ <string name="privdash_label_camera" msgid="1426440033626198096">"Appareil photo"</string>
<string name="privdash_label_microphone" msgid="8415035835803511693">"Micro"</string>
<string name="privdash_label_location" msgid="6882400763866489291">"Position"</string>
<string name="privdash_label_other" msgid="3710394147423236033">"Autre"</string>
@@ -535,7 +537,7 @@
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Vérifier l\'état"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Paramètres de confidentialité"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"Autres paramètres"</string>
- <string name="camera_toggle_label_qs" msgid="3880261453066157285">"Accès à la caméra"</string>
+ <string name="camera_toggle_label_qs" msgid="3880261453066157285">"Accès à l\'appareil photo"</string>
<string name="microphone_toggle_label_qs" msgid="8132912469813396552">"Accès au micro"</string>
<string name="permissions_removed_qs" msgid="8957319130625294572">"Autorisation supprimée"</string>
<string name="camera_usage_qs" msgid="4394233566086665994">"Voir l\'utilisation récente de l\'appareil photo"</string>
@@ -577,7 +579,7 @@
<string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"Accès modifié"</string>
<string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Voir l\'utilisation récente de la localisation"</string>
<string name="privacy_controls_title" msgid="7605929972256835199">"Paramètres de confidentialité"</string>
- <string name="camera_toggle_title" msgid="1251201397431837666">"Accès à la caméra"</string>
+ <string name="camera_toggle_title" msgid="1251201397431837666">"Accès à l\'appareil photo"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"Accès au micro"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Pour les applis et services"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Pour les applis et services. Si ce paramètre est désactivé, il est possible que les données du micro soient quand même partagées quand vous appelez un numéro d\'urgence."</string>
@@ -591,17 +593,17 @@
<string name="permission_rationale_data_sharing_source_title" msgid="6874604543125814316">"Origine des informations sur le partage des données"</string>
<string name="permission_rationale_data_sharing_device_manufacturer_message" msgid="7569261218145653185">"Le développeur a fourni des infos au fabricant de cet appareil concernant la manière dont cette appli partage les données. Le développeur peut mettre à jour ces infos au fil du temps."</string>
<string name="permission_rationale_data_sharing_source_message" msgid="8330794595417986883">"Le développeur a fourni des infos sur "<annotation id="link"><annotation id="install_source" example="App Store">"%1$s"</annotation></annotation>" concernant la manière dont cette appli partage les données. Le développeur peut mettre à jour ces infos au fil du temps."</string>
- <string name="permission_rationale_location_purpose_title" msgid="5115877143670012618">"L\'appli peut partager données de localisation pour :"</string>
+ <string name="permission_rationale_location_purpose_title" msgid="5115877143670012618">"L\'appli peut partager des données de localis. pour :"</string>
<string name="permission_rationale_permission_data_sharing_varies_title" msgid="9103718980919908316">"Partage des données variable"</string>
<string name="permission_rationale_data_sharing_varies_message" msgid="4224469559084489222">"La gestion des données peut varier selon la version de l\'appli, l\'utilisation que vous en faites, votre région et votre âge. "<annotation id="link">"En savoir plus sur le partage des données"</annotation></string>
<string name="permission_rationale_data_sharing_varies_message_without_link" msgid="4912763761399025094">"La gestion des données peut varier selon la version de l\'appli, l\'utilisation que vous en faites, votre région et votre âge."</string>
<string name="permission_rationale_location_settings_title" msgid="7204145004850190953">"Vos données de localisation"</string>
<string name="permission_rationale_permission_settings_message" msgid="631286040979660267">"Modifiez l\'accès de cette appli dans les "<annotation id="link">"paramètres de confidentialité"</annotation></string>
- <string name="permission_rationale_purpose_app_functionality" msgid="8397736681065841405">"Fonctionnement de l\'appli"</string>
- <string name="permission_rationale_purpose_analytics" msgid="2070800501189620712">"Données analytiques"</string>
+ <string name="permission_rationale_purpose_app_functionality" msgid="8397736681065841405">"le fonctionnement de l\'appli ;"</string>
+ <string name="permission_rationale_purpose_analytics" msgid="2070800501189620712">"les données analytiques ;"</string>
<string name="permission_rationale_purpose_developer_communications" msgid="6453047018892062374">"Communications du développeur"</string>
- <string name="permission_rationale_purpose_advertising" msgid="7156966429245180236">"Publicité ou marketing"</string>
- <string name="permission_rationale_purpose_fraud_prevention_security" msgid="4262104770357031902">"Prévention des fraudes, sécurité et conformité"</string>
+ <string name="permission_rationale_purpose_advertising" msgid="7156966429245180236">"la publicité ou marketing ;"</string>
+ <string name="permission_rationale_purpose_fraud_prevention_security" msgid="4262104770357031902">"la prévention des fraudes, la sécurité et la conformité."</string>
<string name="permission_rationale_purpose_personalization" msgid="1589973273682238708">"Personnalisation"</string>
<string name="permission_rationale_purpose_account_management" msgid="2985772421946688879">"Gestion du compte"</string>
<string name="app_permission_rationale_message" msgid="8511466916077100713">"Sécurité des données"</string>
@@ -610,7 +612,7 @@
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Mises à jour du partage des données pour la localisation"</string>
<string name="data_sharing_updates_summary" msgid="764113985772233889">"Consulter les applis qui ont modifié la manière dont elles peuvent partager vos données de localisation"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Ces applis ont modifié la manière dont elles peuvent partager vos données de localisation. Elles ne les partageaient peut-être pas auparavant ou peuvent désormais les partager à des fins de publicité ou de marketing."</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Les développeurs de ces applis ont fourni des infos concernant leurs pratiques de partage des données avec une plate-forme de téléchargement d\'applications. Ils peuvent mettre à jour ces infos au fil du temps.\n\nCes pratiques de partage des données peuvent varier selon la version de l\'appli, l\'utilisation que vous en faites, votre région et votre âge."</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Les développeurs de ces applis ont fourni des infos concernant leurs pratiques de partage des données avec une plate-forme de téléchargement d\'applications. Ils peuvent mettre à jour ces infos à l\'avenir.\n\nCes pratiques de partage des données peuvent varier selon la version de l\'appli, l\'utilisation que vous en faites, votre région et votre âge."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"En savoir plus sur le partage des données"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Vos données de localisation sont désormais partagées avec des tiers"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Vos données de localisation sont désormais partagées avec des tiers à des fins de publicité ou de marketing"</string>
diff --git a/PermissionController/res/values-gl/strings.xml b/PermissionController/res/values-gl/strings.xml
index ad7705f59..91d5090e8 100644
--- a/PermissionController/res/values-gl/strings.xml
+++ b/PermissionController/res/values-gl/strings.xml
@@ -32,8 +32,9 @@
<string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter mentres se estea utilizando a aplicación"</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter Só esta vez"</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Máis datos"</string>
- <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permitir todos"</string>
+ <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permitir acceso a todo"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Permitir todos sempre"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Permitir acceso limitado"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Seleccionar fotos e vídeos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Seleccionar máis"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Non seleccionar máis"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplicacións"</string>
<string name="app_permissions" msgid="3369917736607944781">"Permisos de aplicacións"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplicacións que non se usan"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Editar as fotos seleccionadas desta aplicación"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Non hai aplicacións sen usar"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 aplicacións que non se usan"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Decisións recentes de permisos"</string>
@@ -195,8 +197,8 @@
<string name="approximate_image_description" msgid="938803699637069884">"Localización aproximada"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usar localización precisa"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Cando a localización precisa está desactivada, as aplicacións poden acceder á túa localización aproximada"</string>
- <string name="app_permission_title" msgid="2090897901051370711">"Permiso de <xliff:g id="PERM">%1$s</xliff:g>"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"Permiso de acceso desta aplicación a: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_title" msgid="2090897901051370711">"Permiso de acceso a <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"Acceso a <xliff:g id="PERM">%1$s</xliff:g> para esta aplicación"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Ver todos os permisos de <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Ver todas as aplicacións que teñen este permiso"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Mostrar uso do micrófono do Asistente"</string>
@@ -384,7 +386,7 @@
<string name="role_call_redirection_description" msgid="6091669882014664420">"Aplicacións que che permiten reenviar chamadas saíntes a outro número de teléfono."</string>
<string name="role_call_redirection_request_title" msgid="2816244455003562925">"Queres definir <xliff:g id="APP_NAME">%1$s</xliff:g> como a túa aplicación predeterminada de desvío de chamadas?"</string>
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"Non se necesita ningún permiso"</string>
- <string name="role_call_screening_label" msgid="883935222060878724">"App de filtro de chamadas e spam predefinida"</string>
+ <string name="role_call_screening_label" msgid="883935222060878724">"Identificación de chamadas e spam"</string>
<string name="role_call_screening_short_label" msgid="2048465565063130834">"App ident. de chamadas e spam"</string>
<string name="role_call_screening_description" msgid="2349431420497468981">"Aplicacións que che permiten identificar as chamadas e bloquear o spam, as chamadas automatizadas ou os números non desexados."</string>
<string name="role_call_screening_request_title" msgid="7358309224566977290">"Queres definir <xliff:g id="APP_NAME">%1$s</xliff:g> como a túa aplicación predeterminada de identificación de chamadas e spam?"</string>
@@ -462,8 +464,8 @@
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Esta aplicación pode querer acceder á túa localización todo o tempo, incluso cando non a esteas utilizando. "<annotation id="link">"Permitir en Configuración"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Queres cambiar o acceso da aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; á localización?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Esta aplicación quere acceder á túa localización todo o tempo, incluso cando non a esteas utilizando. "<annotation id="link">"Permitir en Configuración"</annotation>"."</string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; atope dispositivos próximos, se conecte a eles e determine a súa posición relativa?"</string>
- <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; atope dispositivos próximos, se conecte a eles e determine a súa posición relativa? "<annotation id="link">"Permitir na configuración"</annotation>"."</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Permites que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; atope dispositivos próximos, se conecte a eles e determine a súa posición relativa?"</string>
+ <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Permites que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; atope dispositivos próximos, se conecte a eles e determine a súa posición relativa? "<annotation id="link">"Permitir na configuración"</annotation>"."</string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Queres que o acceso de <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> á localización cambie de aproximada a precisa?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á localización aproximada deste dispositivo?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Precisa"</string>
@@ -493,7 +495,7 @@
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; realice e xestione chamadas telefónicas?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda aos datos dos sensores sobre as túas constantes vitais?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Esta aplicación require acceso aos datos dos sensores sobre as túas constantes vitais en todo momento, mesmo cando non a usas. Para facer este cambio, "<annotation id="link">"vai á configuración."</annotation></string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda aos datos dos sensores sobre as túas constantes vitais?"</string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Permites que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda aos datos dos sensores sobre as túas constantes vitais?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Para permitir que esta aplicación acceda aos datos dos sensores corporais en todo momento, aínda que non a esteas utilizando, "<annotation id="link">"vai á configuración"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Permites que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; siga accedendo aos datos dos sensores corporais mentres estea en uso?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; che envíe notificacións?"</string>
diff --git a/PermissionController/res/values-gu/strings.xml b/PermissionController/res/values-gu/strings.xml
index 1d9126a86..c1a2a29f0 100644
--- a/PermissionController/res/values-gu/strings.xml
+++ b/PermissionController/res/values-gu/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"વધુ માહિતી"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"બધાને મંજૂરી આપો"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"હંમેશાં માટે બધાને મંજૂરી આપો"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"મર્યાદિત ઍક્સેસની મંજૂરી આપો"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ફોટા અને વીડિયો પસંદ કરો"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"વધુ ફોટા પસંદ કરો"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"વધુ પસંદ કરશો નહીં"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ઍપ"</string>
<string name="app_permissions" msgid="3369917736607944781">"ઍપની પરવાનગીઓ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ન વપરાયેલી ઍપ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"આ ઍપ માટે પસંદ કરેલા ફોટામાં ફેરફાર કરો"</string>
<string name="no_unused_apps" msgid="12809387670415295">"કોઈ બિનવપરાયેલી ઍપ નથી"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"બિનવપરાયેલી 0 ઍપ"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"પરવાનગી સંબંધિત નિર્ણયો"</string>
@@ -456,7 +458,7 @@
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"જ્યારે માઇક્રોફોનનો ઉપયોગ કરીને વૉઇસ આસિસ્ટંટ સક્રિય કરવામાં આવે, ત્યારે માઇક્રોફોનનું આઇકન સ્ટેટસ બારમાં બતાવો"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારા ડિવાઇસ પર ફોટા અને મીડિયાને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારા સંપર્કોને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
- <string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
+ <string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસના લોકેશનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યા હશો માત્ર ત્યારે જ ઍપ સ્થાનને ઍક્સેસ કરી શકશે"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"તમે આ ઍપનો ઉપયોગ કરી રહ્યાં ન હો, તો પણ તે હંમેશાં તમારા સ્થાનને ઍક્સેસ કરી શકે છે. "<annotation id="link">"સેટિંગમાંથી મંજૂરી આપો."</annotation></string>
@@ -464,8 +466,8 @@
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"તમે આ ઍપનો ઉપયોગ કરી રહ્યાં ન હો, તો પણ તે હંમેશાં તમારા સ્થાનને ઍક્સેસ કરવા માગે છે. "<annotation id="link">"સેટિંગમાંથી મંજૂરી આપો."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને નજીકના ડિવાઇસને શોધવાની, તેને કનેક્ટ કરવાની તેમજ સંબંધિત અંતર નક્કી કરવાની મંજૂરી આપીએ?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને નજીકના ડિવાઇસને શોધવાની, તેને કનેક્ટ કરવાની તેમજ સંબંધિત અંતર નક્કી કરવાની મંજૂરી આપીએ? "<annotation id="link">"સેટિંગમાં મંજૂરી આપો."</annotation></string>
- <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>નો સ્થાનનો ઍક્સેસ અંદાજિતમાંથી બદલીને ચોક્કસ કરીએ?"</string>
- <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસના અંદાજીત સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
+ <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>નો લોકેશનનો ઍક્સેસ અંદાજિતમાંથી બદલીને ચોક્કસ કરીએ?"</string>
+ <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસના અંદાજિત લોકેશનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"ચોક્કસ"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"અંદાજિત"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારા કૅલેન્ડરને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
@@ -482,7 +484,7 @@
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"તમે આ ઍપનો ઉપયોગ કરી રહ્યાં ન હો, તો પણ ઍપ હંમેશાં ઑડિયો રેકોર્ડ કરવાનું ઇચ્છી શકે છે. "<annotation id="link">"સેટિંગમાંથી મંજૂરી આપો."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; માટે માઇક્રોફોનનો ઍક્સેસ બદલીએ?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"તમે આ ઍપનો ઉપયોગ કરી રહ્યાં ન હો, તો પણ ઍપ હંમેશાં ઑડિયો રેકોર્ડ કરવા માગે છે. "<annotation id="link">"સેટિંગમાંથી મંજૂરી આપો."</annotation></string>
- <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારી શારીરિક પ્રવૃત્તિને ઍક્સેસ કરવાની મંજૂરી આપવી છે?"</string>
+ <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારી શારીરિક ઍક્ટિવિટીને ઍક્સેસ કરવાની મંજૂરી આપવી છે?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને ફોટા પાડવાની અને વીડિયો રેકોર્ડ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યા હશો, માત્ર ત્યારે જ ઍપ ફોટા લઈ શકશે અને વીડિયો રેકોર્ડ કરી શકશે"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને ફોટા લેવાની અને વીડિયો રેકોર્ડ કરવાની મંજૂરી આપીએ?"</string>
@@ -581,7 +583,7 @@
<string name="mic_toggle_title" msgid="2649991093496110162">"માઇક્રોફોનનો ઍક્સેસ"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"ઍપ અને સેવાઓ માટે"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"ઍપ અને સેવાઓ માટે. આ સેટિંગ બંધ હોવા છતાં પણ, જ્યારે તમે ઇમર્જન્સી નંબર પર કૉલ કરો ત્યારે કદાચ માઇક્રોફોનનો ડેટા શેર કરવામાં આવી શકે."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"સ્થાનનો ઍક્સેસ ધરાવતી ઍપ અને તેની સેવાઓ જુઓ"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"લોકેશનનો ઍક્સેસ ધરાવતી ઍપ અને તેની સેવાઓ જુઓ"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"ક્લિપબોર્ડનો ઍક્સેસ બતાવો"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"જ્યારે ઍપ તમે કૉપિ કરેલી ટેક્સ્ટ, છબીઓ કે અન્ય કન્ટેન્ટનો ઍક્સેસ કરે, ત્યારે મેસેજ બતાવો"</string>
<string name="show_password_title" msgid="2877269286984684659">"પાસવર્ડ બતાવો"</string>
@@ -608,7 +610,7 @@
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"લોકેશન ડેટા શેર કરવામાં આવી શકે છે"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"આ ઍપ દ્વારા જણાવવામાં આવ્યું છે કે તે ત્રીજા પક્ષો સાથે તમારો લોકેશન ડેટા શેર કરી શકે છે"</string>
<string name="data_sharing_updates_title" msgid="7996933386875213859">"લોકેશન માટે ડેટા શેરિંગ સંબંધિત અપડેટ"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"તે ઍપનું રિવ્યૂ કરો જેમણે તમારા લોકેશન ડેટાને શેર કરવાની રીત બદલી છે"</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"તે ઍપનો રિવ્યૂ કરો જેમણે તમારા લોકેશન ડેટાને શેર કરવાની રીત બદલી છે"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"આ ઍપ દ્વારા તમારા લોકેશન ડેટાને શેર કરવાની રીત બદલવામાં આવી હોઈ શકે છે. તેઓએ તેને પહેલાં શેર કર્યો ન હોય એવું બની શકે છે અથવા હવે તેને જાહેરાત અથવા માર્કેટિંગ હેતુઓ માટે શેર કરી શકે છે."</string>
<string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"આ ઍપના ડેવલપરે ડેટા શેરિંગના તેમના નિયમો વિશે ઍપ સ્ટોરને માહિતી પૂરી પાડી છે. સમય જતાં કદાચ તેઓ તેને અપડેટ કરી શકે છે.\n\nતમારી ઍપનું વર્ઝન, તેનો ઉપયોગ, ઉપયોગ કરવાનો પ્રદેશ અને તમારી ઉંમરના આધારે ડેટા શેરિંગના નિયમો કદાચ અલગ-અલગ હોઈ શકે છે."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"ડેટા શેરિંગ વિશે જાણો"</string>
diff --git a/PermissionController/res/values-hi-v34/strings.xml b/PermissionController/res/values-hi-v34/strings.xml
index 7b95fb793..b73f8b4c5 100644
--- a/PermissionController/res/values-hi-v34/strings.xml
+++ b/PermissionController/res/values-hi-v34/strings.xml
@@ -20,7 +20,7 @@
<string name="security_privacy_brand_name" msgid="7303621734258440812">"सुरक्षा और निजता"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"कंट्रोल"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
- <string name="health_connect_summary" msgid="815473513776882296">"स्वास्थ्य की जानकारी से जुड़े डेटा के लिए, ऐप्लिकेशन का ऐक्सेस मैनेज करें"</string>
+ <string name="health_connect_summary" msgid="815473513776882296">"स्वास्थ्य की जानकारी से जुड़े डेटा के लिए, ऐप्लिकेशन का ऐक्सेस मैनेज किया जा सकता है"</string>
<string name="location_settings" msgid="8863940440881290182">"जगह की जानकारी का ऐक्सेस"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"ऐप्लिकेशन और सेवाओं के लिए. इस सेटिंग के बंद होने पर भी, माइक्रोफ़ोन के डेटा को शेयर किया जा सकता है. ऐसा तब होता है, जब किसी आपातकालीन नंबर पर कॉल किया जाता है"</string>
<string name="location_settings_subtitle" msgid="6846532794702613851">"ऐप्लिकेशन और सेवाओं के लिए"</string>
diff --git a/PermissionController/res/values-hi/strings.xml b/PermissionController/res/values-hi/strings.xml
index bad54b39a..9b3eca786 100644
--- a/PermissionController/res/values-hi/strings.xml
+++ b/PermissionController/res/values-hi/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"ज़्यादा जानकारी"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"सभी के लिए अनुमति दें"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"हमेशा के लिए सभी को अनुमति दें"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"सीमित ऐक्सेस देने की अनुमति दें"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"चुनिंदा फ़ोटो और वीडियो को अनुमति दें"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"ज़्यादा फ़ोटो चुनें"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"ज़्यादा फ़ोटो और वीडियो न चुनें"</string>
@@ -49,7 +50,7 @@
<string name="permission_revoked_all" msgid="3397649017727222283">"सभी अनुमतियां बंद हैं"</string>
<string name="permission_revoked_none" msgid="9213345075484381180">"कोई अनुमति बंद नहीं है"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"अनुमति दें"</string>
- <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"हमेशा के लिए अनुमति दें"</string>
+ <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"हम समय अनुमति है"</string>
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"ऐप्लिकेशन इस्तेमाल करते समय"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"जगह की सटीक जानकारी पर सेट करें"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"जगह की अनुमानित जानकारी सेव करें"</string>
@@ -60,7 +61,8 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ऐप्लिकेशन"</string>
<string name="app_permissions" msgid="3369917736607944781">"ऐप्लिकेशन की अनुमतियां"</string>
<string name="unused_apps" msgid="2058057455175955094">"इस्तेमाल नहीं किए गए ऐप्लिकेशन"</string>
- <string name="no_unused_apps" msgid="12809387670415295">"ऐसा कोई ऐप्लिकेशन नहींं है जिसका इस्तेमाल न किया गया हो"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"इस ऐप्लिकेशन के पास किन फ़ोटो का ऐक्सेस होगा, इसमें बदलाव करें"</string>
+ <string name="no_unused_apps" msgid="12809387670415295">"ऐसा कोई ऐप्लिकेशन नहींं है जो इस्तेमाल में न हो"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"इस्तेमाल न किए जाने वाले ऐप"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"हाल ही में दी गई अनुमतियां"</string>
<string name="review_permission_decisions_view_all" msgid="90391040431566130">"हाल ही में दी गई सभी अनुमतियां देखें"</string>
@@ -194,13 +196,13 @@
<string name="precise_image_description" msgid="6349638632303619872">"सटीक जगह"</string>
<string name="approximate_image_description" msgid="938803699637069884">"अनुमानित जगह"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"जगह की सटीक जानकारी का इस्तेमाल करें"</string>
- <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"जगह की सटीक जानकारी देने वाली सुविधा बंद होने पर, ऐप्लिकेशन आपकी अनुमानित जगह की जानकारी को ऐक्सेस कर सकते हैं"</string>
+ <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"जगह की सटीक जानकारी देने वाली सुविधा बंद होने पर, ऐप्लिकेशन आपकी जगह की अनुमानित जानकारी को ऐक्सेस कर सकते हैं"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> की अनुमति"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"इस ऐप्लिकेशन के लिए, <xliff:g id="PERM">%1$s</xliff:g> ऐक्सेस करने की अनुमति चाहिए"</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"इस ऐप्लिकेशन को <xliff:g id="PERM">%1$s</xliff:g> ऐक्सेस करने की अनुमति दें"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"<xliff:g id="APP">%1$s</xliff:g> को मिली सभी अनुमतियां देखें"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"इस अनुमति वाले सभी ऐप्लिकेशन देखें"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"असिस्टेंट माइक्रोफ़ोन के इस्तेमाल से जुड़ा डेटा दिखाएं"</string>
- <string name="unused_apps_category_title" msgid="2988455616845243901">"इस्तेमाल न किए जाने वाले ऐप की सेटिंग"</string>
+ <string name="unused_apps_category_title" msgid="2988455616845243901">"इस्तेमाल न हो रहे ऐप के लिए सेटिंग"</string>
<string name="auto_revoke_label" msgid="5068393642936571656">"ऐप्लिकेशन का इस्तेमाल न होने पर अनुमतियां हटाएं"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"अनुमतियां हटाएं और जगह खाली करें"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"इस्तेमाल न होने पर ऐप गतिविधि रोकें"</string>
@@ -218,8 +220,8 @@
<string name="auto_revoked_app_summary_one" msgid="7093213590301252970">"<xliff:g id="PERMISSION_NAME">%s</xliff:g> की अनुमति हटाई गई"</string>
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"<xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g> और <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g> की अनुमतियां हटाई गईं"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> और <xliff:g id="NUMBER">%2$s</xliff:g> अन्य अनुमतियां हटाई गईं"</string>
- <string name="unused_apps_page_title" msgid="6986983535677572559">"इस्तेमाल नहीं किए गए ऐप्लिकेशन"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"अगर कुछ महीनों तक किसी ऐप्लिकेशन का इस्तेमाल नहीं किया जाता, तो:\n\n• आपके डेटा की सुरक्षा के लिए, उस ऐप्लिकेशन को दी गई अनुमतियां हटा दी जाती हैं\n• बैटरी बचाने के लिए, सूचनाएं बंद कर दी जाती हैं\n• स्टोरेज खाली करने के लिए, डिवाइस पर कुछ समय तक रहने वाली फ़ाइलें हटा दी जाती हैं\n\nदोबारा अनुमतियां देने और सूचनाएं फिर से पाने के लिए, ऐप्लिकेशन खोलें."</string>
+ <string name="unused_apps_page_title" msgid="6986983535677572559">"इस्तेमाल नहीं हो रहे ऐप्लिकेशन"</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"अगर किसी ऐप्लिकेशन को कुछ महीनों तक इस्तेमाल नहीं किया जाता, तो:\n\n• आपके डेटा की सुरक्षा के लिए, उस ऐप्लिकेशन को दी गई अनुमतियां हटा दी जाती हैं\n• बैटरी बचाने के लिए, सूचनाएं बंद कर दी जाती हैं\n• स्टोरेज खाली करने के लिए, डिवाइस पर कुछ समय तक रहने वाली फ़ाइलें हटा दी जाती हैं\n\nदोबारा अनुमतियां देने और सूचनाएं फिर से पाने के लिए, ऐप्लिकेशन खोलें."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"अगर एक महीने से किसी ऐप्लिकेशन को इस्तेमाल न किया गया हो, तो:\n\n• आपके डेटा को सुरक्षित रखने के लिए, उस ऐप्लिकेशन को दी गई अनुमतियां हटा दी जाती हैं\n• मेमोरी खाली करने के लिए, डिवाइस पर कुछ समय के लिए सेव की गई फ़ाइलें हटा दी जाती हैं\n\nअनुमतियां फिर से देने के लिए, ऐप्लिकेशन खोलें."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{पिछली बार इसे # महीने से भी पहले खोला गया था}one{पिछली बार इसे # महीने से भी पहले खोला गया था}other{पिछली बार इसे # महीने से भी पहले खोला गया था}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"ऐप्लिकेशन पिछली बार <xliff:g id="DATE">%s</xliff:g> को खोला गया था"</string>
@@ -349,21 +351,21 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"ये ऐप्लिकेशन आपकी स्क्रीन, कार्रवाइयां और इनपुट देख सकते हैं, कार्रवाई कर सकते हैं और डिसप्ले को नियंत्रित कर सकते हैं."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"डिफ़ॉल्ट डिजिटल असिस्टेंट ऐप"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"डिजिटल असिस्टेंट ऐप्लिकेशन"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"आप जो स्क्रीन देख रहे हैं, उसकी जानकारी के मुताबिक सहायक ऐप्लिकेशन आपकी मदद कर सकते हैं. कुछ ऐप्लिकेशन पर आपकी पूरी मदद करने के लिए लॉन्चर और बोलकर फ़ोन को निर्देश देना, ये दोनों सेवाएं काम करती हैं."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"स्क्रीन पर दिख रही जानकारी के आधार पर, सहायक ऐप्लिकेशन आपकी मदद कर सकते हैं. कुछ ऐप्लिकेशन हर तरह से आपकी मदद करने के लिए, लॉन्चर और फ़ोन को बोलकर निर्देश देने वाली सेवाओं, दोनों के साथ काम करते हैं."</string>
<string name="role_browser_label" msgid="2877796144554070207">"डिफ़ॉल्ट ब्राउज़र ऐप्लिकेशन"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"ब्राउज़र ऐप्लिकेशन"</string>
- <string name="role_browser_description" msgid="3465253637499842671">"ऐसे ऐप्लिकेशन जिनसे आपको इंटरनेट का ऐक्सेस मिलता है. साथ ही, टैप करने के लिए डिसप्ले लिंक मिलता है"</string>
+ <string name="role_browser_description" msgid="3465253637499842671">"ऐसे ऐप्लिकेशन जो आपको इंटरनेट तक ऐक्सेस देते हैं और आपके टैप किए गए लिंक को डिसप्ले करते हैं"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"<xliff:g id="APP_NAME">%1$s</xliff:g> को अपने डिफ़ॉल्ट ब्राउज़र ऐप्लिकेशन के तौर पर सेट करें?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"अनुमति की ज़रूरत नहीं है"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"डिफ़ॉल्ट फ़ोन ऐप्लिकेशन"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"फ़ोन ऐप्लिकेशन"</string>
- <string name="role_dialer_description" msgid="8768708633696539612">"ऐसे ऐप्लिकेशन जिनकी मदद से, अपने डिवाइस से टेलीफ़ोन कॉल किए जा सकते हैं और इनकमिंग कॉल का जवाब दिया जा सकता है"</string>
+ <string name="role_dialer_description" msgid="8768708633696539612">"ऐसे ऐप्लिकेशन जिनके ज़रिए, अपने डिवाइस से फ़ोन कॉल किए जा सकते हैं और इनकमिंग कॉल का जवाब दिया जा सकता है"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"<xliff:g id="APP_NAME">%1$s</xliff:g> को आपके डिफ़ॉल्ट फ़ोन ऐप्लिकेशन के तौर पर सेट करें?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"इस ऐप्लिकेशन को आपके कैमरे, संपर्कों, माइक्रोफ़ोन, फ़ोन, और मैसेज (एसएमएस) का ऐक्सेस मिलेगा"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"डायलर"</string>
<string name="role_sms_label" msgid="8456999857547686640">"मैसेज का डिफ़ॉल्ट ऐप्लिकेशन"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"एसएमएस ऐप्लिकेशन"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"ऐसे ऐप्लिकेशन जिनकी मदद से अपने फ़ोन नंबर का इस्तेमाल मैसेज, फ़ोटो, वीडियो के साथ ही दूसरी चीज़ें भेजने और पाने के लिए किया जा सकता है"</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"ऐसे ऐप्लिकेशन जिनकी मदद से अपने फ़ोन नंबर का इस्तेमाल मैसेज, फ़ोटो, वीडियो वगैरह भेजने और पाने के लिए किया जा सकता है"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g> को अपने डिफ़ॉल्ट मैसेज ऐप्लिकेशन के तौर पर सेट करें?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"इस ऐप्लिकेशन को आपके कैमरे, संपर्कों, फ़ाइलों और मीडिया, माइक्रोफ़ोन, फ़ोन, और मैसेज (एसएमएस) का ऐक्सेस मिलेगा"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"मैसेज, मैसेज भेजना, मैसेज करना, मैसेज भेजना या पाना."</string>
@@ -375,7 +377,7 @@
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"आपात स्थिति में"</string>
<string name="role_home_label" msgid="3871847846649769412">"डिफ़ॉल्ट होम ऐप्लिकेशन"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"होम ऐप्लिकेशन"</string>
- <string name="role_home_description" msgid="7997371519626556675">"ऐप्लिकेशन जिन्हें अक्सर लॉन्चर कहा जाता है, वे आपके Android डिवाइस पर होम स्क्रीन की जगह ले लेते हैं और आपको डिवाइस के कॉन्टेंट और सुविधाओं का ऐक्सेस देते हैं"</string>
+ <string name="role_home_description" msgid="7997371519626556675">"ऐसे ऐप्लिकेशन जो आपके Android डिवाइस पर होम स्क्रीन की जगह ले लेते हैं और आपको डिवाइस के कॉन्टेंट और सुविधाओं का ऐक्सेस देते हैं. इन्हें अक्सर लॉन्चर कहा जाता है."</string>
<string name="role_home_request_title" msgid="738136983453341081">"<xliff:g id="APP_NAME">%1$s</xliff:g> को अपने डिफ़ॉल्ट होम ऐप्लिकेशन के तौर पर सेट करना चाहते हैं?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"अनुमति की ज़रूरत नहीं है"</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"लॉन्चर"</string>
@@ -455,7 +457,7 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"असिस्टेंट ऐप्लिकेशन का माइक्रोफ़ोन चालू है या बंद, इसकी सूचना दिखाएं"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"आवाज़ से डिवाइस का इस्तेमाल करने के लिए, माइक्रोफ़ोन का इस्तेमाल करते समय स्थिति बार में आइकॉन दिखाएं"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने डिवाइस में मौजूद फ़ोटो और मीडिया ऐक्सेस करने की अनुमति देनी है?"</string>
- <string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने संपर्क देखने की अनुमति देनी है?"</string>
+ <string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को आपके संपर्कों को ऐक्सेस करने की अनुमति देनी है?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस की जगह की जानकारी ऐक्सेस करने की अनुमति देनी है?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"ऐप्लिकेशन, डिवाइस की जगह की जानकारी सिर्फ़ तभी देख पाएगा जब आप इसका इस्तेमाल कर रहे हों"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस की जगह की जानकारी ऐक्सेस करने की अनुमति देनी है?"</string>
@@ -469,12 +471,12 @@
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"सटीक जगह"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"अनुमानित जगह"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपना कैलेंडर ऐक्सेस करने की अनुमति देना है?"</string>
- <string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को एसएमएस (मैसेज) भेजने और देखने की अनुमति देनी है?"</string>
+ <string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को एसएमएस भेजने और देखने की अनुमति देनी है?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को डिवाइस पर मौजूद फ़ोटो, ऑडियो-वीडियो, और फ़ाइलें ऐक्सेस करने की अनुमति देना चाहते हैं?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस में मौजूद &lt;b&gt;फ़ोटो, वीडियो, संगीत, और ऑडियो&lt;/b&gt; का ऐक्सेस देना है?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को डिवाइस में मौजूद &lt;b&gt;फ़ोटो, वीडियो, संगीत, ऑडियो, और अन्य फ़ाइल&lt;/b&gt; का ऐक्सेस देना है?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस में मौजूद संगीत और ऑडियो ऐक्सेस करने की अनुमति देनी है?"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस में मौजूद फ़ोटो और वीडियो ऐक्सेस करने की अनुमति देनी है?"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस में मौजूद फ़ोटो और वीडियो को ऐक्सेस करने की अनुमति देनी है?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस पर मौजूद अन्य फ़ोटो और वीडियो का ऐक्सेस देना है?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को ऑडियो रिकॉर्ड करने की अनुमति देनी है?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ऐप्लिकेशन सिर्फ़ तब ही ऑडियो रिकॉर्ड कर पाएगा, जब आप ऐप्लिकेशन इस्तेमाल कर रहे हों"</string>
@@ -489,14 +491,14 @@
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"यह ऐप्लिकेशन हर समय तस्वीरें ले सकता है और वीडियो रिकॉर्ड कर सकता है. ऐप्लिकेशन इस्तेमाल न करने पर भी ऐसा हो सकता है. "<annotation id="link">"सेटिंग में जाकर अनुमति दें."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"क्या आप &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; के लिए, कैमरे के ऐक्सेस की अनुमति बदलना चाहते हैं?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"यह ऐप्लिकेशन हर समय तस्वीरें लेना और वीडियो रिकॉर्ड करना चाहता है, तब भी जब आप ऐप्लिकेशन इस्तेमाल न कर रहे हों. "<annotation id="link">"सेटिंग में जाकर अनुमति दें."</annotation></string>
- <string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने फ़ोन के काॅल लाॅग को ऐक्सेस करने की अनुमति देनी है?"</string>
+ <string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को आपका कॉल लॉग ऐक्सेस करने की अनुमति देनी है?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को फ़ोन कॉल करने और उन्हें मैनेज करने की अनुमति देनी है?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने स्वास्थ्य से जुड़ी ज़रूरी जानकारी इस्तेमाल करने की अनुमति देनी है?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को बीपी, धड़कन वगैरह की जानकारी इस्तेमाल करने की अनुमति देनी है?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"यह ऐप्लिकेशन, आपके शरीर के बारे में ज़रूरी जानकारी देने वाले सेंसर डेटा को हमेशा ऐक्सेस करने की अनुमति मांगता है. यह अनुमति उस समय के लिए भी मांगी जाती है जिस समय ऐप्लिकेशन का इस्तेमाल न हो रहा हो. यह अनुमति देने के लिए, "<annotation id="link">"सेटिंग पर जाएं."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने शरीर के बारे में जानकारी देने वाले लक्षणों के सेंसर डेटा को ऐक्सेस करने की अनुमति दें?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"इस ऐप्लिकेशन का इस्तेमाल न किए जाने पर भी, इसे बॉडी सेंसर के डेटा को हमेशा ऐक्सेस करने की अनुमति देने के लिए, "<annotation id="link">"सेटिंग पर जाएं."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"क्या इस्तेमाल के दौरान, &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को बॉडी सेंसर के डेटा का ऐक्सेस देते रहना है?"</string>
- <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को सूचनाएं भेजने की अनुमति दें?"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को सूचनाएं भेजने की अनुमति देनी है?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"कंट्रोल की गई अनुमतियां"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> के पास, डिवाइस की जगह की जानकारी का ऐक्सेस है"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"आपके संगठन ने <xliff:g id="APP_NAME">%1$s</xliff:g> को, डिवाइस की जगह की जानकारी का ऐक्सेस दिया है"</string>
@@ -516,7 +518,7 @@
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> को Android की मदद से सुरक्षित किया गया है. आपका डेटा इस डिवाइस पर प्रोसेस किया गया है. इसलिए, इस ऐप्लिकेशन की अनुमति के इस्तेमाल की जानकारी, प्राइवसी डैशबोर्ड पर नहीं दिखती."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"डिवाइस का कैमरा ब्लॉक किया गया है"</string>
<string name="blocked_microphone_title" msgid="1631517143648232585">"डिवाइस का माइक्रोफ़ोन ब्लॉक किया गया है"</string>
- <string name="blocked_location_title" msgid="2005608279812892383">"डिवाइस की जगह की जानकारी की सुविधा बंद है"</string>
+ <string name="blocked_location_title" msgid="2005608279812892383">"डिवाइस की जगह की जानकारी की सेटिंग बंद है"</string>
<string name="blocked_sensor_summary" msgid="4443707628305027375">"ऐप्लिकेशन और सेवाओं के लिए"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"जब आपातकालीन नंबर पर कॉल किया जाता है, तो माइक ब्लॉक होने के बावजूद माइक्रोफ़ोन का डेटा शेयर किया जा सकता है."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"बदलें"</string>
@@ -608,9 +610,9 @@
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"जगह की जानकारी का डेटा शेयर किया जा सकता है"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"इस ऐप्लिकेशन में बताया गया है कि यह तीसरे पक्ष के साथ जगह की जानकारी का डेटा शेयर कर सकता है"</string>
<string name="data_sharing_updates_title" msgid="7996933386875213859">"जगह की जानकारी का डेटा शेयर करने के तरीके के बारे में अपडेट"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"ऐसे ऐप्लिकेशन देखें जिन्होंने शायद आपकी जगह की जानकारी के डेटा को शेयर करने का तरीका बदल दिया है"</string>
- <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"इन ऐप्लिकेशन ने आपकी जगह की जानकारी के डेटा को शेयर करने का तरीका बदल दिया है. ऐसा हो सकता है कि ये ऐप्लिकेशन पहले जगह की जानकारी का डेटा शेयर न करते हों या फिर अब विज्ञापन या मार्केटिंग के लिए यह डेटा शेयर किया हो."</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"इन ऐप्लिकेशन के डेवलपर ने किसी ऐप स्टोर पर डेटा शेयर करने के उनके तरीकों की जानकारी बताई है. वे समय-समय पर इस जानकारी को अपडेट कर सकते हैं.\n\nडेटा शेयर करने के तरीके अलग-अलग हो सकते हैं. ये आपकी जगह, उम्र, ऐप्लिकेशन के वर्शन, और उसके इस्तेमाल के हिसाब से तय किए जाते हैं."</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"उन ऐप्लिकेशन को देखा जा सकता है जिन्होंने आपकी जगह की जानकारी का डेटा शेयर करने का अपना तरीका बदल दिया है"</string>
+ <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"इन ऐप्लिकेशन ने आपकी जगह की जानकारी के डेटा को शेयर करने का तरीका बदल दिया है. ऐसा हो सकता है कि ये ऐप्लिकेशन पहले जगह की जानकारी का डेटा शेयर न करते हों या फिर अब ये विज्ञापन या मार्केटिंग के लिए यह डेटा शेयर करें."</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"इन ऐप्लिकेशन के डेवलपरों ने, डेटा शेयर करने के अपने तरीकों के बारे में ऐप स्टोर पर जानकारी दी है. वे समय-समय पर इस जानकारी को अपडेट कर सकते हैं.\n\nडेटा शेयर करने के तरीके अलग-अलग हो सकते हैं. ये आपकी जगह, उम्र, ऐप्लिकेशन के वर्शन, और उसके इस्तेमाल के हिसाब से तय किए जाते हैं."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"डेटा शेयर करने की नीतियों के बारे में जानें"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"आपकी जगह की जानकारी का डेटा अब तीसरे पक्षों के साथ शेयर किया गया है"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"विज्ञापन देने या मार्केटिंग करने के लिए, आपकी जगह की जानकारी को अब तीसरे पक्षों के साथ शेयर किया जा रहा है"</string>
diff --git a/PermissionController/res/values-hr-v33/strings.xml b/PermissionController/res/values-hr-v33/strings.xml
index a50e58158..77c8a200f 100644
--- a/PermissionController/res/values-hr-v33/strings.xml
+++ b/PermissionController/res/values-hr-v33/strings.xml
@@ -19,7 +19,7 @@
<string name="role_dialer_request_description" msgid="6188305064871543419">"Aplikacija će vam moći slati obavijesti i dobit će pristup vašoj kameri, kontaktima, mikrofonu, telefonu i SMS-ovima"</string>
<string name="role_sms_request_description" msgid="1506966389698625395">"Aplikacija će vam moći slati obavijesti i dobit će pristup vašoj kameri, kontaktima, datotekama, mikrofonu, telefonu i SMS-ovima"</string>
<string name="permission_description_summary_storage" msgid="1917071243213043858">"Aplikacije s tim dopuštenjem imaju pristup svim datotekama na ovom uređaju"</string>
- <string name="work_policy_title" msgid="832967780713677409">"Informacije o pravilima za posao"</string>
+ <string name="work_policy_title" msgid="832967780713677409">"Informacije o pravilima za poslovne uređaje"</string>
<string name="work_policy_summary" msgid="3886113358084963931">"Postavkama upravlja vaš IT administrator"</string>
<string name="safety_center_entry_group_expand_action" msgid="5358289574941779652">"Proširi i prikaži popis"</string>
<string name="safety_center_entry_group_collapse_action" msgid="1525710152244405656">"Sažmi popis i sakrij postavke"</string>
diff --git a/PermissionController/res/values-hr/strings.xml b/PermissionController/res/values-hr/strings.xml
index b63d820f2..1eaaf6949 100644
--- a/PermissionController/res/values-hr/strings.xml
+++ b/PermissionController/res/values-hr/strings.xml
@@ -29,11 +29,12 @@
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"Aplikacija nije pronađena"</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"Nemoj dopustiti"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Nemoj dopustiti i više ne pitaj"</string>
- <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadržite \"Dok se aplikacija koristi\""</string>
+ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadržite \"Dok se aplikacija upotrebljava\""</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zadrži \"Samo ovaj put\""</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Više podataka"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Dopusti sve"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Uvijek dopusti sve"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Omogući ograničeni pristup"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Odaberite slike i videozapise"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Odaberite više"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Nemoj dopustiti"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikacije"</string>
<string name="app_permissions" msgid="3369917736607944781">"Dopuštenja za aplikacije"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nekorištene aplikacije"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Uređivanje odabranih fotografija za ovu aplikaciju"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nema nekorištenih aplikacija"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Nema nekorištenih aplikacija"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nedavne odluke o dopuštenjima"</string>
@@ -194,9 +196,9 @@
<string name="precise_image_description" msgid="6349638632303619872">"Točna lokacija"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Približna lokacija"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Koristi točnu lokaciju"</string>
- <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kad je točna lokacija isključena, aplikacije mogu pristupiti vašoj približnoj lokaciji"</string>
+ <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kad je točna lokacija isključena, aplikacije mogu pristupati vašoj približnoj lokaciji"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Dopuštenje <xliff:g id="PERM">%1$s</xliff:g>"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"Pristup aplikacije značajci \"<xliff:g id="PERM">%1$s</xliff:g>\""</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"Pristup aplikacije dopuštenju \"<xliff:g id="PERM">%1$s</xliff:g>\""</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Pogledajte sva dopuštenja aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Pogledajte sve aplikacije s tim dopuštenjem"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Prikaz korištenja mikrofona Asistenta"</string>
@@ -347,9 +349,9 @@
<string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"Aplikacije pristupačnosti (njih <xliff:g id="NUM_SERVICES">%s</xliff:g>) s potpunim pristupom vašem uređaju"</string>
<string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> može vidjeti vaš zaslon, radnje i unose, izvršavati radnje i upravljati zaslonom."</string>
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Te aplikacije mogu vidjeti vaš zaslon, radnje i unose, izvršavati radnje i upravljati zaslonom."</string>
- <string name="role_assistant_label" msgid="4727586018198208128">"Zadana apl. digital. asistenta"</string>
+ <string name="role_assistant_label" msgid="4727586018198208128">"Zadana aplikacija digitalnog asistenta"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"Apl. digitalnog asistenta"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"Aplikacije pomoćnika mogu vam pomoći na temelju podataka s prikazanog zaslona. Neke aplikacije podržavaju pokretač i usluge glasovnog unosa kako bi vam pružile integriranu pomoć."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"Aplikacije za pomoć služe se podacima koji se prikazuju na zaslonu. Neke aplikacije podržavaju pokretač i usluge glasovnog unosa kako bi vam pružile integriranu pomoć."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Zadana aplikacija preglednika"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Aplikacija preglednika"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Aplikacije koje vam omogućuju pristup internetu i prikazuju veze koje dodirnete"</string>
@@ -373,7 +375,7 @@
<string name="role_emergency_request_title" msgid="8469579020654348567">"Želite li postaviti aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> kao zadanu aplikaciju za hitne slučajeve?"</string>
<string name="role_emergency_request_description" msgid="131645948770262850">"Nije potrebno nijedno dopuštenje"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"hitni slučaj"</string>
- <string name="role_home_label" msgid="3871847846649769412">"Zadana aplik. početnog zaslona"</string>
+ <string name="role_home_label" msgid="3871847846649769412">"Zadana aplikacija početnog zaslona"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Aplikacija početnog zaslona"</string>
<string name="role_home_description" msgid="7997371519626556675">"Aplikacije, poznate i kao pokretači, koje zamjenjuju početne zaslone na vašem Android uređaju i pružaju vam pristup sadržajima i značajkama na njemu"</string>
<string name="role_home_request_title" msgid="738136983453341081">"Želite li postaviti aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> kao zadanu aplikaciju početnog zaslona?"</string>
@@ -474,7 +476,7 @@
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristup &lt;b&gt;foto/video/audiodatotekama i glazbi&lt;/b&gt; na ovom uređaju?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Dopustiti apl. &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristup &lt;b&gt;foto/video/audio i drugim datotekama te glazbi&lt;/b&gt; na uređaju?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristup glazbi i audiodatotekama na ovom uređaju?"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti pristup fotografijama i videozapisima na ovom uređaju?"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da pristupa fotografijama i videozapisima na ovom uređaju?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti pristup većem broju fotografija i videozapisa na ovom uređaju?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da snima audiozapise?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći snimati audiozapise samo dok je upotrebljavate"</string>
@@ -491,7 +493,7 @@
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Aplikacija traži dopuštenje za snimanje fotografija i videozapisa u svakom trenutku, čak i kad je ne upotrebljavate. "<annotation id="link">"Dopustite u postavkama."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da pristupa zapisnicima poziva vašeg telefona?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da upućuje telefonske pozive i upravlja njima?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa podacima senzora o vašim vitalnim znakovima?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Dopuštate li da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa podacima o vašim vitalnim znakovima?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Aplikacija želi uvijek pristupati podacima senzora o vašim vitalnim znakovima, čak i kad je ne upotrebljavate. Da biste unijeli tu promjenu, "<annotation id="link">"otvorite postavke"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa podacima senzora o vašim vitalnim znakovima?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Da biste omogućili aplikaciji da uvijek pristupa podacima s biometrijskih senzora, čak i kada je ne upotrebljavate, "<annotation id="link">"otvorite postavke."</annotation></string>
@@ -505,7 +507,7 @@
<string name="not_used_permissions_description" msgid="7595514824169388718">"Dopuštenja koja upotrebljavaju samo aplikacije sustava."</string>
<string name="additional_permissions_label" msgid="7693557637462569046">"Dodatna dopuštenja"</string>
<string name="additional_permissions_description" msgid="2186611950890732112">"Dopuštenja koja definiraju aplikacije."</string>
- <string name="privdash_label_camera" msgid="1426440033626198096">"Fotoaparat"</string>
+ <string name="privdash_label_camera" msgid="1426440033626198096">"Kamera"</string>
<string name="privdash_label_microphone" msgid="8415035835803511693">"Mikrofon"</string>
<string name="privdash_label_location" msgid="6882400763866489291">"Lokacija"</string>
<string name="privdash_label_other" msgid="3710394147423236033">"Drugo"</string>
@@ -521,7 +523,7 @@
<string name="blocked_mic_summary" msgid="8960466941528458347">"Podaci mikrofona i dalje se mogu dijeliti kad pozovete broj hitne službe."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Promijeni"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sigurnost i privatnost"</string>
- <string name="safety_center_rescan_button" msgid="4517514567809409596">"Skeniranje uređaja"</string>
+ <string name="safety_center_rescan_button" msgid="4517514567809409596">"Skeniraj uređaj"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Odbaci"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"Želite li odbaciti ovo upozorenje?"</string>
<string name="safety_center_issue_card_dismiss_confirmation_message" msgid="3775418736671093563">"Pregledajte postavke sigurnosti i privatnosti za dodatnu zaštitu"</string>
@@ -532,10 +534,10 @@
<string name="security_settings" msgid="3808106921175271317">"Sigurnosne postavke"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"Dopuštenja"</string>
<string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Sigurnost i privatnost"</string>
- <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Provjera statusa"</string>
+ <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Provjerite status"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Vaše kontrole privatnosti"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"Više postavki"</string>
- <string name="camera_toggle_label_qs" msgid="3880261453066157285">"Pristup fotoaparatu"</string>
+ <string name="camera_toggle_label_qs" msgid="3880261453066157285">"Pristup kameri"</string>
<string name="microphone_toggle_label_qs" msgid="8132912469813396552">"Pristup mikrofonu"</string>
<string name="permissions_removed_qs" msgid="8957319130625294572">"Dopuštenje uklonjeno"</string>
<string name="camera_usage_qs" msgid="4394233566086665994">"Prikaži nedavnu upotrebu fotoaparata"</string>
@@ -577,7 +579,7 @@
<string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"Promijenjen je pristup"</string>
<string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Prikaži nedavnu upotrebu lokacije"</string>
<string name="privacy_controls_title" msgid="7605929972256835199">"Kontrole privatnosti"</string>
- <string name="camera_toggle_title" msgid="1251201397431837666">"Pristup fotoaparatu"</string>
+ <string name="camera_toggle_title" msgid="1251201397431837666">"Pristup kameri"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"Pristup mikrofonu"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Za aplikacije i usluge"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Za aplikacije i usluge. Ako je ta postavka isključena, podaci mikrofona i dalje se mogu dijeliti kad nazovete broj hitne službe."</string>
diff --git a/PermissionController/res/values-hu/strings.xml b/PermissionController/res/values-hu/strings.xml
index 12ad687a6..fb014cfa9 100644
--- a/PermissionController/res/values-hu/strings.xml
+++ b/PermissionController/res/values-hu/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Bővebben"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Összes engedélyezése"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Összes engedélyezése mindig"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Korlátozott hozzáférés engedélyezése"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Fotók és videók kijelölése"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Több kijelölése"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Nem jelölök ki többet"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Alkalmazások"</string>
<string name="app_permissions" msgid="3369917736607944781">"Alkalmazásengedélyek"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nem használt alkalmazások"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Az alkalmazáshoz kiválasztott fotók módosítása"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nincsenek nem használt appok"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 nem használt alkalmazás"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Friss engedélyezési döntések"</string>
diff --git a/PermissionController/res/values-hy-v34/strings.xml b/PermissionController/res/values-hy-v34/strings.xml
index fb8997eb1..10455cbd3 100644
--- a/PermissionController/res/values-hy-v34/strings.xml
+++ b/PermissionController/res/values-hy-v34/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"Անվտանգություն և գաղտնիություն"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"Ապահովություն և գաղտնիություն"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Կառավարման տարրեր"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"Կառավարեք առողջության մասին տվյալների հասանելիությունը հավելվածների համար"</string>
diff --git a/PermissionController/res/values-hy/strings.xml b/PermissionController/res/values-hy/strings.xml
index 73742eb53..ccc248b53 100644
--- a/PermissionController/res/values-hy/strings.xml
+++ b/PermissionController/res/values-hy/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Մանրամասն"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Թույլատրել բոլորը"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Միշտ թույլատրել բոլորը"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Տրամադրել սահմանափակ հասանելիություն"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Կոնկրետ լուսանկարներ և տեսանյութեր"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Ընտրել այլ տարրեր"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Չընտրել այլ լուսանկարներ"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Հավելվածներ"</string>
<string name="app_permissions" msgid="3369917736607944781">"Հավելվածների թույլտվություններ"</string>
<string name="unused_apps" msgid="2058057455175955094">"Չօգտագործվող հավելվածներ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Փոխել այս հավելվածի համար ընտրված լուսանկարները"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Չօգտագործվող հավելվածներ չկան"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Չօգտագործվող հավելվածներ չկան"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Թույլտվության որոշումներ"</string>
@@ -196,7 +198,7 @@
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Օգտագործել ճշգրիտ տեղադրությունը"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Եթե ճշգրիտ տեղադրությունն անջատված է, հավելվածին հասանելի է ձեր մոտավոր գտնվելու վայրը"</string>
<string name="app_permission_title" msgid="2090897901051370711">"«<xliff:g id="PERM">%1$s</xliff:g>» թույլտվություն"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g> օգտագործելու թույլտվություն այս հավելվածի համար"</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g>ն օգտագործելու թույլտվություն այս հավելվածի համար"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Տեսնել «<xliff:g id="APP">%1$s</xliff:g>» հավելվածի բոլոր թույլտվությունները"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Դիտել այս թույլտվությունն ունեցող հավելվածների ցանկը"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Ցույց տալ օգնականի կողմից խոսափողի օգտագործման վիճակագրությունը"</string>
@@ -456,33 +458,33 @@
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Ցույց տալ պատկերակը կարգավիճակի գոտում, երբ ձայնային օգնականի ակտիվացման համար օգտագործվում է խոսափողը"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել սարքի լուսանկարներն ու մուլտիմեդիա ֆայլերը"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր կոնտակտները"</string>
- <string name="permgrouprequest_location" msgid="6990232580121067883">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի տեղադրության տվյալները"</string>
+ <string name="permgrouprequest_location" msgid="6990232580121067883">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի տեղադրության տվյալները"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Տեղադրության տվյալները հասանելի կլինեն հավելվածին, միայն երբ այն օգտագործելիս լինեք"</string>
- <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի տեղադրության տվյալները"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի տեղադրության տվյալները"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Հավելվածին անհրաժեշտ է ձեր գտնվելու վայրը հետագծելու թույլտվություն, նույնիսկ երբ դուք չեք օգտվում դրանից։ "<annotation id="link">"Թույլտվությունը տրամադրեք այստեղ"</annotation>"։"</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Փոխե՞լ տեղադրության մասին տվյալների հասանելիությունը &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածի համար։"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Հավելվածին անհրաժեշտ է ձեր գտնվելու վայրը հետագծելու թույլտվություն, նույնիսկ երբ դուք չեք օգտվում դրանից։ "<annotation id="link">"Թույլտվությունը տրամադրեք այստեղ"</annotation>"։"</string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին գտնել մոտակա սարքերը, միանալ դրանց և որոշել դրանց հարաբերական դիրքավորումը։"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին գտնել մոտակա սարքերը, միանալ դրանց և որոշել դրանց հարաբերական դիրքը։"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին գտնել մոտակա սարքերը, միանալ դրանց և որոշել դրանց հարաբերական դիրքավորումը։ Թույլատրելու համար անցեք "<annotation id="link">"կարգավորումներ։"</annotation></string>
- <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"«<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>» հավելվածի տեղորոշումը փոխե՞լ մոտավորից ճգշրիտի"</string>
- <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի մոտավոր տեղադրությունը"</string>
+ <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> հավելվածի տեղորոշումը փոխե՞լ մոտավորից ճգշրիտի"</string>
+ <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի մոտավոր տեղադրությունը"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Ճշգրիտ"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Մոտավոր"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր օրացույցը:"</string>
- <string name="permgrouprequest_sms" msgid="5672063688745420991">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ուղարկել և դիտել SMS հաղորդագրություններ:"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր օրացույցը"</string>
+ <string name="permgrouprequest_sms" msgid="5672063688745420991">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ուղարկել և դիտել SMS հաղորդագրություններ"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել սարքում պահված լուսանկարները, մուլտիմեդիան և ֆայլերը"</string>
- <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի &lt;b&gt;լուսանկարները, տեսանյութերը, երաժշտությունը և աուդիո ֆայլերը&lt;/b&gt;"</string>
- <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի &lt;b&gt;նկարները, երգերը, տեսանյութերը, աուդիո ֆայլերը և մյուս ֆայլերը&lt;/b&gt;"</string>
- <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի երաժշտությունը և մյուս աուդիո ֆայլերը"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի լուսանկարներն ու տեսանյութերը"</string>
- <string name="permgrouprequest_more_photos" msgid="128933814654231321">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի լուսանկարներն ու տեսանյութերը"</string>
+ <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի &lt;b&gt;լուսանկարները, տեսանյութերը, երաժշտությունը և աուդիո ֆայլերը&lt;/b&gt;"</string>
+ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի &lt;b&gt;նկարները, երգերը, տեսանյութերը, աուդիո ֆայլերը և մյուս ֆայլերը&lt;/b&gt;"</string>
+ <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի երաժշտությունը և մյուս աուդիո ֆայլերը"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի լուսանկարներն ու տեսանյութերը"</string>
+ <string name="permgrouprequest_more_photos" msgid="128933814654231321">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի լուսանկարներն ու տեսանյութերը"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ձայնագրել"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Այս հավելվածը կկարողանա ձայնագրություններ անել միայն, երբ այն օգտագործելիս լինեք"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ձայնագրություններ անել։"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Այս հավելվածը հավանաբար կուզենա ձայնագրություններ անել նույնիսկ այն ժամանակ, երբ չեք օգտվում դրանից։ "<annotation id="link">"Թույլտվությունը տրամադրեք այստեղ։"</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Փոխե՞լ խոսափողի հասանելիության կարգավորումները &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածի համար։"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Այս հավելվածն ուզւոմ է ձայնագրություններ անել նույնիսկ այն ժամանակ, երբ չեք օգտվում դրանից։ "<annotation id="link">"Թույլտվությունը տրամադրեք այստեղ։"</annotation></string>
- <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր ֆիզիկական ակտիվության տվյալները"</string>
+ <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր ֆիզիկական ակտիվության տվյալները"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին լուսանկարել և տեսագրել"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Այս հավելվածը կկարողանա լուսանկարել և տեսագրել միայն, երբ այն օգտագործելիս լինեք"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին լուսանկարել և տեսագրել։"</string>
@@ -493,7 +495,7 @@
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին կատարել հեռախոսազանգեր և կառավարել դրանք"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին սենսորից ստանալ ձեր կենսագործունեության հիմնական տվյալները:"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Հավելվածին անհրաժեշտ է մարմնի սենսորների տվյալների հասանելիություն, նույնիսկ երբ չեք օգտվում դրանից։ Այս փոփոխությունը կատարելու համար "<annotation id="link">"անցեք կարգավորումներ"</annotation>"։"</string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ձեր կենսագործունեության տվյալները ստանալ սենսորներից"</string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ձեր կենսագործունեության տվյալները ստանալ սենսորներից"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Եթե ուզում եք, որ այս հավելվածին հասանելի լինեն մարմնի սենսորների տվյալները, նույնիսկ երբ չեք օգտվում հավելվածից, "<annotation id="link">"փոխեք կարգավորումները"</annotation>"։"</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Մարմնի սենսորների տվյալները հասանելի դարձնե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին, միայն երբ այն օգտագործվում է"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ծանուցումներ ուղարկել ձեզ"</string>
@@ -520,7 +522,7 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Հավելվածների և ծառայությունների համար"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Խոսափողի տվյալները կարող են դեռ փոխանցվել, երբ զանգեք արտակարգ իրավիճակների որևէ հեռախոսահամարի։"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Փոխել"</string>
- <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Անվտանգություն և գաղտնիություն"</string>
+ <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Ապահովություն և գաղտնիություն"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Սկանավորել սարքը"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Փակել"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"Փակե՞լ այս ծանուցումը"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"Անվտանգության և գաղտնիության կարգավիճակը։ <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>։ <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"Անվտանգության կարգավորումներ"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"Թույլտվություններ"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Անվտանգություն և գաղտնիություն"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Ապահովություն և գաղտնիություն"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Ստուգեք կարգավիճակը"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Գաղտնիության կարգավորումներ"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"Այլ կարգավորումներ"</string>
diff --git a/PermissionController/res/values-in-v33/strings.xml b/PermissionController/res/values-in-v33/strings.xml
index b74e806df..90be234ea 100644
--- a/PermissionController/res/values-in-v33/strings.xml
+++ b/PermissionController/res/values-in-v33/strings.xml
@@ -27,7 +27,7 @@
<string name="safety_center_entry_group_with_actions_needed_content_description" msgid="2708884606775932657">"Daftar. <xliff:g id="ENTRY_TITLE">%1$s</xliff:g>. Diperlukan tindakan. <xliff:g id="ENTRY_SUMMARY">%2$s</xliff:g>"</string>
<string name="safety_center_entry_group_item_content_description" msgid="7348298582877249787">"Item daftar. <xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string>
<string name="safety_center_entry_content_description" msgid="3639565652938224321">"<xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string>
- <string name="safety_center_more_issues_card_title" msgid="7425844746197493312">"Peringatan lainnya"</string>
+ <string name="safety_center_more_issues_card_title" msgid="7425844746197493312">"Peringatan lain"</string>
<string name="safety_center_dismissed_issues_card_title" msgid="2340129842725145733">"Peringatan yang ditutup"</string>
<string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Luaskan untuk melihat satu peringatan lain}other{Luaskan untuk melihat # peringatan lain}}"</string>
<string name="safety_center_issue_card_prefix_content_description" msgid="1447445289637043544">"Peringatan. <xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-in/strings.xml b/PermissionController/res/values-in/strings.xml
index 456ce7b82..07071881f 100644
--- a/PermissionController/res/values-in/strings.xml
+++ b/PermissionController/res/values-in/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Info lengkap"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Izinkan semua"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Selalu izinkan semua"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Izinkan akses terbatas"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Pilih foto dan video"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Pilih lainnya"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Jangan pilih lainnya"</string>
@@ -49,17 +50,18 @@
<string name="permission_revoked_all" msgid="3397649017727222283">"semua dinonaktifkan"</string>
<string name="permission_revoked_none" msgid="9213345075484381180">"tidak ada yang dinonaktifkan"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"Izinkan"</string>
- <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"Izinkan sepanjang waktu"</string>
+ <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"Selalu izinkan"</string>
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Saat aplikasi digunakan"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"Ubah ke lokasi presisi"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"Tetap gunakan perkiraan"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"Hanya kali ini"</string>
- <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"Izinkan sepanjang waktu"</string>
+ <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"Selalu izinkan"</string>
<string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"Izinkan pengelolaan semua file"</string>
<string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"Izinkan akses ke file media"</string>
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikasi"</string>
<string name="app_permissions" msgid="3369917736607944781">"Izin aplikasi"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplikasi tidak digunakan"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Edit foto yang dipilih untuk aplikasi ini"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Tak ada aplikasi tidak dipakai"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 aplikasi tidak digunakan"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Keputusan izin terbaru"</string>
@@ -70,7 +72,7 @@
<string name="denied_permission_decision" msgid="5308961501779563781">"Anda menolak akses <xliff:g id="APP_NAME">%1$s</xliff:g> ke <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Hari ini}=1{1 hari lalu}other{# hari lalu}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Nonaktifkan aplikasi"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"Jika Anda menonaktifkan aplikasi ini, Android dan aplikasi lain mungkin tidak berfungsi lagi sesuai harapan. Perlu diingat, Anda tidak dapat menghapus aplikasi yang disertakan oleh pabrikan di perangkat Anda. Namun, Anda dapat menonaktifkannya, yang berarti mematikan dan menyembunyikan aplikasi tersebut di perangkat Anda."</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"Jika aplikasi ini dinonaktifkan, Android dan aplikasi lain mungkin tidak berfungsi normal. Perlu diingat, Anda tidak dapat menghapus aplikasi ini karena merupakan bawaan perangkat. Namun, Anda dapat menonaktifkannya, yang berarti mematikan dan menyembunyikannya di perangkat."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Pengelola izin"</string>
<string name="never_ask_again" msgid="4728762438198560329">"Jangan tanya lagi"</string>
<string name="no_permissions" msgid="3881676756371148563">"Tidak ada izin"</string>
@@ -107,7 +109,7 @@
<!-- no translation found for background_access_chooser_dialog_choices:0 (1351721623256561996) -->
<!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) -->
<!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) -->
- <string name="permission_access_always" msgid="1474641821883823446">"Izinkan sepanjang waktu"</string>
+ <string name="permission_access_always" msgid="1474641821883823446">"Selalu izinkan"</string>
<string name="permission_access_only_foreground" msgid="7801170728159326195">"Izinkan saat aplikasi digunakan"</string>
<string name="permission_access_never" msgid="4647014230217936900">"Jangan izinkan"</string>
<string name="loading" msgid="4789365003890741082">"Memuat…"</string>
@@ -127,7 +129,7 @@
<string name="permission_usage_title" msgid="1568233336351734538">"Dasbor privasi"</string>
<string name="auto_permission_usage_summary" msgid="7335667266743337075">"Lihat aplikasi yang baru saja menggunakan izin"</string>
<string name="permission_group_usage_title" msgid="2595013198075285173">"Penggunaan <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string>
- <string name="perm_usage_adv_info_title" msgid="3357831829538873708">"Lihat izin lainnya"</string>
+ <string name="perm_usage_adv_info_title" msgid="3357831829538873708">"Lihat izin lain"</string>
<string name="perm_usage_adv_info_summary_2_items" msgid="3702175198750127822">"<xliff:g id="PERMGROUP_0">%1$s</xliff:g>, <xliff:g id="PERMGROUP_1">%2$s</xliff:g>"</string>
<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>, dan <xliff:g id="NUM">%3$s</xliff:g> lainnya"</string>
<string name="permission_group_usage_subtitle_24h" msgid="5120155996322114181">"Linimasa penggunaan <xliff:g id="PERMGROUP">%1$s</xliff:g> Anda oleh aplikasi dalam 24 jam terakhir"</string>
@@ -186,8 +188,8 @@
<string name="app_permission_button_allow" msgid="5808039516494774647">"Izinkan"</string>
<string name="app_permission_button_allow_all_files" msgid="1792232272599018825">"Izinkan pengelolaan semua file"</string>
<string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Izinkan akses hanya ke media"</string>
- <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Izinkan sepanjang waktu"</string>
- <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Izinkan hanya saat aplikasi digunakan"</string>
+ <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Selalu izinkan"</string>
+ <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Izinkan saat aplikasi digunakan"</string>
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Selalu izinkan semua"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Selalu tanya"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Jangan izinkan"</string>
@@ -357,12 +359,12 @@
<string name="role_browser_request_description" msgid="5888803407905985941">"Tidak ada izin yang diperlukan"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"Aplikasi telepon default"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"Aplikasi telepon"</string>
- <string name="role_dialer_description" msgid="8768708633696539612">"Aplikasi yang memungkinkan Anda melakukan dan menerima panggilan telepon di perangkat"</string>
+ <string name="role_dialer_description" msgid="8768708633696539612">"Aplikasi yang memungkinkan Anda memulai dan menerima panggilan telepon di perangkat"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"Tetapkan <xliff:g id="APP_NAME">%1$s</xliff:g> sebagai aplikasi telepon default Anda?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"Aplikasi ini akan diberi akses ke Kamera, Kontak, Mikrofon, Telepon, dan SMS Anda"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"telepon"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Aplikasi SMS default"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"Aplikasi SMS"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"aplikasi SMS"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Aplikasi yang memungkinkan Anda menggunakan nomor telepon untuk mengirim dan menerima SMS, foto, video, dan sebagainya"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Tetapkan <xliff:g id="APP_NAME">%1$s</xliff:g> sebagai aplikasi SMS default Anda?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Aplikasi ini akan diberi akses ke Kamera, Kontak, File dan media, Mikrofon, Telepon, dan SMS Anda"</string>
@@ -607,7 +609,7 @@
<string name="app_permission_rationale_message" msgid="8511466916077100713">"Keamanan data"</string>
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"Data lokasi mungkin dibagikan"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Aplikasi ini menyatakan bahwa aplikasi mungkin membagikan data lokasi Anda kepada pihak ketiga"</string>
- <string name="data_sharing_updates_title" msgid="7996933386875213859">"Pembaruan berbagi data untuk lokasi"</string>
+ <string name="data_sharing_updates_title" msgid="7996933386875213859">"Pembaruan berbagi data lokasi"</string>
<string name="data_sharing_updates_summary" msgid="764113985772233889">"Tinjau aplikasi yang mengubah caranya berbagi data lokasi Anda"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Aplikasi ini telah mengubah caranya berbagi data lokasi Anda. Aplikasi mungkin sebelumnya tidak membagikan data, atau mungkin kini membagikan data untuk tujuan iklan atau pemasaran."</string>
<string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Developer aplikasi ini memberikan info tentang praktik berbagi data mereka kepada app store. Developer dapat memperbaruinya dari waktu ke waktu.\n\nPraktik berbagi data mungkin berbeda-beda berdasarkan versi aplikasi, penggunaan, wilayah, dan usia Anda."</string>
diff --git a/PermissionController/res/values-is/strings.xml b/PermissionController/res/values-is/strings.xml
index ab698d6ab..22936ea30 100644
--- a/PermissionController/res/values-is/strings.xml
+++ b/PermissionController/res/values-is/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Upplýsingar"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Leyfa allt"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Alltaf leyfa allt"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Leyfa takmarkaðan aðgang"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Velja myndir og myndskeið"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Velja meira"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Ekki velja fleiri"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Forrit"</string>
<string name="app_permissions" msgid="3369917736607944781">"Heimildir forrits"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ónotuð forrit"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Breyttu myndavali fyrir þetta forrit"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Engin ónotuð forrit"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ónotuð forrit"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nýlegar heimildaákvarðanir"</string>
@@ -294,7 +296,7 @@
<string name="accessibility_remove_access_button_label" msgid="44145801526711640">"Fjarlægja aðgang"</string>
<string name="accessibility_show_all_apps_button_label" msgid="960067249326392280">"Skoða forrit með fullan aðgang"</string>
<string name="accessibility_remove_access_success_label" msgid="4380995302917014670">"Aðgangur fjarlægður"</string>
- <string name="safety_center_notification_app_label" msgid="2457720616141926534">"Android kerfið"</string>
+ <string name="safety_center_notification_app_label" msgid="2457720616141926534">"Android-kerfið"</string>
<string name="auto_revoke_after_notification_title" msgid="5417761027669887431">"Heimildir forrits fjarlægðar vegna persónuverndar"</string>
<string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"<xliff:g id="APP_NAME">%s</xliff:g> hefur ekki verið notað í nokkra mánuði. Ýttu til að skoða."</string>
<string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> og eitt forrit í viðbót hafa ekki verið notuð í nokkra mánuði. Ýttu til að skoða."</string>
@@ -375,7 +377,7 @@
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"neyð"</string>
<string name="role_home_label" msgid="3871847846649769412">"Sjálfgefið heimaforrit"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Heimaforrit"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Forrit sem koma í stað heimaskjásins í Android tækinu þínu, stundum nefnd ræsiforrit, sem gefa þér aðgang að efni og eiginleikum í tækinu."</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Forrit sem koma í stað heimaskjásins í Android-tækinu þínu, stundum nefnd ræsiforrit, sem gefa þér aðgang að efni og eiginleikum í tækinu."</string>
<string name="role_home_request_title" msgid="738136983453341081">"Velja <xliff:g id="APP_NAME">%1$s</xliff:g> sem sjálfgefið heimaforrit?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Engra heimilda krafist"</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"ræsiforrit"</string>
diff --git a/PermissionController/res/values-it-v33/strings.xml b/PermissionController/res/values-it-v33/strings.xml
index 66187fa98..9bea693c9 100644
--- a/PermissionController/res/values-it-v33/strings.xml
+++ b/PermissionController/res/values-it-v33/strings.xml
@@ -29,7 +29,7 @@
<string name="safety_center_entry_content_description" msgid="3639565652938224321">"<xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string>
<string name="safety_center_more_issues_card_title" msgid="7425844746197493312">"Altri avvisi"</string>
<string name="safety_center_dismissed_issues_card_title" msgid="2340129842725145733">"Avvisi chiusi"</string>
- <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Espandi e visualizza un altro avviso}many{Espandi e visualizza altri # avvisi}other{Espandi e visualizza altri # avvisi}}"</string>
+ <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Espandi e visualizza un altro avviso}many{Espandi e visualizza altri # di avvisi}other{Espandi e visualizza altri # avvisi}}"</string>
<string name="safety_center_issue_card_prefix_content_description" msgid="1447445289637043544">"Avviso. <xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>"</string>
<string name="safety_center_resolved_issue_fallback" msgid="8548932070610766651">"Azione completata"</string>
<string name="safety_center_qs_status_summary" msgid="5193925895830451177">"Controlla le impostazioni che possono proteggere meglio il tuo dispositivo"</string>
diff --git a/PermissionController/res/values-it/strings.xml b/PermissionController/res/values-it/strings.xml
index 4d653a874..2418050b7 100644
--- a/PermissionController/res/values-it/strings.xml
+++ b/PermissionController/res/values-it/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Altre info"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Consenti tutto"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Consenti sempre tutto"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Consenti accesso limitato"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Seleziona foto e video"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Seleziona più messaggi"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Non selezionare altro"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"App"</string>
<string name="app_permissions" msgid="3369917736607944781">"Autorizzazioni app"</string>
<string name="unused_apps" msgid="2058057455175955094">"App inutilizzate"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Modifica le foto selezionate per questa app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nessuna app inutilizzata"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 app inutilizzate"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Decisioni recenti per le autorizzazioni"</string>
@@ -68,7 +70,7 @@
<string name="auto_permission_manager_summary" msgid="9157438376234301354">"Gestisci l\'accesso ai dati relativi a calendario, registri chiamate e altro"</string>
<string name="granted_permission_decision" msgid="7824827491551861365">"Hai consentito l\'accesso dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g> a: <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="denied_permission_decision" msgid="5308961501779563781">"Hai negato l\'accesso dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g> a: <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
- <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Oggi}=1{1 giorno fa}many{# giorni fa}other{# giorni fa}}"</string>
+ <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Oggi}=1{1 giorno fa}many{# di giorni fa}other{# giorni fa}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Disattiva app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Se disattivi questa app, Android e altre app potrebbero non funzionare più come previsto. Tieni presente che non puoi eliminare questa app perché è preinstallata sul tuo dispositivo. Puoi scegliere di disattivare l\'app e nasconderla sul tuo dispositivo."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gestione autorizzazioni"</string>
@@ -139,15 +141,15 @@
<string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string>
<string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string>
<string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string>
- <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# giorno}many{# giorni}other{# giorni}}"</string>
- <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ora}many{# ore}other{# ore}}"</string>
+ <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# giorno}many{# di giorni}other{# giorni}}"</string>
+ <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ora}many{# di ore}other{# ore}}"</string>
<string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}many{# min}other{# min}}"</string>
<string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sec}many{# sec}other{# sec}}"</string>
<string name="permission_usage_any_permission" msgid="6358023078298106997">"Qualsiasi autorizzazione"</string>
<string name="permission_usage_any_time" msgid="3802087027301631827">"Qualsiasi data"</string>
- <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Ultimo giorno}many{Ultimi # giorni}other{Ultimi # giorni}}"</string>
+ <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Ultimo giorno}many{Ultimi # di giorni}other{Ultimi # giorni}}"</string>
<string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Ultima ora}many{Ultime # ore}other{Ultime # ore}}"</string>
- <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Ultimo minuto}many{Ultimi # minuti}other{Ultimi # minuti}}"</string>
+ <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Ultimo minuto}many{Ultimi # di minuti}other{Ultimi # minuti}}"</string>
<string name="no_permission_usages" msgid="9119517454177289331">"Autorizzazioni non usate"</string>
<string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Accesso più recente in qualsiasi momento"</string>
<string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Accesso più recente negli ultimi 7 giorni"</string>
@@ -161,8 +163,8 @@
<string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Uso autorizzazioni nell\'ultima ora"</string>
<string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Uso autorizzazioni negli ultimi 15 minuti"</string>
<string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Uso autorizzazioni nell\'ultimo minuto"</string>
- <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Autorizzazione non utilizzata nell\'ultimo giorno}many{Autorizzazione non utilizzata negli ultimi # giorni}other{Autorizzazione non utilizzata negli ultimi # giorni}}"</string>
- <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Autorizzazione non utilizzata nell\'ultima ora}many{Autorizzazione non utilizzata nelle ultime # ore}other{Autorizzazione non utilizzata nelle ultime # ore}}"</string>
+ <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Autorizzazione non utilizzata nell\'ultimo giorno}many{Autorizzazione non utilizzata in # di giorni}other{Autorizzazione non utilizzata negli ultimi # giorni}}"</string>
+ <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Autorizzazione non utilizzata nell\'ultima ora}many{Autorizzazione non utilizzata in # di ore}other{Autorizzazione non utilizzata nelle ultime # ore}}"</string>
<string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Autorizzazione usata da 1 app}many{Autorizzazione usata da # app}other{Autorizzazione usata da # app}}"</string>
<string name="permission_usage_view_details" msgid="6675335735468752787">"Mostra tutto nella Dashboard"</string>
<string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrata per: <xliff:g id="PERM">%1$s</xliff:g>"</string>
@@ -193,7 +195,7 @@
<string name="app_permission_button_deny" msgid="6016454069832050300">"Non consentire"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Posizione esatta"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Posizione approssimativa"</string>
- <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Utilizza posizione esatta"</string>
+ <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usa posizione esatta"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Quando la posizione esatta non è attiva, le app possono accedere alla tua posizione approssimativa"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Autorizzazione <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Accesso a <xliff:g id="PERM">%1$s</xliff:g> per questa app"</string>
@@ -221,7 +223,7 @@
<string name="unused_apps_page_title" msgid="6986983535677572559">"App inutilizzate"</string>
<string name="unused_apps_page_summary" msgid="1867593913217272155">"Se un\'app non viene usata per alcuni mesi:\n\n• Vengono rimosse le autorizzazioni per proteggere i tuoi dati\n• Vengono interrotte le notifiche per risparmiare batteria\n• Vengono rimossi i file temporanei per liberare spazio\n\nApri l\'app per consentire di nuovo autorizzazioni e notifiche."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Se un\'app non viene usata per un mese:\n\n• Vengono rimosse le autorizzazioni per proteggere i tuoi dati\n• Vengono rimossi i file temporanei per liberare spazio\n\nApri l\'app per consentire di nuovo le autorizzazioni."</string>
- <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Aperte l\'ultima volta più di # mese fa}many{Aperte l\'ultima volta più di # mesi fa}other{Aperte l\'ultima volta più di # mesi fa}}"</string>
+ <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Aperte l\'ultima volta più di # mese fa}many{Aperte l\'ultima volta più di # di mesi fa}other{Aperte l\'ultima volta più di # mesi fa}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"Ultimo utilizzo dell\'app: <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="last_opened_summary_short" msgid="1646067226191176825">"Ultimo utilizzo: <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"Se consenti la gestione di tutti i file, questa app potrà accedere, modificare ed eliminare tutti i file salvati in spazi di archiviazione comuni su questo dispositivo o su qualsiasi dispositivo di archiviazione connesso. L\'app potrà accedere ai file senza informarti."</string>
@@ -247,23 +249,23 @@
<string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Rifiutata/Accesso mai effettuato"</string>
<string name="allowed_header" msgid="7769277978004790414">"Autorizzata"</string>
<string name="allowed_always_header" msgid="6455903312589013545">"Autorizzazione sempre concessa"</string>
- <string name="allowed_foreground_header" msgid="6845655788447833353">"Autorizzazione concessa solo durante l\'uso"</string>
+ <string name="allowed_foreground_header" msgid="6845655788447833353">"Autorizzazione concessa solo se in uso"</string>
<string name="allowed_storage_scoped" msgid="5383645873719086975">"Autorizzate solo per contenuti multimediali"</string>
<string name="allowed_storage_full" msgid="5356699280625693530">"Autorizzate per gestire tutti i file"</string>
<string name="ask_header" msgid="2633816846459944376">"Chiedi ogni volta"</string>
<string name="denied_header" msgid="903209608358177654">"Non autorizzata"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Visualizza altre app che possono accedere a tutti i file"</string>
- <string name="days" msgid="609563020985571393">"{count,plural, =1{1 giorno}many{# giorni}other{# giorni}}"</string>
- <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ora}many{# ore}other{# ore}}"</string>
- <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# minuti}other{# minuti}}"</string>
- <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# secondo}many{# secondi}other{# secondi}}"</string>
+ <string name="days" msgid="609563020985571393">"{count,plural, =1{1 giorno}many{# di giorni}other{# giorni}}"</string>
+ <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ora}many{# di ore}other{# ore}}"</string>
+ <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# di minuti}other{# minuti}}"</string>
+ <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# secondo}many{# di secondi}other{# secondi}}"</string>
<string name="permission_reminders" msgid="6528257957664832636">"Promemoria autorizzazione"</string>
<string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 app inutilizzata"</string>
<string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> app inutilizzate"</string>
<string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Autorizzazioni rimosse per proteggere la tua privacy. Tocca per controllare"</string>
<string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Autorizzazioni rimosse per le app inutilizzate"</string>
<string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Alcune app non sono state usate per alcuni mesi. Tocca per controllare."</string>
- <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# app inutilizzata}many{# app inutilizzate}other{# app inutilizzate}}"</string>
+ <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# app inutilizzata}many{# di app inutilizzate}other{# app inutilizzate}}"</string>
<string name="unused_apps_notification_content" msgid="9195026773244581246">"Le autorizzazioni e i file temporanei sono stati rimossi; le notifiche sono state interrotte. Tocca per controllare."</string>
<string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Controlla le app di cui sono state rimosse le autorizzazioni"</string>
<string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Per le app che non usi da un po\' di tempo sono stati rimossi i file temporanei e le autorizzazioni e sono state interrotte le notifiche."</string>
@@ -272,7 +274,7 @@
<string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Durante la guida, hai consentito all\'app <xliff:g id="APP">%1$s</xliff:g> di accedere a <xliff:g id="PERMISSION">%2$s</xliff:g>"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Durante la guida, hai consentito all\'app <xliff:g id="APP">%1$s</xliff:g> di accedere a <xliff:g id="PERMISSION_1">%2$s</xliff:g> e <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"Durante la guida, hai concesso <xliff:g id="COUNT">%1$d</xliff:g> autorizzazioni all\'app <xliff:g id="APP">%2$s</xliff:g>"</string>
- <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_0">%1$s</xliff:g> e a # altra app}many{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_1">%1$s</xliff:g> e ad altre # app}other{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_1">%1$s</xliff:g> e ad altre # app}}"</string>
+ <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_0">%1$s</xliff:g> e a # altra app}many{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_1">%1$s</xliff:g> e a # di altre app}other{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_1">%1$s</xliff:g> e ad altre # app}}"</string>
<string name="go_to_settings" msgid="1053735612211228335">"Vai a Impostazioni"</string>
<string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Alcune app non sono state usate per alcuni mesi"</string>
<string name="permissions_removed_category_title" msgid="1064754271178447643">"Autorizzazioni rimosse"</string>
@@ -462,8 +464,8 @@
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Questa app potrebbe voler accedere sempre alla tua posizione, anche quando non la usi. "<annotation id="link">"Consenti l\'accesso nelle impostazioni"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Vuoi cambiare l\'accesso alla posizione per l\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Questa app vorrebbe accedere sempre alla tua posizione, anche quando non la usi. "<annotation id="link">"Consenti l\'accesso nelle impostazioni"</annotation>"."</string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di trovare e connettersi ai dispositivi vicini, e di stabilirne la posizione relativa?"</string>
- <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di trovare e connettersi ai dispositivi vicini, e di stabilirne la posizione relativa? "<annotation id="link">"Consenti nelle impostazioni."</annotation></string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di trovare i dispositivi vicini, connettersi e stabilirne la posizione relativa?"</string>
+ <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di trovare i dispositivi vicini, connettersi e stabilirne la posizione relativa? "<annotation id="link">"Consenti nelle impostazioni."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Modificare l\'accesso alla posizione di <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> da approssimativa a esatta?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione approssimativa di questo dispositivo?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Esatta"</string>
@@ -489,9 +491,9 @@
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Questa app potrebbe voler scattare foto e registrare video sempre, anche quando non la usi. "<annotation id="link">"Consenti l\'accesso nelle impostazioni."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Vuoi cambiare l\'accesso alla fotocamera per l\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Questa app vorrebbe scattare foto e registrare video sempre, anche quando non la usi. "<annotation id="link">"Consenti l\'accesso nelle impostazioni."</annotation></string>
- <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai registri chiamate del tuo telefono?"</string>
+ <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai registri chiamate del tuo smartphone?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di effettuare e gestire telefonate?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai dati dei sensori relativi ai parametri vitali?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai dati dei sensori sui parametri vitali?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Questa app vuole accedere sempre ai dati dei sensori relativi ai tuoi parametri vitali, anche quando non la usi. Per apportare questa modifica, "<annotation id="link">"vai alle impostazioni."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai dati dei sensori relativi ai parametri vitali?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Per consentire a questa app di accedere sempre ai dati dei sensori del corpo, anche quando non la usi, "<annotation id="link">"vai alle impostazioni"</annotation>"."</string>
@@ -614,7 +616,7 @@
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Scopri di più sulla condivisione dei dati"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Ora i tuoi dati sulla posizione vengono condivisi con terze parti"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Ora i tuoi dati sulla posizione vengono condivisi con terze parti per scopi pubblicitari o di marketing"</string>
- <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Aggiornamenti nelle ultime 24 ore}=1{Aggiornamenti nelle ultime 24 ore}many{Aggiornamenti negli ultimi # giorni}other{Aggiornamenti negli ultimi # giorni}}"</string>
+ <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{App aggiornate nelle ultime 24 ore}=1{App aggiornate nelle ultime 24 ore}many{App aggiornate negli ultimi # di giorni}other{App aggiornate negli ultimi # giorni}}"</string>
<string name="no_updates_at_this_time" msgid="9031085635689982935">"Nessun aggiornamento al momento"</string>
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Aggiornamenti relativi alla condivisione dei dati"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Alcune app hanno cambiato la modalità di condivisione dei tuoi dati sulla posizione"</string>
diff --git a/PermissionController/res/values-iw/strings.xml b/PermissionController/res/values-iw/strings.xml
index f261a0552..46288cc93 100644
--- a/PermissionController/res/values-iw/strings.xml
+++ b/PermissionController/res/values-iw/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"מידע נוסף"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"אישור של הכול"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"אישור של הכול תמיד"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"הרשאה לגישה מוגבלת"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"בחירת תמונות וסרטונים"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"בחירת תמונות נוספות"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"לא לבחור עוד"</string>
@@ -51,15 +52,16 @@
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"יש אישור"</string>
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"כן, כל הזמן"</string>
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"בזמן השימוש באפליקציה"</string>
- <string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"שינוי למיקום המדויק"</string>
- <string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"שימוש במיקום המשוער"</string>
+ <string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"לשנות למיקום מדויק"</string>
+ <string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"להמשיך עם מיקום משוער"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"רק הפעם"</string>
- <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"להתיר כל הזמן"</string>
+ <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"כן, כל הזמן"</string>
<string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"אישור לניהול כל הקבצים"</string>
<string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"אישור גישה לקבצי מדיה"</string>
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"אפליקציות"</string>
<string name="app_permissions" msgid="3369917736607944781">"הרשאות לאפליקציות"</string>
<string name="unused_apps" msgid="2058057455175955094">"אפליקציות שמזמן לא השתמשת בהן"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"עריכת הרשימה של התמונות שנבחרו לאפליקציה הזו"</string>
<string name="no_unused_apps" msgid="12809387670415295">"אין אפליקציות שאינן בשימוש"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"אין אפליקציות שאינן בשימוש"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"ההחלטות האחרונות לגבי הרשאות"</string>
@@ -107,7 +109,7 @@
<!-- no translation found for background_access_chooser_dialog_choices:0 (1351721623256561996) -->
<!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) -->
<!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) -->
- <string name="permission_access_always" msgid="1474641821883823446">"להתיר כל הזמן"</string>
+ <string name="permission_access_always" msgid="1474641821883823446">"כן, כל הזמן"</string>
<string name="permission_access_only_foreground" msgid="7801170728159326195">"רק כשהאפליקציה בשימוש"</string>
<string name="permission_access_never" msgid="4647014230217936900">"אין אישור"</string>
<string name="loading" msgid="4789365003890741082">"בטעינה…"</string>
@@ -196,15 +198,15 @@
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"שימוש במיקום מדויק"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"כשהמיקום המדויק מושבת, אפליקציות יכולות לגשת למיקום המשוער של המכשיר"</string>
<string name="app_permission_title" msgid="2090897901051370711">"הרשאה: <xliff:g id="PERM">%1$s</xliff:g>"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"לאפליקציה תהיה גישה אל: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"לאפליקציה תהיה גישה ל<xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"הצגת כל ההרשאות של \'<xliff:g id="APP">%1$s</xliff:g>\'"</string>
- <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"הצגת כל האפליקציות עם ההרשאה הזו"</string>
+ <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"אני רוצה לראות את כל האפליקציות עם ההרשאה הזו"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"‏השימוש במיקרופון של Assistant"</string>
<string name="unused_apps_category_title" msgid="2988455616845243901">"הגדרות של אפליקציות שמזמן לא השתמשת בהן"</string>
<string name="auto_revoke_label" msgid="5068393642936571656">"הסרת ההרשאות כשלא בשימוש"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"הסרת הרשאות ופינוי מקום אחסון"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"השהיית הפעילות באפליקציה אם אין בה שימוש"</string>
- <string name="unused_apps_summary" msgid="8839466950318403115">"ההרשאות של האפליקציה יוסרו, הקבצים הזמניים יימחקו ותופסק קבלת ההתראות ממנה"</string>
+ <string name="unused_apps_summary" msgid="8839466950318403115">"הסרת ההרשאות, מחיקה של הקבצים הזמניים, הפסקה של קבלת ההתראות"</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_revoked_apps_page_summary" msgid="6594753657893756536">"כדי להגן על הנתונים שלך, הוסרו הרשאות מאפליקציות שלא השתמשת בהן במשך מספר חודשים."</string>
diff --git a/PermissionController/res/values-ja-v33/strings.xml b/PermissionController/res/values-ja-v33/strings.xml
index e735bb0f7..0669bd15d 100644
--- a/PermissionController/res/values-ja-v33/strings.xml
+++ b/PermissionController/res/values-ja-v33/strings.xml
@@ -19,7 +19,7 @@
<string name="role_dialer_request_description" msgid="6188305064871543419">"このアプリは、通知の送信と、カメラ、連絡先、マイク、電話、SMS へのアクセスが可能になります"</string>
<string name="role_sms_request_description" msgid="1506966389698625395">"このアプリは、通知の送信と、カメラ、連絡先、ファイル、マイク、電話、SMS へのアクセスが可能になります"</string>
<string name="permission_description_summary_storage" msgid="1917071243213043858">"この権限があるアプリは、このデバイス内のすべてのファイルにアクセスできます"</string>
- <string name="work_policy_title" msgid="832967780713677409">"仕事に関するポリシーの情報"</string>
+ <string name="work_policy_title" msgid="832967780713677409">"仕事用ポリシーの情報"</string>
<string name="work_policy_summary" msgid="3886113358084963931">"IT 管理者によって管理されている設定"</string>
<string name="safety_center_entry_group_expand_action" msgid="5358289574941779652">"リストを開いて表示する"</string>
<string name="safety_center_entry_group_collapse_action" msgid="1525710152244405656">"リストを閉じて設定を非表示にする"</string>
diff --git a/PermissionController/res/values-ja/strings.xml b/PermissionController/res/values-ja/strings.xml
index 06ee27a95..0a3d59a35 100644
--- a/PermissionController/res/values-ja/strings.xml
+++ b/PermissionController/res/values-ja/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"詳細"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"すべて許可"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"常にすべて許可"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"制限付きでアクセスを許可する"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"写真と動画を選択"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"さらに選択"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"他を選択しない"</string>
@@ -55,11 +56,12 @@
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"おおよその位置情報を保持"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"今回のみ"</string>
<string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"常に許可"</string>
- <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"すべてのファイルの管理を許可"</string>
- <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"メディア ファイルへのアクセスを許可"</string>
+ <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"すべての管理を許可"</string>
+ <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"アクセスのみ許可"</string>
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"アプリ"</string>
<string name="app_permissions" msgid="3369917736607944781">"アプリの権限"</string>
<string name="unused_apps" msgid="2058057455175955094">"使用されていないアプリ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"このアプリがアクセスできる写真を変更します"</string>
<string name="no_unused_apps" msgid="12809387670415295">"使用されていないアプリはありません"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"使用していないアプリ: 0 個"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"最近の権限の許可 / 拒否"</string>
@@ -84,7 +86,7 @@
<string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g> 個のアプリを許可"</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> 件のアプリを許可"</string>
<string name="menu_show_system" msgid="4254021607027872504">"システムアプリを表示"</string>
- <string name="menu_hide_system" msgid="3855390843744028465">"システムアプリを表示しない"</string>
+ <string name="menu_hide_system" msgid="3855390843744028465">"システムアプリを非表示"</string>
<string name="menu_show_7_days_data" msgid="8979611198508523706">"過去 7 日間を表示"</string>
<string name="menu_show_24_hours_data" msgid="8228054833323380780">"過去 24 時間を表示"</string>
<string name="manage_permission" msgid="2895385393037061964">"権限の管理"</string>
@@ -185,7 +187,7 @@
<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_media_only" msgid="2834282724426046154">"メディアへのアクセスのみを許可"</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>
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"常にすべて許可"</string>
@@ -248,7 +250,7 @@
<string name="allowed_header" msgid="7769277978004790414">"許可"</string>
<string name="allowed_always_header" msgid="6455903312589013545">"常に許可"</string>
<string name="allowed_foreground_header" msgid="6845655788447833353">"使用中のみ許可"</string>
- <string name="allowed_storage_scoped" msgid="5383645873719086975">"メディアへのアクセスのみが許可されたアプリ"</string>
+ <string name="allowed_storage_scoped" msgid="5383645873719086975">"アクセスのみ許可"</string>
<string name="allowed_storage_full" msgid="5356699280625693530">"すべてのファイルの管理が許可されたアプリ"</string>
<string name="ask_header" msgid="2633816846459944376">"毎回確認する"</string>
<string name="denied_header" msgid="903209608358177654">"許可しない"</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"これらのアプリでは、画面、アクション、入力の表示、アクションの実行、ディスプレイの操作を行えます。"</string>
<string name="role_assistant_label" msgid="4727586018198208128">"デフォルトのデジタル アシスタント アプリ"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"デジタル アシスタント アプリ"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"アシストアプリは、表示している画面の情報に基づいてアシスタントを提供します。一部のアプリはランチャーと音声入力サービスの両方に対応しており、統合されたアシスタントを提供します。"</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"アシストアプリは、表示している画面の情報に基づいてサポートを提供します。一部のアプリはランチャーと音声入力サービスの両方に対応しており、統合されたサポートを提供します。"</string>
<string name="role_browser_label" msgid="2877796144554070207">"デフォルトのブラウザアプリ"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"ブラウザアプリ"</string>
<string name="role_browser_description" msgid="3465253637499842671">"インターネットにアクセスするためのアプリです。タップしたリンクは、このアプリで開きます。"</string>
@@ -455,7 +457,7 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"アシスタントのトリガー検出を表示"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"マイクを使って音声アシスタントを有効にする場合にステータスバーにアイコンを表示する"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"デバイス内の写真やメディアへのアクセスを「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
- <string name="permgrouprequest_contacts" msgid="8391550064551053695">"連絡先へのアクセスを「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
+ <string name="permgrouprequest_contacts" msgid="8391550064551053695">"連絡先へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"このデバイスの位置情報へのアクセスを「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"このアプリは、ユーザーがアプリを使用している間のみ位置情報にアクセスできます"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"このデバイスの位置情報へのアクセスを「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
@@ -483,18 +485,18 @@
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に対するマイクへのアクセス許可を変更しますか?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"このアプリは、いつでも(ユーザーがアプリを使用していない場合でも)音声を録音できる権限を求めています。"<annotation id="link">"[設定] で許可してください。"</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"身体活動データへのアクセスを「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"写真と動画の撮影を「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"写真と動画の撮影を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"アプリは、ユーザーがアプリを使用している場合のみ写真や動画を撮影できます"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"写真と動画の撮影を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"このアプリは、いつでも(ユーザーがアプリを使用していない場合でも)写真や動画を撮影する可能性があります。"<annotation id="link">"[設定] で許可してください。"</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に対するカメラへのアクセス許可を変更しますか?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"このアプリは、いつでも(ユーザーがアプリを使用していない場合でも)写真や動画を撮影できる権限を求めています。"<annotation id="link">"[設定] で許可してください。"</annotation></string>
- <string name="permgrouprequest_calllog" msgid="2065327180175371397">"通話履歴へのアクセスを「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
- <string name="permgrouprequest_phone" msgid="1829234136997316752">"電話の発信と管理を「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"バイタルサインに関するセンサーデータへのアクセスを「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"このアプリは、未使用時も含め、常にバイタルサインに関するセンサーデータへのアクセスを試みます。これを変更するには、"<annotation id="link">"設定に移動"</annotation>"してください。"</string>
+ <string name="permgrouprequest_calllog" msgid="2065327180175371397">"通話履歴へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_phone" msgid="1829234136997316752">"電話の発信と管理を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; にバイタルサインに関するセンサーデータへのアクセスを許可しますか?"</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"このアプリは、非使用時も含め、常にバイタルサインに関するセンサーデータへのアクセスを試みます。これを変更するには、"<annotation id="link">"設定に移動"</annotation>"してください。"</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"バイタルサインに関するセンサーデータへのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
- <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"アプリの未使用時も、常にボディセンサー データにアクセスすることをこのアプリに許可するには、"<annotation id="link">"設定に移動"</annotation>"してください。"</string>
+ <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"アプリの非使用時も、常にボディセンサー データにアクセスすることをこのアプリに許可するには、"<annotation id="link">"設定に移動"</annotation>"してください。"</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"ボディセンサー データへのアクセスを、引き続きアプリの使用時のみ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"通知の送信を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"権限は管理されています"</string>
@@ -583,7 +585,7 @@
<string name="mic_toggle_description" msgid="9163104307990677157">"アプリとサービスによるアクセス。この設定が OFF の場合でも、緊急通報番号に発信したときは、マイクのデータが共有されることがあります。"</string>
<string name="location_settings_subtitle" msgid="2328360561197430695">"位置情報にアクセスできるアプリとサービスを確認"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"クリップボードへのアクセスを通知"</string>
- <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"クリップボードにコピーしたテキストや画像などにアプリがアクセスすると、メッセージが表示されます"</string>
+ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"クリップボードにコピーしたテキストや画像などにアプリがアクセスした際に、メッセージを表示します"</string>
<string name="show_password_title" msgid="2877269286984684659">"パスワードを表示"</string>
<string name="show_password_summary" msgid="1110166488865981610">"入力した文字を一瞬だけ表示します"</string>
<string name="permission_rationale_message_location" msgid="2153841534298068414">"このアプリは、位置情報をサードパーティと共有する可能性があります"</string>
diff --git a/PermissionController/res/values-ka/strings.xml b/PermissionController/res/values-ka/strings.xml
index 15f434a33..b143ef3ce 100644
--- a/PermissionController/res/values-ka/strings.xml
+++ b/PermissionController/res/values-ka/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"მეტი ინფორმაცია"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"ყველას დაშვება"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"ყოველთვის ყველას დაშვება"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"შეზღუდული წვდომის დაშვება"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ფოტოებისა და ვიდეოების არჩევა"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"მეტის არჩევა"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"მეტის აღარ არჩევა"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"აპები"</string>
<string name="app_permissions" msgid="3369917736607944781">"აპის ნებართვები"</string>
<string name="unused_apps" msgid="2058057455175955094">"გამოუყენებელი აპები"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"დაარედაქტირეთ არჩეული ფოტოები ამ აპისთვის"</string>
<string name="no_unused_apps" msgid="12809387670415295">"გამოუყენებელი აპები არ არის"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 გამოუყენებელი აპი"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"ნებართვის შეს. უახ. გადაწყვეტ."</string>
@@ -200,7 +202,7 @@
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"ყველა ნებართვის ნახვა: <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"ამ ნებართვის მქონე ყველა აპის ნახვა"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"ასისტენტის მიკროფონის გამოყენების ჩვენება"</string>
- <string name="unused_apps_category_title" msgid="2988455616845243901">"აპის გამოუყენებელი პარამეტრები"</string>
+ <string name="unused_apps_category_title" msgid="2988455616845243901">"გამოუყენებელი აპის პარამეტრები"</string>
<string name="auto_revoke_label" msgid="5068393642936571656">"ნებართვების ამოშლა აპის გამოუყენებლობის შემთხვევაში."</string>
<string name="unused_apps_label" msgid="2595428768404901064">"ნებართვების ამოშლა და მეხსიერების გათავისუფლება"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"დაპაუზდეს აპში აქტივობა, თუ არ იყენებენ"</string>
diff --git a/PermissionController/res/values-kk/strings.xml b/PermissionController/res/values-kk/strings.xml
index 72320b1d0..657cf13a4 100644
--- a/PermissionController/res/values-kk/strings.xml
+++ b/PermissionController/res/values-kk/strings.xml
@@ -22,18 +22,19 @@
<string name="cancel" msgid="8943320028373963831">"Бас тарту"</string>
<string name="back" msgid="6249950659061523680">"Артқа"</string>
<string name="available" msgid="6007778121920339498">"Қолжетімді"</string>
- <string name="blocked" msgid="9195547604866033708">"Бөгелген"</string>
+ <string name="blocked" msgid="9195547604866033708">"Блокталған"</string>
<string name="on" msgid="280241003226755921">"Қосулы"</string>
<string name="off" msgid="1438489226422866263">"Өшіру"</string>
<string name="uninstall_or_disable" msgid="4496612999740858933">"Жою немесе өшіру"</string>
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"Қолданба табылмады"</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"Рұқсат бермеу"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Рұқсат бермеу және қайта сұрамау"</string>
- <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"Қолданба пайдаланылатын кезде\" түймесін басып тұрыңыз"</string>
+ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"Қолданбаны пайдаланған кездегі\" рұқсатты қалдыру"</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Тек осы жолы\" күйінде қалдыру"</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Толығырақ"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Бәріне рұқсат беру"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Әрдайым бәріне рұқсат беру"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Шектеулі рұқсат беру"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Фотосуреттер мен бейнелерді таңдау"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Тағы таңдау"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Тағы басқасын таңдамау"</string>
@@ -49,7 +50,7 @@
<string name="permission_revoked_all" msgid="3397649017727222283">"барлық рұқсаттар өшірілді"</string>
<string name="permission_revoked_none" msgid="9213345075484381180">"ешқандай рұқсат өшірілмеді"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"Рұқсат беру"</string>
- <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"Әрқашан рұқсат беру"</string>
+ <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"Біржола рұқсат ету"</string>
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Қолданбаны пайдаланған кезде"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"Нақты локацияға ауысу"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"Болжалды локацияны қалдыру"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Қолданбалар"</string>
<string name="app_permissions" msgid="3369917736607944781">"Қолданба рұқсаттары"</string>
<string name="unused_apps" msgid="2058057455175955094">"Пайдаланылмайтын қолданбалар"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Осы қолданба пайдалана алатын фотосуреттер тізімін өзгертеді."</string>
<string name="no_unused_apps" msgid="12809387670415295">"Пайдаланылмайтын қолданбалар жоқ"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 пайдаланылмайтын қолданба бар"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Рұқсат беру әрекеттері"</string>
@@ -70,7 +72,7 @@
<string name="denied_permission_decision" msgid="5308961501779563781">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасына <xliff:g id="PERMISSION_NAME">%2$s</xliff:g> рұқсатын пайдалануға тыйым салдыңыз."</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Бүгін}=1{1 күн бұрын}other{# күн бұрын}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Қолданбаны өшіру"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"Бұл қолданбаны өшірсеңіз, Android жүйесі мен басқа қолданбалар тиісінше жұмыс істемеуі мүмкін. Бұл қолданба құрылғыға алдын ала орнатылатындықтан, оны жою мүмкін еместігін ескеріңіз. Өшіріп қою арқылы сіз бұл қолданбаны ажыратып, оны құрылғыда жасыра аласыз."</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"Бұл қолданбаны өшірсеңіз, Android жүйесі мен басқа қолданбалар тиісінше жұмыс істемеуі мүмкін. Құрылғыға алдын ала орнатылатындықтан, оны жою мүмкін еместігін ескеріңіз. Өшірілген қолданбаның жұмысы тоқтап, ол құрылғыда жасырын тұрады."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Рұқсат менеджері"</string>
<string name="never_ask_again" msgid="4728762438198560329">"Қайта сұралмасын"</string>
<string name="no_permissions" msgid="3881676756371148563">"Рұқсат жоқ"</string>
@@ -108,7 +110,7 @@
<!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) -->
<!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) -->
<string name="permission_access_always" msgid="1474641821883823446">"Біржола рұқсат ету"</string>
- <string name="permission_access_only_foreground" msgid="7801170728159326195">"Қолданбаны пайдаланғанда ғана"</string>
+ <string name="permission_access_only_foreground" msgid="7801170728159326195">"Қолданбаны пайдаланғанда ғана рұқсат ету"</string>
<string name="permission_access_never" msgid="4647014230217936900">"Рұқсат бермеу"</string>
<string name="loading" msgid="4789365003890741082">"Жүктелуде…"</string>
<string name="all_permissions" msgid="6911125611996872522">"Барлық рұқсаттар"</string>
@@ -186,7 +188,7 @@
<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_media_only" msgid="2834282724426046154">"Тек медиафайлдарды пайдалануға рұқсат беру"</string>
- <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Біржола рұқсат беру"</string>
+ <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Біржола рұқсат ету"</string>
<string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Қолданбаны пайдаланғанда ғана рұқсат беру"</string>
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Барлығына әрқашан рұқсат ету"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Әрдайым сұрау"</string>
@@ -386,7 +388,7 @@
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"Ешқандай рұқсат қажет емес."</string>
<string name="role_call_screening_label" msgid="883935222060878724">"Нөмір мен спамды анықтаудың әдепкі қолданбасы"</string>
<string name="role_call_screening_short_label" msgid="2048465565063130834">"Нөмірді автоматты анықтау қызметі, спамды анықтау қолданбасы"</string>
- <string name="role_call_screening_description" msgid="2349431420497468981">"Қоңырау шалушыны анықтауға, спамды және автоматты қоңырауларды және қажетсіз нөмірлерді бөгеуге мүмкіндік беретін қолданбалар"</string>
+ <string name="role_call_screening_description" msgid="2349431420497468981">"Қоңырау шалушыны анықтауға, спамды және автоматты қоңырауларды және қажетсіз нөмірлерді блоктауға мүмкіндік беретін қолданбалар"</string>
<string name="role_call_screening_request_title" msgid="7358309224566977290">"<xliff:g id="APP_NAME">%1$s</xliff:g> нөмірді автоматты анықтау қызметі мен спам анықтау қолданбасы болып орнатылсын ба?"</string>
<string name="role_call_screening_request_description" msgid="7338511921032446006">"Ешқандай рұқсат қажет емес."</string>
<string name="role_automotive_navigation_label" msgid="2701890757955474751">"Әдепкі навигация қолданбасы"</string>
@@ -464,7 +466,7 @@
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Қолданбаны пайдаланбасаңыз да, ол үнемі геодеректеріңізді пайдаланғысы келеді. "<annotation id="link">"Параметрлерден рұқсат беріңіз."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына маңайдағы құрылғыларды табуға, олармен байланысуға және олардың орналасуын анықтауға рұқсат берілсін бе?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына маңайдағы құрылғыларды табуға, олармен байланысуға және олардың орналасуын анықтауға рұқсат берілсін бе? "<annotation id="link">"Параметрлерден рұқсат бере аласыз."</annotation></string>
- <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> қолданбасының болжалды емес, нақты локацияны пайдалануына рұқсат беру керек пе?"</string>
+ <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> қолданбасының болжалды емес, нақты локацияны пайдалануына рұқсат берілсін бе?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына осы құрылғының болжалды орнын пайдалануға рұқсат берілсін бе?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Нақты"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Болжалды"</string>
diff --git a/PermissionController/res/values-km-v34/strings.xml b/PermissionController/res/values-km-v34/strings.xml
index ccf92d186..dd8426131 100644
--- a/PermissionController/res/values-km-v34/strings.xml
+++ b/PermissionController/res/values-km-v34/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"សុវត្ថិភាព និងឯកជនភាព"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"សន្តិសុខ និងឯកជនភាព"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"ការគ្រប់គ្រង"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"គ្រប់គ្រងសិទ្ធិរបស់កម្មវិធី​ក្នុងការចូលប្រើទិន្នន័យសុខភាព"</string>
diff --git a/PermissionController/res/values-km/strings.xml b/PermissionController/res/values-km/strings.xml
index a50189176..974efc58c 100644
--- a/PermissionController/res/values-km/strings.xml
+++ b/PermissionController/res/values-km/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"ព័ត៌មាន​បន្ថែម"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"អនុញ្ញាតទាំងអស់"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"អនុញ្ញាតទាំងអស់ជានិច្ច"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"អនុញ្ញាតឱ្យចូលប្រើដោយមានកម្រិត"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ជ្រើសរើស​រូបថត និង​វីដេអូ"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"ជ្រើសរើស​ច្រើន​ទៀត"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"កុំជ្រើសរើសទៀត"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"កម្មវិធី"</string>
<string name="app_permissions" msgid="3369917736607944781">"ការអនុញ្ញាតកម្មវិធី"</string>
<string name="unused_apps" msgid="2058057455175955094">"កម្មវិធី​ដែលមិន​ប្រើ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"កែរូបថតដែលបានជ្រើសរើសសម្រាប់កម្មវិធីនេះ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"មិនមានកម្មវិធីដែលមិនបានប្រើទេ"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"កម្មវិធី​ដែលមិន​ប្រើ 0"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"ការសម្រេចថ្មីៗលើការអនុញ្ញាត"</string>
@@ -492,8 +494,8 @@
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"អនុញ្ញាត​ឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូល​ប្រើ​កំណត់ហេតុ​ហៅទូរសព្ទ​របស់អ្នក?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; គ្រប់គ្រង និង​ធ្វើការហៅទូរសព្ទ?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើ​ទិន្នន័យឧបករណ៍ចាប់សញ្ញាអំពីស្ថានភាពសុខភាពរបស់អ្នក?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"កម្មវិធីនេះចង់ចូលប្រើទិន្នន័យឧបករណ៍ចាប់សញ្ញាអំពីស្ថានភាពសុខភាពរបស់អ្នកគ្រប់ពេល ទោះបីជានៅពេលអ្នកមិនកំពុងប្រើកម្មវិធីនេះក៏ដោយ។ ដើម្បីធ្វើការផ្លាស់ប្ដូរនេះ សូម"<annotation id="link">"ចូលទៅកាន់ការកំណត់។"</annotation></string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទិន្នន័យ​ឧបករណ៍ចាប់សញ្ញា​អំពីស្ថានភាពសុខភាព​របស់អ្នកឬ?"</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"កម្មវិធីនេះចង់ចូលប្រើទិន្នន័យឧបករណ៍ចាប់សញ្ញាអំពីសញ្ញាជីវិតរបស់អ្នកគ្រប់ពេល ទោះបីជានៅពេលអ្នកមិនកំពុងប្រើកម្មវិធីនេះក៏ដោយ។ ដើម្បីធ្វើការផ្លាស់ប្ដូរនេះ សូម"<annotation id="link">"ចូលទៅកាន់ការកំណត់។"</annotation></string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទិន្នន័យ​ឧបករណ៍ចាប់សញ្ញា​អំពីសញ្ញាជីវិត​របស់អ្នកឬ?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"ដើម្បីអនុញ្ញាតឱ្យកម្មវិធីនេះចូលប្រើទិន្នន័យឧបករណ៍ចាប់សញ្ញារាងកាយគ្រប់ពេល ទោះបីជានៅពេលអ្នកមិនកំពុងប្រើកម្មវិធីនេះក៏ដោយ "<annotation id="link">"សូមចូលទៅកាន់ការកំណត់។"</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"បន្តអនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទិន្នន័យឧបករណ៍ចាប់សញ្ញារាងកាយ ខណៈពេលកំពុងប្រើកម្មវិធីឬ?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ផ្ញើ​ការជូនដំណឹង​ឱ្យអ្នកឬ?"</string>
@@ -520,7 +522,7 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"សម្រាប់កម្មវិធី និងសេវាកម្ម"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"ទិន្នន័យមីក្រូហ្វូន​នៅតែអាចត្រូវ​បានចែករំលែក​ដដែល នៅពេលអ្នក​ហៅទៅលេខសង្គ្រោះបន្ទាន់។"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"ផ្លាស់ប្ដូរ"</string>
- <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"សុវត្ថិភាព និងឯកជនភាព"</string>
+ <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"សន្តិសុខ និងឯកជនភាព"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"ស្កេនឧបករណ៍"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"ច្រានចោល"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"ច្រានចោលការជូនដំណឹងនេះឬ?"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"ស្ថានភាពសុវត្ថិភាព និង​ឯកជនភាព។ <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>។ <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"ការកំណត់​សុវត្ថិភាព"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"ការអនុញ្ញាត"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"សុវត្ថិភាព និងឯកជនភាព"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"សន្តិសុខ និងឯកជនភាព"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"ពិនិត្យ​​ស្ថានភាព"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"ការគ្រប់គ្រង​ឯកជនភាពរបស់អ្នក"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"ការកំណត់ច្រើនទៀត"</string>
diff --git a/PermissionController/res/values-kn/strings.xml b/PermissionController/res/values-kn/strings.xml
index 62868d4f9..c7036082e 100644
--- a/PermissionController/res/values-kn/strings.xml
+++ b/PermissionController/res/values-kn/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"ಹೆಚ್ಚಿನ ಮಾಹಿತಿ"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"ಎಲ್ಲವನ್ನೂ ಅನುಮತಿಸಿ"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"ಯಾವಾಗಲೂ ಎಲ್ಲವನ್ನೂ ಅನುಮತಿಸಿ"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"ಸೀಮಿತ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಅನುಮತಿಸಿ"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ಫೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"ಇನ್ನಷ್ಟು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"ಹೆಚ್ಚಿನ ಫೋಟೋಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಬೇಡಿ"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ಆ್ಯಪ್‌ಗಳು"</string>
<string name="app_permissions" msgid="3369917736607944781">"ಆ್ಯಪ್ ಅನುಮತಿಗಳು"</string>
<string name="unused_apps" msgid="2058057455175955094">"ಬಳಕೆಯಾಗದ ಆ್ಯಪ್‌ಗಳು"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"ಈ ಆ್ಯಪ್‌ಗಾಗಿ ಆಯ್ಕೆಮಾಡಲಾದ ಫೋಟೋಗಳನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ಯಾವುದೇ ಬಳಕೆಯಾಗದ ಆ್ಯಪ್‌ಗಳಿಲ್ಲ"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ಬಳಕೆಯಾಗದ ಆ್ಯಪ್‌ಗಳು"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"ಇತ್ತೀಚಿನ ಅನುಮತಿ ನಿರ್ಧಾರಗಳು"</string>
@@ -462,7 +464,7 @@
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಬಳಸದಿರುವಾಗಲೂ ಸಹ, ಯಾವಾಗಲೂ ನಿಮ್ಮ ಸ್ಥಳಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಲು ಆ್ಯಪ್ ಬಯಸಬಹುದು. "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಅನುಮತಿಸಿ."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಆ್ಯಪ್‌ಗಾಗಿ ಸ್ಥಳ ಪ್ರವೇಶವನ್ನು ಬದಲಾಯಿಸಬೇಕೆ?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಬಳಸದಿರುವಾಗಲೂ ಸಹ, ಯಾವಾಗಲೂ ನಿಮ್ಮ ಸ್ಥಳಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಲು ಆ್ಯಪ್ ಬಯಸುತ್ತದೆ. "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಅನುಮತಿಸಿ."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"ಸಮೀಪದ ಸಾಧನಗಳನ್ನು ಹುಡುಕಲು, ಅವುಗಳಿಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ಮತ್ತು ಅವುಗಳ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"ಸಮೀಪದ ಸಾಧನಗಳನ್ನು ಹುಡುಕಲು, ಅವುಗಳಿಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ಮತ್ತು ಅವುಗಳ ತುಲನಾತ್ಮಕ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"ಸಮೀಪದ ಸಾಧನಗಳನ್ನು ಹುಡುಕಲು, ಅವುಗಳಿಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ಮತ್ತು ಅವುಗಳ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ? "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಅನುಮತಿಸಿ."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> ನ ಸ್ಥಳ ಪ್ರವೇಶವನ್ನು ಅಂದಾಜಿನಿಂದ ನಿಖರತೆಗೆ ಬದಲಾಯಿಸಬೇಕೇ?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"ಈ ಸಾಧನದ ಅಂದಾಜು ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
@@ -482,7 +484,7 @@
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಬಳಸದಿರುವಾಗಲೂ ಸಹ, ಈ ಆ್ಯಪ್ ಯಾವಾಗಲೂ ಆಡಿಯೊವನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಲು ಬಯಸಬಹುದು. "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಅನುಮತಿಸಿ."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಆ್ಯಪ್‌ಗಾಗಿ ಮೈಕ್ರೋಫೋನ್ ಪ್ರವೇಶವನ್ನು ಬದಲಾಯಿಸಬೇಕೆ?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಬಳಸದಿರುವಾಗಲೂ ಸಹ, ಈ ಆ್ಯಪ್ ಯಾವಾಗಲೂ ಆಡಿಯೊವನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಲು ಬಯಸುತ್ತದೆ. "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಅನುಮತಿಸಿ."</annotation></string>
- <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"ನಿಮ್ಮ ದೈಹಿಕ ಚಟುವಟಿಕೆಯನ್ನು ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ಕ್ಕೆ ಅನುಮತಿಸುವುದೇ?"</string>
+ <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"ನಿಮ್ಮ ದೈಹಿಕ ಚಟುವಟಿಕೆಯನ್ನು ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"ಚಿತ್ರಗಳನ್ನು ಸೆರೆಹಿಡಿಯಲು ಮತ್ತು ವೀಡಿಯೊ ರೆಕಾರ್ಡ್‌ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಬಳಸುತ್ತಿರುವಾಗ ಮಾತ್ರ ಆ್ಯಪ್‌ಗೆ ಚಿತ್ರಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ಮತ್ತು ವೀಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"ಚಿತ್ರಗಳನ್ನು ಸೆರೆಹಿಡಿಯಲು ಮತ್ತು ವೀಡಿಯೊ ರೆಕಾರ್ಡ್‌ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
@@ -491,7 +493,7 @@
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"ನೀವು ಈ ಆ್ಯಪ್ ಬಳಸದಿರುವಾಗಲೂ ಸಹ, ಈ ಆ್ಯಪ್ ಎಲ್ಲಾ ಸಮಯದಲ್ಲೂ ಚಿತ್ರಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ಮತ್ತು ವೀಡಿಯೊವನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಲು ಬಯಸುತ್ತದೆ. "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಅನುಮತಿಸಿ."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"ನಿಮ್ಮ ಫೋನ್‌ ಕರೆಯ ಲಾಗ್‌ಗಳಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಪಡೆಯಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"ಫೋನ್ ಕರೆಗಳನ್ನು ಮಾಡಲು ಮತ್ತು ನಿರ್ವಹಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"ನಿಮ್ಮ ಮುಖ್ಯ ಲಕ್ಷಣಗಳ ಕುರಿತು ಸೆನ್ಸರ್ ಡೇಟಾವನ್ನು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"ನಿಮ್ಮ ಅವಶ್ಯಕ ಶಾರೀರಿಕ ಕಾರ್ಯಗಳ ಚಿಹ್ನೆಗಳ ಕುರಿತ ಸೆನ್ಸರ್ ಡೇಟಾವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"ನೀವು ಆ್ಯಪ್ ಬಳಸದಿರುವಾಗಲೂ ಸಹ, ನಿಮ್ಮ ಆರೋಗ್ಯ ಮಾಪನಗಳ ಕುರಿತು ಎಲ್ಲಾ ಸಮಯದಲ್ಲೂ ಸೆನ್ಸರ್ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಲು ಈ ಆ್ಯಪ್ ಬಯಸುತ್ತದೆ. ಈ ಬದಲಾವಣೆಯನ್ನು ಮಾಡಲು, "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"ನಿಮ್ಮ ಜೀವನಾಧಾರವಾಗಿರುವ ಲಕ್ಷಣಗಳ ಕುರಿತ ಸೆನ್ಸರ್ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಬಳಸದಿರುವಾಗಲೂ ಸಹ, ಎಲ್ಲಾ ಸಮಯದಲ್ಲೂ ದೇಹದ ಸೆನ್ಸರ್‌ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಲು ಈ ಆ್ಯಪ್ ಅನ್ನು ಅನುಮತಿಸಲು, "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ."</annotation></string>
diff --git a/PermissionController/res/values-ko/strings.xml b/PermissionController/res/values-ko/strings.xml
index 15256ebec..5b0daffe8 100644
--- a/PermissionController/res/values-ko/strings.xml
+++ b/PermissionController/res/values-ko/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"추가 정보"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"모두 허용"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"항상 모두 허용"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"제한된 액세스 허용"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"사진 및 동영상 선택"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"더보기 선택"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"추가 선택 안함"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"앱"</string>
<string name="app_permissions" msgid="3369917736607944781">"앱 권한"</string>
<string name="unused_apps" msgid="2058057455175955094">"사용하지 않는 앱"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"이 앱에서 액세스할 수 있는 사진 변경"</string>
<string name="no_unused_apps" msgid="12809387670415295">"사용하지 않는 앱 없음"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"사용하지 않는 앱 0개"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"최근 권한 결정"</string>
@@ -340,7 +342,7 @@
<string name="no_apps_allowed" msgid="7718822655254468631">"허용된 앱 없음"</string>
<string name="no_apps_allowed_full" msgid="8011716991498934104">"모든 파일에 허용된 앱 없음"</string>
<string name="no_apps_allowed_scoped" msgid="4908850477787659501">"미디어에만 허용된 앱 없음"</string>
- <string name="no_apps_denied" msgid="7663435886986784743">"거부된 앱 없음"</string>
+ <string name="no_apps_denied" msgid="7663435886986784743">"허용되지 않은 앱 없음"</string>
<string name="car_permission_selected" msgid="180837028920791596">"선택됨"</string>
<string name="settings" msgid="5409109923158713323">"설정"</string>
<string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"<xliff:g id="SERVICE_NAME">%s</xliff:g>이(가) 기기에 대한 전체 액세스 권한을 가지고 있습니다."</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"이 앱이 내 화면, 작업, 입력 내용을 보고 작업을 실행하며 디스플레이를 제어할 수 있습니다."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"기본 디지털 어시스턴트 앱"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"디지털 어시스턴트 앱"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"지원 앱은 화면에 표시된 정보에 맞게 도움을 줄 수 있습니다. 일부 앱은 통합된 지원을 제공하기 위해 런처와 음성 입력 서비스를 모두 지원합니다."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"지원 앱은 화면에 표시된 정보를 기반으로 도움을 줄 수 있습니다. 일부 앱은 통합된 지원을 제공하기 위해 런처와 음성 입력 서비스를 모두 지원합니다."</string>
<string name="role_browser_label" msgid="2877796144554070207">"기본 브라우저 앱"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"브라우저 앱"</string>
<string name="role_browser_description" msgid="3465253637499842671">"인터넷에 액세스하고 탭하는 링크를 표시하는 앱"</string>
@@ -492,9 +494,9 @@
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 통화 기록에 액세스하도록 허용하시겠습니까?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 전화를 걸고 관리하도록 허용하시겠습니까?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 생체 신호에 관한 센서 데이터에 액세스하도록 허용하시겠습니까?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"앱이 사용되고 있지 않을 때도 항상 생체 신호 센서 데이터에 액세스하고자 합니다. 권한을 변경하려면 "<annotation id="link">"설정으로 이동"</annotation>"하세요."</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"앱이 사용되고 있지 않을 때를 포함해 항상 생체 신호 센서 데이터에 액세스하고자 합니다. 권한을 변경하려면 "<annotation id="link">"설정으로 이동"</annotation>"하세요."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 생체 신호에 관한 센서 데이터에 액세스하도록 허용하시겠습니까?"</string>
- <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"앱을 사용하지 않을 때도 앱이 항상 생체 신호 센서 데이터에 액세스하도록 허용하려면 "<annotation id="link">"설정으로 이동"</annotation>"하세요"</string>
+ <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"앱을 사용하지 않을 때를 포함해 앱이 항상 생체 신호 센서 데이터에 액세스하도록 허용하려면 "<annotation id="link">"설정으로 이동"</annotation>"하세요"</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱을 사용하는 중에만 생체 신호 센서 데이터에 액세스하도록 허용하는 설정을 유지하시겠습니까?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 알림을 보내도록 허용하시겠습니까?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"관리 대상 권한"</string>
diff --git a/PermissionController/res/values-ky/strings.xml b/PermissionController/res/values-ky/strings.xml
index e8816f7fd..aa7463009 100644
--- a/PermissionController/res/values-ky/strings.xml
+++ b/PermissionController/res/values-ky/strings.xml
@@ -34,20 +34,21 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Дагы маалымат"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Баарына уруксат берүү"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Ар дайым баарына уруксат берүү"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Чектелген мүмкүнчүлүк берүү"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Сүрөттөрдү жана видеолорду тандаңыз"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Дагы тандоо"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Башка тандалбасын"</string>
<string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Баары бир тыюу салуу"</string>
<string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Жабуу"</string>
<string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> уруксаттын ичинен <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
- <string name="permission_warning_template" msgid="2247087781222679458">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна <xliff:g id="ACTION">%2$s</xliff:g> уруксат берилсинби?"</string>
- <string name="permission_add_background_warning_template" msgid="1812914855915092273">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна <xliff:g id="ACTION">%2$s</xliff:g> аракетине ар дайым уруксат берилсинби?"</string>
+ <string name="permission_warning_template" msgid="2247087781222679458">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна <xliff:g id="ACTION">%2$s</xliff:g> уруксат бересизби?"</string>
+ <string name="permission_add_background_warning_template" msgid="1812914855915092273">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна <xliff:g id="ACTION">%2$s</xliff:g> аракетине ар дайым уруксат бересизби?"</string>
<string name="allow_permission_foreground_only" msgid="116465816039675404">"Ушул колдонмодо иштегенде гана"</string>
<string name="allow_permission_always" msgid="5194342531206054051">"Ар дайым"</string>
<string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"Тыюу салам жана экинчи суралбасын"</string>
<string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g> өчүрүлгөн"</string>
<string name="permission_revoked_all" msgid="3397649017727222283">"баары өчүрүлгөн"</string>
- <string name="permission_revoked_none" msgid="9213345075484381180">"эч бири өчүрүлгөн жок"</string>
+ <string name="permission_revoked_none" msgid="9213345075484381180">"эч бири өчкөн жок"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"Уруксат берүү"</string>
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"Бардык учурда уруксат берилет"</string>
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Колдонмо ачылып турганда"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Колдонмолор"</string>
<string name="app_permissions" msgid="3369917736607944781">"Колдонмонун уруксаттары"</string>
<string name="unused_apps" msgid="2058057455175955094">"Колдонулбаган колдонмолор"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Бул колдонмого жеткиликтүү сүрөттөрдүн тизмесин өзгөртүү"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Бардык колдонмолор колдонулууда"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Колдонулбаган колдонмолор: 0"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Уруксаттарга байланыштуу аракеттер"</string>
@@ -375,7 +377,7 @@
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"өзгөчө кырдаалда"</string>
<string name="role_home_label" msgid="3871847846649769412">"Демейки башкы экран"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Home колдонмосу"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Android түзмөгүңүздө башкы экрандын ордуна колдонулган, функциялар менен колдонмолорду издеп таап, иштеткен колдонмолор"</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Android түзмөгүңүздө башкы экрандын ордуна колдонулуп, функциялар менен колдонмолорду издеп таап, иштеткен колдонмолор"</string>
<string name="role_home_request_title" msgid="738136983453341081">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосун демейки \"үй\" колдонмосу катары коёсузбу?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Уруксаттардын кереги жок"</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"жүргүзгүч"</string>
@@ -465,11 +467,11 @@
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; жакын жердеги түзмөктөрдү таап, аларга туташып жана абалын аныктай берсинби?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; жакын жердеги түзмөктөрдү таап, аларга туташып жана абалын аныктай алсынбы? "<annotation id="link">"Параметрлерден уруксат берүү."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> колдонмосунда жайгашкан жер болжолдуу эмес, так аныкталсынбы?"</string>
- <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна бул түзмөктүн болжолдуу жайгашкан жерин пайдаланууга уруксат берилсинби?"</string>
+ <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна бул түзмөктүн болжолдуу жайгашкан жерин пайдаланууга уруксат бересизби?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Так"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Болжолдуу"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна жылнаамаңызды пайдаланууга уруксат бересизби?"</string>
- <string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна SMS билдирүүлөрдү жөнөтүүгө жана окууга уруксат берилсинби?"</string>
+ <string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна SMS билдирүүлөрдү жөнөтүүгө жана окууга уруксат бересизби?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна түзмөгүңүздөгү сүрөттөрдү жана башка мультимедиа файлдарын пайдаланууга уруксат бересизби?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна ушул түзмөктөгү &lt;b&gt;сүрөттөрдү, видеолорду, ырларды жана аудио файлдарды&lt;/b&gt; жеткиликтүү кыласызбы?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна ушул түзмөктөгү &lt;b&gt;сүрөттөрдү, видеолорду, ырларды, аудио файлдарды жана башка нерселерди&lt;/b&gt; жеткиликтүү кыласызбы?"</string>
@@ -492,10 +494,10 @@
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна телефондогу чалуулар тизмесин пайдаланууга уруксат бересизби?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна телефон чалууга жана чалууларды башкарууга уруксат бересизби?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна организмдин абалына көз салган сенсордун көрсөткүчтөрүн көрүүгө уруксат бересизби?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Бул колдонмону иштетпей турсаңыз деле, ал такай организмдин негизги көрсөткүчтөрү тууралуу cенсордун дайындарын жаздыра алат. Муну өзгөртүү үчүн "<annotation id="link">"параметрлерге өтүңүз."</annotation></string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Бул колдонмону иштетпесеңиз да, ага организмдин негизги көрсөткүчтөрү тууралуу cенсордун маалыматы жеткиликтүү болушу керек. Ага уруксат берүү үчүн "<annotation id="link">"параметрлерге өтүңүз"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна дене-бой сенсорлорунун көрсөткүчтөрүн көрүүгө уруксат бересизби?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Бул колдонмого дене сенсорлорунун көрсөткүчтөрүн көрүү мүмкүнчүлүгүн берүү үчүн (колдонмо колдонулбай турганда да) "<annotation id="link">"параметрлерди өзгөртүңүз."</annotation></string>
- <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу колдонулуп жатканда дене бой сенсорлорунун көрсөткүчтөрүн көрүү мүмкүнчүлүгүн бересизби?"</string>
+ <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу колдонулуп жатканда дене бой сенсорлорунун көрсөткүчтөрүн жеткиликтүү кыласызбы?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна сизге билдирмелерди жөнөтүүгө уруксат бересизби?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Көзөмөлдөнгөн уруксаттар"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> жүргөн жериңизди аныктай алат"</string>
@@ -581,7 +583,7 @@
<string name="mic_toggle_title" msgid="2649991093496110162">"Микрофонду колдонуу"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Колдонмолор жана кызматтар үчүн"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Кырсыктаганда жардамга келчү кызматка чалганда, микрофондогу нерселер бөлүшүлүшү мүмкүн."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"Кайда жүргөнүңүздү көрүүгө уруксаты бар колдонмолорду жана кызматтарды көрүү"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"Кайда жүргөнүңүздү көрө алган колдонмолор менен кызматтардын тизмеси"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Алмашуу буферин пайдалануу мүмкүнчүлүгүн көрсөтүү"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Колдонмолор көчүрүлгөн текстти, сүрөттөрдү же башка нерселерди пайдаланганда билдирүүлөр көрүнөт"</string>
<string name="show_password_title" msgid="2877269286984684659">"Сырсөз көрүнсүн"</string>
@@ -610,11 +612,11 @@
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Турган жериңизди билдирүү ыкмасын жаңыртуу"</string>
<string name="data_sharing_updates_summary" msgid="764113985772233889">"Жүргөн жериңизди көрсөткөн ыкманы өзгөрткөн колдонмолорду карап чыксаңыз болот"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Бул колдонмолор жүргөн жериңиз тууралуу маалыматты бөлүшүү ыкмасын өзгөрттү. Алар бул маалыматты мурда бөлүшпөй же азыр жарнамалоо же маркетинг максаттарында бөлүшүшү мүмкүн."</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Бул колдонмолорду иштеп чыгуучулар нерселерди бөлүшүү тууралуу маалыматты колдонмолор дүкөнүнө беришти. Алар бул маалыматты жаңыртып турушу мүмкүн.\n\nМаалыматтарды бөлүшүү ыкмасы колдонмонун версиясына, колдонулушуна, регионго жана курагыңызга жараша айырмаланышы мүмкүн."</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Бул колдонмолорду иштеп чыккандар дүкөнгө маалымат бөлүшүү ыкмасын беришти. Бул маалыматтар такай жаңырып турушу мүмкүн.\n\nМаалыматтар бөлүшүү ыкмасы колдонмонун версиясына, анын колдонулушуна, өлкөгө жана курагыңызга жараша айырмаланышы мүмкүн."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Маалыматтарды бөлүшүү жөнүндө кеңири маалымат"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Жүргөн жериңиз тууралуу маалымат үчүнчү тараптар менен бөлүшүлүп жатат"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Жүргөн жериңизди үчүнчү тараптар жарнамалоо же маркетинг максатында билип турушат"</string>
- <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Кечээ жаңыртылды}=1{Кечээ жаңыртылды}other{# күн мурда жаңыртылды}}"</string>
+ <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Кечээ жаңырды}=1{Кечээ жаңырды}other{# күн мурда жаңырды}}"</string>
<string name="no_updates_at_this_time" msgid="9031085635689982935">"Азырынча жаңыртуулар жок"</string>
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Маалыматты бөлүшүү жаңыртуулары"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Айрым колдонмолор турган жериңизди билдирүү ыкмасын өзгөрттү"</string>
diff --git a/PermissionController/res/values-lo/strings.xml b/PermissionController/res/values-lo/strings.xml
index be8cf2947..1300cc48c 100644
--- a/PermissionController/res/values-lo/strings.xml
+++ b/PermissionController/res/values-lo/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"ຂໍ້ມູນເພີ່ມເຕີມ"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"ອະນຸຍາດທັງໝົດ"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"ອະນຸຍາດທັງໝົດຕະຫຼອດ"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"ອະນຸຍາດສິດເຂົ້າເຖິງແບບຈຳກັດ"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ເລືອກຮູບພາບ ແລະ ວິດີໂອ"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"ເລືອກເພີ່ມເຕີມ"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"ຢ່າເລືອກເພີ່ມເຕີມ"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ແອັບ"</string>
<string name="app_permissions" msgid="3369917736607944781">"ສິດອະນຸຍາດແອັບ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ແອັບທີ່ບໍ່ໄດ້ໃຊ້"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"ແກ້ໄຂຮູບພາບທີ່ເລືອກໄວ້ສຳລັບແອັບນີ້"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ບໍ່ມີແອັບທີ່ບໍ່ໄດ້ໃຊ້"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ແອັບທີ່ບໍ່ໄດ້ໃຊ້"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"ການຕັດສິນໃຈການອະນຸຍາດຫຼ້າສຸດ"</string>
diff --git a/PermissionController/res/values-lt/strings.xml b/PermissionController/res/values-lt/strings.xml
index 1c179457d..7479434f2 100644
--- a/PermissionController/res/values-lt/strings.xml
+++ b/PermissionController/res/values-lt/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Daugiau inform."</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Leisti viską"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Visada leisti viską"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Suteikti ribotą prieigą"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Pasirinkti nuotraukas ir vaizdo įrašus"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Pasirinkti daugiau"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Nesirinkti daugiau"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Programos"</string>
<string name="app_permissions" msgid="3369917736607944781">"Programų leidimai"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nenaudojamos programos"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Redaguoti pasirinktas šios programos nuotraukas"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nėra nenaudojamų programų"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Nenaudojamų programų: 0"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nauj. sprendimai dėl leidimų"</string>
@@ -609,7 +611,7 @@
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Ši programa nurodė, kad gali bendrinti vietovės duomenis su trečiosiomis šalimis"</string>
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Duomenų bendrinimo atnaujinimai pagal vietovę"</string>
<string name="data_sharing_updates_summary" msgid="764113985772233889">"Peržiūrėkite programas, kuriose pakeisti vietovės duomenų bendrinimo metodai"</string>
- <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Šiose programose pakeisti vietovės duomenų bendrinimo metodai Gali būti, kad anksčiau jie nebuvo bendrinami, arba dabar jie gali būti bendrinami reklamavimo ar rinkodaros tikslais."</string>
+ <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Šiose programose pakeisti vietovės duomenų bendrinimo metodai. Gali būti, kad anksčiau jie nebuvo bendrinami, arba dabar jie gali būti bendrinami reklamavimo ar rinkodaros tikslais."</string>
<string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Šių programų kūrėjai pateikė informacijos apie savo duomenų bendrinimo praktikos metodus programų parduotuvėje. Jie gali ją atnaujinti per laiką.\n\nDuomenų bendrinimo praktikos metodai gali skirtis atsižvelgiant į programos versiją, naudojimą, regioną ir amžių."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Sužinokite apie duomenų bendrinimą"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Jūsų vietovės duomenys dabar bendrinami su trečiosiomis šalimis"</string>
diff --git a/PermissionController/res/values-lv/strings.xml b/PermissionController/res/values-lv/strings.xml
index 530e2cc6c..4fb7d2dd3 100644
--- a/PermissionController/res/values-lv/strings.xml
+++ b/PermissionController/res/values-lv/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Informācija"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Atļaut visu"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Vienmēr atļaut visu"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Atļaut ierobežotu piekļuvi"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Atlasīt fotoattēlus un video"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Atlasīt citus"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Neatlasīt vairāk"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Lietotnes"</string>
<string name="app_permissions" msgid="3369917736607944781">"Lietotņu atļaujas"</string>
<string name="unused_apps" msgid="2058057455175955094">"Neizmantotās lietotnes"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Rediģēt fotoattēlu atlasi šai lietotnei"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nav neizmantotu lietotņu"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 neizmantotu lietotņu"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nesenās darbības ar atļaujām"</string>
@@ -585,7 +587,7 @@
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Rādīt paziņojumus par piekļuvi starpliktuvei"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Rādīt ziņojumu, kad lietotnes piekļūst jūsu nokopētajam tekstam, attēliem vai citam saturam"</string>
<string name="show_password_title" msgid="2877269286984684659">"Rādīt paroles"</string>
- <string name="show_password_summary" msgid="1110166488865981610">"Rakstot tiek īslaicīgi rādītas rakstzīmes"</string>
+ <string name="show_password_summary" msgid="1110166488865981610">"Rakstot īslaicīgi rādīt rakstzīmes"</string>
<string name="permission_rationale_message_location" msgid="2153841534298068414">"Lietotne norādīja, ka tā var kopīgot atrašanās vietas datus ar trešajām pusēm."</string>
<string name="permission_rationale_location_title" msgid="2404797182678793506">"Datu kopīgošana un atrašanās vieta"</string>
<string name="permission_rationale_data_sharing_source_title" msgid="6874604543125814316">"Datu kopīgošanas informācijas avots"</string>
diff --git a/PermissionController/res/values-mk/strings.xml b/PermissionController/res/values-mk/strings.xml
index d8c542b76..abd3a6224 100644
--- a/PermissionController/res/values-mk/strings.xml
+++ b/PermissionController/res/values-mk/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Уште информации"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Дозволи за сите"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Секогаш дозволувај ги сите"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Дозволи ограничен пристап"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Одредени фотографии и видеа"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Изберете повеќе"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Не избирајте повеќе"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Апликации"</string>
<string name="app_permissions" msgid="3369917736607944781">"Дозволи за апликации"</string>
<string name="unused_apps" msgid="2058057455175955094">"Некористени апликации"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Изберете ги фотографиите до кои ќе има пристап апликацијава"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Нема некористени апликации"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 некористени апликации"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Неодамнешни одлуки за дозволи"</string>
@@ -90,7 +92,7 @@
<string name="manage_permission" msgid="2895385393037061964">"Управувајте со дозволата"</string>
<string name="no_apps" msgid="2412612731628386816">"Нема апликации"</string>
<string name="location_settings" msgid="3624412509133422562">"Поставки за локација"</string>
- <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> е давател на услуги според локација за овој уред. Пристапот до локацијата може да се смени од поставките за локација."</string>
+ <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> е давател на локациски услуги за овој уред. Пристапот до локацијата може да се смени од поставките за локација."</string>
<string name="system_warning" msgid="1173400963234358816">"Ако ја одбиете дозволава, основните функции на уредот можеби веќе нема да функционираат како што треба."</string>
<string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Оваа апликација е дизајнирана за постара верзија на Android. Ако го одбиете пристапот на апликацијава до фотографиите и видеата, пристапот до музиката и другото аудио исто така ќе се одбие."</string>
<string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Оваа апликација е дизајнирана за постара верзија на Android. Ако го одбиете пристапот на апликацијава до музиката и другото аудио, пристапот до фотографиите и видеата исто така ќе се одбие."</string>
@@ -236,7 +238,7 @@
<string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Апликациите со оваа дозвола може да наоѓаат и да се поврзуваат со уреди во близина и да ја утврдуваат нивната релативна положба"</string>
<string name="permission_description_summary_microphone" msgid="630834800308329907">"Апликациите со оваа дозвола може да снимаат аудио"</string>
<string name="permission_description_summary_phone" msgid="4515277217435233619">"Апликациите со оваа дозвола може да упатуваат телефонски повици и да управуваат со нив"</string>
- <string name="permission_description_summary_sensors" msgid="1836045815643119949">"Апликациите со оваа дозвола може да пристапуваат до податоци од сензорите за виталните знаци"</string>
+ <string name="permission_description_summary_sensors" msgid="1836045815643119949">"Апликациите со оваа дозвола може да пристапуваат до податоци од сензорите за виталните функции"</string>
<string name="permission_description_summary_sms" msgid="725999468547768517">"Апликациите со оваа дозвола може да испраќаат и прегледуваат SMS-пораки"</string>
<string name="permission_description_summary_storage" msgid="6575759089065303346">"Апликациите со оваа дозвола може да пристапуваат до фотографии, аудиовизуелни содржини и датотеки на вашиот уред"</string>
<string name="permission_description_summary_read_media_aural" msgid="3354728149930482199">"Апликациите со оваа дозвола може да пристапуваат до музиката и другите аудиодатотеки на уредов"</string>
@@ -473,7 +475,7 @@
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до фотографиите, аудиовизуелните содржини и датотеките на уредот?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до &lt;b&gt;фотографии, видеа, музика и аудио&lt;/b&gt; на уредов?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до &lt;b&gt;фотографии, видеа, музика, аудио и други датотеки&lt;/b&gt; на уредов?"</string>
- <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до музика и аудиодатотеки на уредов?"</string>
+ <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до музика и аудио на уредов?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до фотографии и видеа на уредов?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до повеќе фотографии и видеа на уредов?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да снима аудио?"</string>
@@ -491,9 +493,9 @@
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Апликацијава сака да снима слики и видеа цело време, дури и кога не ја користите. "<annotation id="link">"Дозволете во „Поставки“."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до евиденцијата на повици?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да упатува телефонски повици и да управува со нив?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до податоците на сензорот за витални знаци?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Апликацијава сака да пристапува до податоците од сензорите за вашите витални знаци цело време, дури и кога не се користи. За да го промените ова, "<annotation id="link">"одете во „Поставки“."</annotation></string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Дали да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до податоците од сензорите за витални знаци?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до податоците на сензорот за витални функции?"</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Апликацијава сака да пристапува до податоците од сензорите за вашите витални функции цело време, дури и кога не се користи. За да го промените ова, "<annotation id="link">"одете во „Поставки“."</annotation></string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Дали да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до податоците од сензорите за витални функции?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"За да дозволите апликацијава да пристапува до податоци од телесните сензори цело време, дури и кога не ја користите, "<annotation id="link">"одете во „Поставки“."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"И понатаму да се дозволи пристап на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до податоци од телесните сензори додека се користи апликацијата?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да ви испраќа известувања?"</string>
diff --git a/PermissionController/res/values-ml-v33/strings.xml b/PermissionController/res/values-ml-v33/strings.xml
index fec0f6707..a4cc0190e 100644
--- a/PermissionController/res/values-ml-v33/strings.xml
+++ b/PermissionController/res/values-ml-v33/strings.xml
@@ -38,7 +38,7 @@
<string name="safety_center_qs_expand_action" msgid="2193190557696484169">"വികസിപ്പിച്ച് ഓപ്ഷനുകൾ കാണിക്കുക"</string>
<string name="safety_center_qs_collapse_action" msgid="5809657430125309183">"ചുരുക്കുക"</string>
<string name="safety_center_qs_privacy_control" msgid="1160682635058529673">"മാറുക. <xliff:g id="PRIVACY_CONTROL_TITLE">%1$s</xliff:g>. <xliff:g id="PRIVACY_CONTROL_STATUS">%2$s</xliff:g>"</string>
- <string name="safety_center_qs_toggle_action" msgid="5920465736488119255">"മാറ്റുക"</string>
+ <string name="safety_center_qs_toggle_action" msgid="5920465736488119255">"ടോഗിൾ ചെയ്യുക"</string>
<string name="safety_center_qs_open_action" msgid="2760200829912423728">"തുറക്കുക"</string>
<string name="safety_center_review_settings_button" msgid="938981137942443930">"ക്രമീകരണം അവലോകനം ചെയ്യുക"</string>
<string name="safety_center_gear_label" msgid="5175877094379694098">"ക്രമീകരണം"</string>
diff --git a/PermissionController/res/values-ml/strings.xml b/PermissionController/res/values-ml/strings.xml
index 81cf39cf5..1380a6fbc 100644
--- a/PermissionController/res/values-ml/strings.xml
+++ b/PermissionController/res/values-ml/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"കൂടുതൽ വിവരങ്ങൾ"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"എല്ലാം അനുവദിക്കുക"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"എപ്പോഴും എല്ലാം അനുവദിക്കുക"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"പരിമിതമായ ആക്‌സസ് അനുവദിക്കുക"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ഫോട്ടോകളും വീഡിയോകളും തിരഞ്ഞെടുക്കുക"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"കൂടുതൽ തിരഞ്ഞെടുക്കുക"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"കൂടുതൽ തിരഞ്ഞെടുക്കരുത്"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ആപ്പുകൾ"</string>
<string name="app_permissions" msgid="3369917736607944781">"ആപ്പ് അനുമതികൾ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ഉപയോഗിക്കാത്ത ആപ്പുകൾ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"ഈ ആപ്പിനായി തിരഞ്ഞെടുത്ത ഫോട്ടോകൾ എഡിറ്റ് ചെയ്യുക"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ഉപയോഗിക്കാത്ത ആപ്പുകൾ ഇല്ല"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"ഉപയോഗിക്കാത്ത 0 ആപ്പുകൾ"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"സമീപകാല അനുമതി തീരുമാനങ്ങൾ"</string>
diff --git a/PermissionController/res/values-mn/strings.xml b/PermissionController/res/values-mn/strings.xml
index 3332fe450..a3d98a066 100644
--- a/PermissionController/res/values-mn/strings.xml
+++ b/PermissionController/res/values-mn/strings.xml
@@ -28,12 +28,13 @@
<string name="uninstall_or_disable" msgid="4496612999740858933">"Устгах эсвэл идэвхгүй болгох"</string>
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"Апп олдсонгүй"</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"Бүү зөвшөөр"</string>
- <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Бүү зөвшөөрч, дахин битгий асуу"</string>
+ <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Бүү зөвшөөр, дахин бүү асуу"</string>
<string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"Аппыг ашиглаж байх үед\" хэвээр үлдээх"</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“Зөвхөн энэ удаад зөвшөөрөх”-г хэвээр хадгалах"</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Дэлгэрэнгүй мэдээлэл"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Бүгдийг зөвшөөрөх"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Бүгдийг үргэлж зөвшөөрөх"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Хязгаарлагдмал хандалтыг зөвшөөрөх"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Зураг болон видеонуудыг сонгох"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Илүү ихийг сонгох"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Нэмж өгөгдөл сонгохгүй"</string>
@@ -44,7 +45,7 @@
<string name="permission_add_background_warning_template" msgid="1812914855915092273">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д <xliff:g id="ACTION">%2$s</xliff:g>-г тогтмол зөвшөөрөх үү?"</string>
<string name="allow_permission_foreground_only" msgid="116465816039675404">"Зөвхөн апп ашиглах үед"</string>
<string name="allow_permission_always" msgid="5194342531206054051">"Тогтмол"</string>
- <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"Бүү зөвшөөрч, дахин битгий асуу"</string>
+ <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"Бүү зөвшөөр, дахин бүү асуу"</string>
<string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g>-г цуцалсан"</string>
<string name="permission_revoked_all" msgid="3397649017727222283">"бүгдийг цуцалсан"</string>
<string name="permission_revoked_none" msgid="9213345075484381180">"алийг нь ч цуцлаагүй"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Аппууд"</string>
<string name="app_permissions" msgid="3369917736607944781">"Аппын зөвшөөрөл"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ашиглаагүй аппууд"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Энэ аппад сонгосон зургуудыг засна уу"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ашиглаагүй апп байхгүй байна"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ашиглаагүй апп"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Саяхны зөвшөөрлийн шийдвэр"</string>
@@ -374,7 +376,7 @@
<string name="role_emergency_request_description" msgid="131645948770262850">"Ямар ч зөвшөөрөл шаардлагагүй"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"ice"</string>
<string name="role_home_label" msgid="3871847846649769412">"Нүүр хуудасны өгөгдмөл апп"</string>
- <string name="role_home_short_label" msgid="8544733747952272337">"Нүүр хуудасны апп"</string>
+ <string name="role_home_short_label" msgid="8544733747952272337">"Home апп"</string>
<string name="role_home_description" msgid="7997371519626556675">"Таны Android төхөөрөмжийн үндсэн нүүрийг сольдог бөгөөд таны төхөөрөмжийн контент болон онцлогуудад хандах эрх олгодог, ихэвчлэн эхлүүлэгч гэж нэрлэгддэг аппууд"</string>
<string name="role_home_request_title" msgid="738136983453341081">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г нүүр хуудасны өгөгдмөл аппаар тохируулах уу?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Ямар ч зөвшөөрөл шаардлагагүй"</string>
@@ -443,7 +445,7 @@
<string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> алдаа засах мэдээллийг байршуулах хүсэлтэй байна."</string>
<string name="incident_report_dialog_title" msgid="669104389325204095">"Дебаг хийх өгөгдлийг хуваалцах уу?"</string>
<string name="incident_report_dialog_intro" msgid="5897733669850951832">"Систем асуудал илрүүллээ."</string>
- <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> энэ төхөөрөмжөөс <xliff:g id="DATE">%2$s</xliff:g>-н <xliff:g id="TIME">%3$s</xliff:g>-д авсан алдааны мэдээг байршуулах хүсэлт тавьж байна. Алдааны мэдээнд таны төхөөрөмжийн талаарх эсвэл хэрэглэгчийн нэр, байршлын өгөгдөл, төхөөрөмжийн таниулбар болон сүлжээний мэдээлэл зэрэг аппуудын бүртгэсэн хувийн мэдээллийг агуулна. Та алдааны мэдээг зөвхөн энэ мэдээллийг хуваалцахдаа итгэлтэй байгаа хүмүүс болон аппуудтай хуваалцана уу. <xliff:g id="APP_NAME_1">%4$s</xliff:g>-д алдааны мэдээг байршуулахыг зөвшөөрөх үү?"</string>
+ <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> энэ төхөөрөмжөөс <xliff:g id="DATE">%2$s</xliff:g>-н <xliff:g id="TIME">%3$s</xliff:g>-д авсан алдааны мэдээг байршуулах хүсэлт тавьж байна. Алдааны мэдээнд таны төхөөрөмжийн талаарх эсвэл хэрэглэгчийн нэр, байршлын өгөгдөл, төхөөрөмжийн танигч болон сүлжээний мэдээлэл зэрэг аппуудын бүртгэсэн хувийн мэдээллийг агуулна. Та алдааны мэдээг зөвхөн энэ мэдээллийг хуваалцахдаа итгэлтэй байгаа хүмүүс болон аппуудтай хуваалцана уу. <xliff:g id="APP_NAME_1">%4$s</xliff:g>-д алдааны мэдээг байршуулахыг зөвшөөрөх үү?"</string>
<string name="incident_report_error_dialog_text" msgid="4189647113387092272">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н алдааны мэдээг боловсруулахад алдаа гарсан тул алдаа засах дэлгэрэнгүй өгөгдлийг хуваалцахаас татгалзлаа. Төвөг удсанд хүлцэл өчье."</string>
<string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Зөвшөөрөх"</string>
<string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Татгалзах"</string>
diff --git a/PermissionController/res/values-mr/strings.xml b/PermissionController/res/values-mr/strings.xml
index 18eccb6a7..1b0c3d48d 100644
--- a/PermissionController/res/values-mr/strings.xml
+++ b/PermissionController/res/values-mr/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"अधिक माहिती"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"सर्वांना अनुमती द्या"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"नेहमी सर्वांना अनुमती द्या"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"मर्यादित अ‍ॅक्सेसची अनुमती द्या"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"फोटो आणि व्हिडिओ निवडा"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"आणखी निवडा"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"आणखी निवडू नका"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"अ‍ॅप्स"</string>
<string name="app_permissions" msgid="3369917736607944781">"अ‍ॅप परवानग्या"</string>
<string name="unused_apps" msgid="2058057455175955094">"न वापरलेली अ‍ॅप्स"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"या अ‍ॅपसाठी निवडलेले फोटो संपादित करा"</string>
<string name="no_unused_apps" msgid="12809387670415295">"न वापरलेली कोणतीही ॲप्स नाहीत"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"न वापरलेली शून्य अ‍ॅप्स"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"अलीकडील परवानगीसंबंधित निर्णय"</string>
@@ -219,7 +221,7 @@
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"<xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g> आणि <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g> परवानग्या काढल्या गेल्या"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> आणि इतर <xliff:g id="NUMBER">%2$s</xliff:g> परवानग्या काढल्या गेल्या"</string>
<string name="unused_apps_page_title" msgid="6986983535677572559">"न वापरलेली अ‍ॅप्स"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"ॲप काही महिन्यांमध्ये वापरले गेले नसल्यास:\n\n• तुमच्या डेटाचे संरक्षण करण्यासाठी परवानग्या काढून टाकल्या जातात\n• बॅटरीची बचत करण्यासाठी सूचना थांबवल्या जातात\n• जागा मोकळी करण्यासाठी तात्पुरत्या फाइल काढून टाकल्या जातात\n\nपरवानग्या आणि सूचना यांना पुन्हा अनुमती देण्यासाठी ॲप उघडा."</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"ॲप काही महिन्यांमध्ये वापरले गेले नसल्यास:\n\n• तुमच्या डेटाचे संरक्षण करण्यासाठी परवानग्या काढून टाकल्या जातात\n• बॅटरीची बचत करण्यासाठी नोटिफिकेशन थांबवल्या जातात\n• जागा मोकळी करण्यासाठी तात्पुरत्या फाइल काढून टाकल्या जातात\n\nपरवानग्या आणि नोटिफिकेशन यांना पुन्हा अनुमती देण्यासाठी ॲप उघडा."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"एखादे ॲप एका महिन्यांसाठी वापरले गेले नसल्यास:\n\n• तुमच्या डेटाचे संरक्षण करण्यासाठी परवानग्या काढून टाकल्या जातात\n• जागा मोकळी करण्याकरिता तात्पुरत्या फाइल काढून टाकल्या जातात\n\nपुन्हा परवानग्या देण्यासाठी, ॲप उघडा."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{शेवटचे # महिन्यापूर्वीपेक्षा आधी उघडले}other{शेवटचे # महिन्यांपूर्वीपेक्षा आधी उघडले}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"<xliff:g id="DATE">%s</xliff:g> रोजी ॲप शेवटचे उघडलेले"</string>
@@ -492,11 +494,11 @@
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुमचे फोन कॉल लॉग अ‍ॅक्सेस करण्याची अनुमती द्यायची का?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला फोन कॉल करू आणि ते व्यवस्थापित करू द्यायचे?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुमच्या महत्त्वाच्या लक्षणांविषयीचा सेन्सर डेटा अ‍ॅक्सेस करू द्यायचे?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"तुम्ही अ‍ॅप वापरत नसतानादेखील या ॲपला तुमच्या महत्त्वाच्या परिमाणांबद्दलचा सेन्सर डेटा पूर्णवेळ ॲक्सेस करायचा आहे. हा बदल करण्यासाठी, "<annotation id="link">" सेटिंग्जवर जा."</annotation></string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुमच्या महत्त्वाच्या परिमाणांसंबंधित सेन्सर डेटा अ‍ॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"तुम्ही अ‍ॅप वापरत नसतानादेखील या ॲपला तुमच्या महत्त्वाच्या लक्षणांबद्दलचा सेन्सर डेटा पूर्णवेळ ॲक्सेस करायचा आहे. हा बदल करण्यासाठी, "<annotation id="link">" सेटिंग्जवर जा."</annotation></string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुमच्या महत्त्वाच्या लक्षणांसंबंधित सेन्सर डेटा अ‍ॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"तुम्ही ॲप वापरत नसतानादेखील या ॲपला शरीर सेन्सर डेटा नेहमी अ‍ॅक्सेस करू देण्यासाठी, "<annotation id="link">"सेटिंग्जवर जा."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"ॲप वापरात असताना &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला शरीर सेन्सर डेटा अ‍ॅक्सेस करण्याची अनुमती देणे सुरू ठेवायचे आहे का?"</string>
- <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुम्हाला सूचना पाठवू द्यायचे का?"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुम्हाला नोटिफिकेशन पाठवू द्यायची का?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"नियंत्रित परवानग्या"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> कडे स्थान अ‍ॅक्सेस आहे"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"तुमची संस्था <xliff:g id="APP_NAME">%1$s</xliff:g> ला तुमचे स्थान अ‍ॅक्सेस करण्याची अनुमती देते"</string>
@@ -552,7 +554,7 @@
<string name="recent_app_usage_1_qs" msgid="261450184773310741">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) द्वारे अलीकडे वापरले गेले"</string>
<string name="active_app_usage_2_qs" msgid="6107866785243565283">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) द्वारे वापरले जात आहे"</string>
<string name="recent_app_usage_2_qs" msgid="3591205954235694403">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) द्वारे अलीकडे वापरले गेले"</string>
- <string name="media_confirm_dialog_positive_button" msgid="9020793594051526399">"कंफर्म करा"</string>
+ <string name="media_confirm_dialog_positive_button" msgid="9020793594051526399">"कन्फर्म करा"</string>
<string name="media_confirm_dialog_negative_button" msgid="226987376924861785">"मागे जा"</string>
<string name="media_confirm_dialog_title_a_to_p_aural_allow" msgid="8560601114044699903">"इतर फाइलच्या अ‍ॅक्सेसलादेखील अनुमती दिली जाईल"</string>
<string name="media_confirm_dialog_title_a_to_p_aural_deny" msgid="7841428716317307685">"इतर फाइलच्या अ‍ॅक्सेसलादेखील अनुमती दिली जाणार नाही"</string>
diff --git a/PermissionController/res/values-ms/strings.xml b/PermissionController/res/values-ms/strings.xml
index 7e560fa73..1451bf660 100644
--- a/PermissionController/res/values-ms/strings.xml
+++ b/PermissionController/res/values-ms/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Lagi maklumat"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Benarkan semua"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Sentiasa benarkan semua"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Benarkan akses terhad"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Pilih foto dan video"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Pilih lagi"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Jangan pilih lagi"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apl"</string>
<string name="app_permissions" msgid="3369917736607944781">"Kebenaran apl"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apl yang tidak digunakan"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Edit foto yang dipilih untuk apl ini"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Tiada apl yang tidak digunakan"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 apl yang tidak digunakan"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Keputusan kebenaran terbaharu"</string>
@@ -247,7 +249,7 @@
<string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Ditolak / Tidak pernah mengakses"</string>
<string name="allowed_header" msgid="7769277978004790414">"Dibenarkan"</string>
<string name="allowed_always_header" msgid="6455903312589013545">"Dibenarkan sepanjang masa"</string>
- <string name="allowed_foreground_header" msgid="6845655788447833353">"Dibenarkan hanya semasa dalam penggunaan"</string>
+ <string name="allowed_foreground_header" msgid="6845655788447833353">"Dibenarkan hanya semasa digunakan"</string>
<string name="allowed_storage_scoped" msgid="5383645873719086975">"Dibenarkan mengakses media sahaja"</string>
<string name="allowed_storage_full" msgid="5356699280625693530">"Dibenarkan mengurus semua fail"</string>
<string name="ask_header" msgid="2633816846459944376">"Tanya setiap kali"</string>
@@ -375,7 +377,7 @@
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"ice"</string>
<string name="role_home_label" msgid="3871847846649769412">"Apl skrin utama lalai"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Apl skrin utama"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Apl, yang sering dipanggil pelancar, yang menggantikan Skrin Utama pada peranti Android anda dan memberi anda akses kepada kandungan dan ciri peranti anda"</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Apl, yang sering dipanggil pelancar, yang menggantikan Skrin Utama pada peranti Android dan memberi anda akses kepada kandungan dan ciri peranti anda"</string>
<string name="role_home_request_title" msgid="738136983453341081">"Tetapkan <xliff:g id="APP_NAME">%1$s</xliff:g> sebagai apl skrin utama lalai anda?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Kebenaran tidak diperlukan"</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"pelancar"</string>
diff --git a/PermissionController/res/values-my-v34/strings.xml b/PermissionController/res/values-my-v34/strings.xml
index 4d4e03401..9709684b9 100644
--- a/PermissionController/res/values-my-v34/strings.xml
+++ b/PermissionController/res/values-my-v34/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"လုံခြုံရေးနှင့် ကိုယ်ရေးဒေတာ"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"စက်နှင့်အချက်အလက်လုံခြုံရေး"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"သတ်မှတ်ချက်များ"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"ကျန်းမာရေးဒေတာအတွက် အက်ပ်သုံးခွင့်ကို စီမံနိုင်သည်"</string>
diff --git a/PermissionController/res/values-my/strings.xml b/PermissionController/res/values-my/strings.xml
index f83657838..31997b2b5 100644
--- a/PermissionController/res/values-my/strings.xml
+++ b/PermissionController/res/values-my/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"နောက်ထပ်"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"အားလုံး ခွင့်ပြုရန်"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"အားလုံး အမြဲခွင့်ပြုရန်"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"ကန့်သတ်သုံးခြင်းကို ခွင့်ပြုရန်"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ရွေးထားသော ဓာတ်ပုံနှင့်ဗီဒီယိုများ"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"နောက်ထပ် ရွေးရန်"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"နောက်ထပ်မရွေးပါနှင့်"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"အက်ပ်များ"</string>
<string name="app_permissions" msgid="3369917736607944781">"အက်ပ်ခွင့်ပြုချက်များ"</string>
<string name="unused_apps" msgid="2058057455175955094">"အသုံးမပြုသော အက်ပ်များ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"ဤအက်ပ်အတွက် ရွေးထားသောဓာတ်ပုံများကို ပြင်ရန်"</string>
<string name="no_unused_apps" msgid="12809387670415295">"အသုံးမပြုသောအက်ပ်များ မရှိပါ"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"အသုံးမပြုသောအက်ပ် 0 ခု"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"လတ်တလောခွင့်ပြုသည့် ဆုံးဖြတ်ချက်များ"</string>
@@ -124,7 +126,7 @@
<string name="current_permissions_category" msgid="4292990083585728880">"လက်ရှိ ခွင့်ပြုချက်များ"</string>
<string name="message_staging" msgid="9110563899955511866">"အက်ပ်ကို ပြင်ဆင်နေသည်…"</string>
<string name="app_name_unknown" msgid="1319665005754048952">"အမည်မသိ"</string>
- <string name="permission_usage_title" msgid="1568233336351734538">"အချက်အလက်လုံခြုံမှု ဒက်ရှ်ဘုတ်"</string>
+ <string name="permission_usage_title" msgid="1568233336351734538">"ကိုယ်ရေးဒေတာ ဒက်ရှ်ဘုတ်"</string>
<string name="auto_permission_usage_summary" msgid="7335667266743337075">"ခွင့်ပြုချက်များ လတ်တလောသုံးထားသည့် အက်ပ်ကိုကြည့်နိုင်သည်"</string>
<string name="permission_group_usage_title" msgid="2595013198075285173">"<xliff:g id="PERMGROUP">%1$s</xliff:g> အသုံးပြုမှု"</string>
<string name="perm_usage_adv_info_title" msgid="3357831829538873708">"အခြားခွင့်ပြုချက်များ ကြည့်ပါ"</string>
@@ -347,8 +349,8 @@
<string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ အက်ပ် <xliff:g id="NUM_SERVICES">%s</xliff:g> ခုက သင့်စက်ပစ္စည်းကို အပြည့်အဝ အသုံးပြုခွင့်ရှိသည်"</string>
<string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> သည် သင်၏ မျက်နှာပြင်၊ လုပ်ဆောင်ချက်များ၊ ထည့်သွင်းမှုများကို ကြည့်နိုင်ပြီး လုပ်ဆောင်ချက်များကို ဆောင်ရွက်နိုင်သည့်အပြင် မျက်နှာပြင်သမှုကိုလည်း ထိန်းချုပ်နိုင်သည်။"</string>
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"ဤအက်ပ်များက သင်၏ မျက်နှာပြင်၊ လုပ်ဆောင်ချက်များ၊ ထည့်သွင်းမှုများကို ကြည့်နိုင်ပြီး လုပ်ဆောင်ချက်များကို ဆောင်ရွက်နိုင်သည့်အပြင် မျက်နှာပြင်ပြသမှုကိုလည်း ထိန်းချုပ်နိုင်သည်။"</string>
- <string name="role_assistant_label" msgid="4727586018198208128">"မူလဒစ်ဂျစ်တယ် Assistant အက်ပ်"</string>
- <string name="role_assistant_short_label" msgid="3369003713187703399">"ဒစ်ဂျစ်တယ် Assistant အက်ပ်"</string>
+ <string name="role_assistant_label" msgid="4727586018198208128">"မူရင်း ဒစ်ဂျစ်တယ်အထောက်အကူ အက်ပ်"</string>
+ <string name="role_assistant_short_label" msgid="3369003713187703399">"ဒစ်ဂျစ်တယ်အထောက်အကူ အက်ပ်"</string>
<string name="role_assistant_description" msgid="6622458130459922952">"အကူအညီအက်ပ်များသည် သင်ကြည့်နေသည့် မျက်နှာပြင်မှ အချက်အလက်ကို အခြေခံ၍ ပံ့ပိုးပေးနိုင်ပါသည်။ ဘက်စုံ အထောက်အကူပေးနိုင်ရန်အတွက် အချို့အက်ပ်များသည် စဖွင့်စနစ်နှင့် အသံဖြင့်ထည့်သွင်းဝန်ဆောင်မှု နှစ်ခုလုံးကို ပံ့ပိုးပါသည်။"</string>
<string name="role_browser_label" msgid="2877796144554070207">"မူရင်း ဘရောင်ဇာအက်ပ်"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"ဘရောင်ဇာ အက်ပ်"</string>
@@ -427,12 +429,12 @@
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"လင့်ခ်များကို ဖွင့်ခြင်း"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"အလုပ်အတွက် မူရင်း"</string>
<string name="default_app_none" msgid="9084592086808194457">"မရှိ"</string>
- <string name="default_app_system_default" msgid="6218386768175513760">"(စနစ်မူလ)"</string>
+ <string name="default_app_system_default" msgid="6218386768175513760">"(စနစ်မူရင်း)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"အက်ပ် မရှိပါ"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"ရွေးထားသည်"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"ရွေးထားသည် - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
- <string name="special_app_access_search_keyword" msgid="8032347212290774210">"အထူးအက်ပ်များ ဝင်သုံးခွင့်"</string>
- <string name="special_app_access" msgid="5019319067120213797">"အထူးအက်ပ်များ သုံးခွင့်ရှိသည်"</string>
+ <string name="special_app_access_search_keyword" msgid="8032347212290774210">"အက်ပ် အထူးအသုံးပြုခွင့်"</string>
+ <string name="special_app_access" msgid="5019319067120213797">"အက်ပ် အထူးအသုံးပြုခွင့်"</string>
<string name="no_special_app_access" msgid="6950277571805106247">"အထူးအက်ပ်များ သုံးခွင့်မရှိပါ"</string>
<string name="special_app_access_no_apps" msgid="4102911722787886970">"အက်ပ် မရှိပါ"</string>
<string name="home_missing_work_profile_support" msgid="1756855847669387977">"အလုပ်ပရိုဖိုင်ကို မပံ့ပိုးပါ"</string>
@@ -456,7 +458,7 @@
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"အသံအကူအညီ စတင်ရန် မိုက်ခရိုဖုန်းအသုံးပြုသည့်အခါ သင်္ကေတကို အခြေအနေဘားတွင် ပြပါ"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သင့်စက်ပေါ်ရှိ ဓာတ်ပုံနှင့် မီဒီယာဖိုင်များ ဝင်သုံးခွင့်ပေးလိုပါသလား။"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သင်၏အဆက်အသွယ်များကို သုံးခွင့်ပေးလိုပါသလား။"</string>
- <string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား ဤစက်ပစ္စည်း၏တည်နေရာကို သုံးခွင့်ပေးလိုပါသလား။"</string>
+ <string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား ဤစက်ပစ္စည်း၏တည်နေရာကို သုံးခွင့်ပေးမလား။"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"အက်ပ်ကိုအသုံးပြုသည့် အချိန်တွင်သာ ၎င်းကတည်နေရာကို အသုံးပြုခွင့်ရပါမည်"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား ဤစက်ပစ္စည်း၏တည်နေရာကို သုံးခွင့်ပေးလိုပါသလား။"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"သင် အသုံးမပြုနေလျှင်တောင်မှ ဤအက်ပ်က သင့်တည်နေရာကို သုံးခွင့်ရနေပါမည်။ "<annotation id="link">"ဆက်တင်များတွင် ခွင့်ပြုပါ။"</annotation></string>
@@ -478,20 +480,20 @@
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"ဤစက်ရှိ နောက်ထပ်ဓာတ်ပုံနှင့် ဗီဒီယိုများကို &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သုံးခွင့်ပေးမလား။"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို အသံဖမ်းယူခွင့် ပေးလိုပါသလား။"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ဤအက်ပ်ကို အသုံးပြုနေသည့် အချိန်တွင်သာ ၎င်းက အသံဖမ်းနိုင်သည်။"</string>
- <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို အသံဖမ်းခွင့် ပေးလိုပါသလား။"</string>
+ <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို အသံဖမ်းခွင့် ပေးမလား။"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"ဤအက်ပ်ကို သင်အသုံးမပြုနေလျှင်ပင် ၎င်းက တစ်ချိန်လုံး အသံဖမ်းယူလိုသည်။ "<annotation id="link">"ဆက်တင်များတွင် ခွင့်ပြုပါ။"</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;gt အတွက် မိုက်ခရိုဖုန်း အသုံးပြုခွင့် ပြောင်းမလား။"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"ဤအက်ပ်ကို သင်အသုံးမပြုနေလျှင်ပင် ၎င်းက တစ်ချိန်လုံး အသံဖမ်းလိုသည်။ "<annotation id="link">"ဆက်တင်များတွင် ခွင့်ပြုပါ။"</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သင့်ကိုယ်ခန္ဓာလှုပ်ရှားမှုကို ဝင်ကြည့်ခွင့် ပေးလိုပါသလား။"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား ဓာတ်ပုံနှင့် ဗီဒီယိုရိုက်ကူးခွင့် ပေးလိုပါသလား။"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား ဓာတ်ပုံနှင့် ဗီဒီယိုရိုက်ကူးခွင့် ပေးမလား။"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"ဤအက်ပ်ကို အသုံးပြုနေသည့် အချိန်တွင်သာ ၎င်းက ဓာတ်ပုံနှင့် ဗီဒီယိုများကို ရိုက်ကူးနိုင်သည်။"</string>
- <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို ဓာတ်ပုံနှင့် ဗီဒီယိုရိုက်ကူးခွင့် ပေးလိုပါသလား။"</string>
+ <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို ဓာတ်ပုံနှင့် ဗီဒီယိုရိုက်ကူးခွင့် ပေးမလား။"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"ဤအက်ပ်ကို သင်အသုံးမပြုနေလျှင်ပင် ၎င်းက ဓာတ်ပုံနှင့် ဗီဒီယိုများကို တစ်ချိန်လုံး ရိုက်ကူးလိုသည်။ "<annotation id="link">"ဆက်တင်များတွင် ခွင့်ပြုပါ။"</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;gt အတွက် ကင်မရာအသုံးပြုခွင့် ပြောင်းမလား။"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"ဤအက်ပ်ကို သင်အသုံးမပြုနေလျှင်ပင် ၎င်းက ဓာတ်ပုံနှင့် ဗီဒီယိုများကို တစ်ချိန်လုံး ရိုက်ကူးလိုသည်။ "<annotation id="link">"ဆက်တင်များတွင် ခွင့်ပြုပါ။"</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သင်၏ခေါ်ဆိုထားသော မှတ်တမ်းများကို သုံးခွင့်ပေးလိုပါသလား။"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို ဖုန်းခေါ်ဆိုမှုများ ပြုလုပ်ခွင့်နှင့် စီမံခွင့်ပေးလိုပါသလား။"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သင်၏ အရေးကြီးသော ကျန်းမာရေးလက္ခဏာဆိုင်ရာ အာရုံခံကိရိယာဒေတာ သုံးခွင့်ပေးလိုပါသလား။"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သင့်အဓိကကိုယ်တွင်းအင်္ဂါအခြေအနေ လက္ခဏာများနှင့်ပတ်သက်သည့် အာရုံခံစနစ် ဒေတာကို သုံးခွင့်ပြုမလား။"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"အက်ပ်သုံးမနေသော်လည်း ဤအက်ပ်က သင်၏အရေးကြီးသည့် ကျန်းမာရေးလက္ခဏာဆိုင်ရာ အာရုံခံစနစ်ဒေတာကို အမြဲသုံးခွင့် ရယူလိုသည်။ ဤအပြောင်းအလဲကို ပြုလုပ်ရန် "<annotation id="link">"ဆက်တင်များသို့ သွားပါ။"</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သင်၏ အရေးကြီးသော ကျန်းမာရေးလက္ခဏာဆိုင်ရာ အာရုံခံစနစ်ဒေတာကို သုံးခွင့်ပေးလိုပါသလား။"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"အက်ပ်သုံးမနေသော်လည်း ဤအက်ပ်ကို ခန္ဓာကိုယ်အာရုံခံစနစ် ဒေတာများ အမြဲသုံးခွင့်ပြုရန် "<annotation id="link">"ဆက်တင်များသို့ သွားပါ။"</annotation></string>
@@ -520,7 +522,7 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"အက်ပ်နှင့် ဝန်ဆောင်မှုများအတွက်"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"အရေးပေါ် နံပါတ်ကို သင်ခေါ်ဆိုချိန်တွင် မိုက်ခရိုဖုန်းဒေတာကို မျှဝေနိုင်သေးသည်။"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"ပြောင်းရန်"</string>
- <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"လုံခြုံရေးနှင့် ကိုယ်ရေးဒေတာ"</string>
+ <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"စက်နှင့်အချက်အလက်လုံခြုံရေး"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"စက်ကိုစစ်ဆေးရန်"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"ပယ်ရန်"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"ဤသတိပေးချက်ကို ပယ်မလား။"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"လုံခြုံရေးနှင့် ကိုယ်ရေးအချက်အလက်လုံခြုံမှု အခြေအနေ။ <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>။ <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"လုံခြုံရေး ဆက်တင်များ"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"ခွင့်ပြုချက်များ"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"လုံခြုံရေးနှင့် ကိုယ်ရေးဒေတာ"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"စက်နှင့်အချက်အလက်လုံခြုံရေး"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"အခြေအနေ စစ်ဆေးပါ"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"အချက်အလက်လုံခြုံမှု ဆက်တင်များ"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"နောက်ထပ်ဆက်တင်များ"</string>
@@ -576,7 +578,7 @@
<string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"ဤအက်ပ်ကို ပိတ်ထားချိန်၌ပင် ၎င်းသည် သင့်တည်နေရာကို အမြဲကြည့်နိုင်သည်။\n\nလုံခြုံရေးနှင့် အရေးပေါ်အက်ပ်အချို့သည် ရည်ရွယ်ထားသည့်အတိုင်း အလုပ်လုပ်ရန် နောက်ခံတွင် သင့်တည်နေရာကြည့်ခွင့် လိုအပ်သည်။"</string>
<string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"အသုံးပြုခွင့် ပြောင်းသွားသည်"</string>
<string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"မကြာသေးမီက တည်နေရာအသုံးပြုမှုကို ကြည့်ရန်"</string>
- <string name="privacy_controls_title" msgid="7605929972256835199">"ကန့်သတ်ရန်ဆက်တင်များ"</string>
+ <string name="privacy_controls_title" msgid="7605929972256835199">"ကိုယ်ရေးအချက်အလက်လုံခြုံမှု ဆက်တင်များ"</string>
<string name="camera_toggle_title" msgid="1251201397431837666">"ကင်မရာသုံးခွင့်"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"မိုက်ခရိုဖုန်းသုံးခွင့်"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"အက်ပ်နှင့် ဝန်ဆောင်မှုများအတွက်"</string>
diff --git a/PermissionController/res/values-nb/strings.xml b/PermissionController/res/values-nb/strings.xml
index 1e5f58453..45a527fc7 100644
--- a/PermissionController/res/values-nb/strings.xml
+++ b/PermissionController/res/values-nb/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mer info"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Tillat alle"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Tillat alltid alle"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Gi begrenset tilgang"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Velg bilder og videoer"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Velg flere"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Ikke velg flere"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apper"</string>
<string name="app_permissions" msgid="3369917736607944781">"Apptillatelser"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ubrukte apper"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Endre bilder som er valgt for denne appen"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ingen ubrukte apper"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ubrukte apper"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nylige tillatelsesavgjørelser"</string>
@@ -427,7 +429,7 @@
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Åpning av linker"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Jobbstandard"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ingen"</string>
- <string name="default_app_system_default" msgid="6218386768175513760">"(Systemstandard)"</string>
+ <string name="default_app_system_default" msgid="6218386768175513760">"(System-&amp;shy;standard)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Ingen apper"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Valgt"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Valgt – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-ne/strings.xml b/PermissionController/res/values-ne/strings.xml
index c418348bd..f17adcbcd 100644
--- a/PermissionController/res/values-ne/strings.xml
+++ b/PermissionController/res/values-ne/strings.xml
@@ -27,13 +27,14 @@
<string name="off" msgid="1438489226422866263">"निष्क्रिय"</string>
<string name="uninstall_or_disable" msgid="4496612999740858933">"अनइन्स्टल गर्नुहोस् वा असक्षम पार्नुहोस्"</string>
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"एप फेला परेन"</string>
- <string name="grant_dialog_button_deny" msgid="88262611492697192">"अनुमति नदिइयोस्"</string>
+ <string name="grant_dialog_button_deny" msgid="88262611492697192">"अनुमति नदिनुहोस्"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"अनुमति नदिइनुहोस् र फेरि नसोध्नुहोस्"</string>
- <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“एप प्रयोगमा भएको बेलामा” शीर्षक कायम राख्नुहोस्"</string>
+ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“एप प्रयोग भइरहेका बेला” भन्ने अनुमति कायमै राख्नुहोस्"</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“यस बेला मात्र” राख्नुहोस्"</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"थप जानकारी"</string>
- <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"सबै डेटा प्रयोग गर्ने अनुमति दिनुहोस्"</string>
- <string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"सधैँ सबै अनुमति दिइयोस्"</string>
+ <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"सबै अनुमति दिनुहोस्"</string>
+ <string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"सधैँ सबै अनुमति दिनुहोस्"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"सीमित अनुमति दिनुहोस्"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"फोटो र भिडियोहरू चयन गर्नुहोस्"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"अझ धेरै फोटो चयन गर्नुहोस्"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"थप डेटा चयन नगर्नुहोस्"</string>
@@ -44,22 +45,23 @@
<string name="permission_add_background_warning_template" msgid="1812914855915092273">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई सधैँ <xliff:g id="ACTION">%2$s</xliff:g> अनुमति दिने हो?"</string>
<string name="allow_permission_foreground_only" msgid="116465816039675404">"एप प्रयोग गर्दा मात्र"</string>
<string name="allow_permission_always" msgid="5194342531206054051">"सधैँ"</string>
- <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"अनुमति नदिइयोस् र फेरि नसोधियोस्"</string>
+ <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"अनुमति नदिनुहोस् र फेरि नसोध्नुहोस्"</string>
<string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g> अनुमति असक्षम पारिएको छ"</string>
<string name="permission_revoked_all" msgid="3397649017727222283">"सबै अनुमति असक्षम पारिएको छ"</string>
<string name="permission_revoked_none" msgid="9213345075484381180">"कुनै पनि असक्षम पारिएको छैन"</string>
- <string name="grant_dialog_button_allow" msgid="5314677880021102550">"अनुमति दिइयोस्"</string>
- <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"सधैँ अनुमति दिइयोस्"</string>
+ <string name="grant_dialog_button_allow" msgid="5314677880021102550">"अनुमति दिनुहोस्"</string>
+ <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"सधैँ अनुमति दिनुहोस्"</string>
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"एप चलाएका बेला"</string>
- <string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"सटीक लोकेसन प्रयोग गरियोस्"</string>
- <string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"अनुमानित लोकेसन मात्र प्रयोग गरियोस्"</string>
+ <string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"सटीक लोकेसन प्रयोग गर्न दिनुहोस्"</string>
+ <string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"अनुमानित लोकेसन मात्र प्रयोग गर्न दिनुहोस्"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"यस पटक मात्र"</string>
- <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"सधैँ अनुमति दिइयोस्"</string>
- <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"सबै फाइलहरू व्यवस्थापन गर्ने अनुमति दिइयोस्"</string>
- <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"मिडिया फाइल प्रयोग गर्ने अनुमति दिइयोस्"</string>
+ <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"सधैँ अनुमति दिनुहोस्"</string>
+ <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"सबै फाइलहरू व्यवस्थापन गर्ने अनुमति दिनुहोस्"</string>
+ <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"मिडिया फाइल प्रयोग गर्ने अनुमति दिनुहोस्"</string>
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"एपहरू"</string>
<string name="app_permissions" msgid="3369917736607944781">"एपसम्बन्धी अनुमति"</string>
<string name="unused_apps" msgid="2058057455175955094">"प्रयोग नगरिएका एपहरू"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"यो एपका लागि चयन गरिएका फोटोहरू सम्पादन गर्नुहोस्"</string>
<string name="no_unused_apps" msgid="12809387670415295">"सबै एप चलाइएका छन्"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"प्रयोग नगरिएका एउटा पनि एप छैन"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"हालै दिइएका अनुमतिसम्बन्धी निर्णयहरू"</string>
@@ -70,7 +72,7 @@
<string name="denied_permission_decision" msgid="5308961501779563781">"तपाईंले <xliff:g id="PERMISSION_NAME">%2$s</xliff:g> प्रयोग गर्न <xliff:g id="APP_NAME">%1$s</xliff:g> ले मागेको अनुमति अस्वीकार गर्नुभएको छ"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{आज}=1{१ दिनअघि}other{# दिनअघि}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"एप असक्षम पार्नुहोस्"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"तपाईंले यो एप असक्षम पार्नुभयो भने Android र अन्य एपहरूले अब उप्रान्त अपेक्षाअनुसार कार्य नगर्न सक्छन्। स्मरण रहोस्, तपाईं यो एप तपाईंको यन्त्रसँग पहिल्यै स्थापना भएर आएको हुँदा तपाईं यसलाई मेटाउन सक्नुहुन्न। यो एप असक्षम पारेर, तपाईं यसलाई निष्क्रिय पार्नुहुन्छ तथा यसलाई आफ्नो डिभाइसमा लुकाउनुहुन्छ।"</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"तपाईंले यो एप असक्षम पार्नुभयो भने Android र अन्य एपले अब उप्रान्त अपेक्षाअनुसार काम नगर्न सक्छन्। स्मरण रहोस्, तपाईं यो एप तपाईंको डिभाइसमा पहिल्यै इन्स्टल भएर आएको हुँदा तपाईं यसलाई मेटाउन सक्नुहुन्न। यो एप असक्षम पारेर, तपाईं यसलाई निष्क्रिय पार्न र आफ्नो डिभाइसमा लुकाउन मात्र सक्नुहुन्छ।"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"अनुमति व्यवस्थापन"</string>
<string name="never_ask_again" msgid="4728762438198560329">"फेरि नसोध्नुहोला"</string>
<string name="no_permissions" msgid="3881676756371148563">"अनुमति दिन भनी कुनै अनुरोध छैन"</string>
@@ -83,10 +85,10 @@
<string name="default_permission_description" msgid="4624464917726285203">"कुनै अज्ञात कारबाही गर्नुहोस्"</string>
<string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_1">%2$d</xliff:g> मध्ये <xliff:g id="COUNT_0">%1$d</xliff:g> एपहरूलाई अनुमति दिइएको छ"</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> एपहरूलाई अनुमति दिइएको छ"</string>
- <string name="menu_show_system" msgid="4254021607027872504">"सिस्टम देखाइयोस्"</string>
- <string name="menu_hide_system" msgid="3855390843744028465">"सिस्टम लुकाइयोस्"</string>
- <string name="menu_show_7_days_data" msgid="8979611198508523706">"पछिल्ला ७ दिनमा कुन कुन समयमा अनुमति प्रयोग गरियो भन्ने कुरा देखाइयोस्"</string>
- <string name="menu_show_24_hours_data" msgid="8228054833323380780">"पछिल्लो २४ घण्टामा प्रयोग गरिएका अनुमतिहरू देखाइयोस्"</string>
+ <string name="menu_show_system" msgid="4254021607027872504">"सिस्टम देखाउनुहोस्"</string>
+ <string name="menu_hide_system" msgid="3855390843744028465">"सिस्टम लुकाउनुहोस्"</string>
+ <string name="menu_show_7_days_data" msgid="8979611198508523706">"पछिल्ला ७ दिनमा कुन कुन समयमा अनुमति प्रयोग गरियो भन्ने कुरा देखाउनुहोस्"</string>
+ <string name="menu_show_24_hours_data" msgid="8228054833323380780">"पछिल्लो २४ घण्टामा प्रयोग गरिएका अनुमतिहरू देखाउनुहोस्"</string>
<string name="manage_permission" msgid="2895385393037061964">"अनुमति व्यवस्थापन गर्नुहोस्"</string>
<string name="no_apps" msgid="2412612731628386816">"कुनै पनि एप छैन"</string>
<string name="location_settings" msgid="3624412509133422562">"लोकेसन सेटिङ"</string>
@@ -107,8 +109,8 @@
<!-- no translation found for background_access_chooser_dialog_choices:0 (1351721623256561996) -->
<!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) -->
<!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) -->
- <string name="permission_access_always" msgid="1474641821883823446">"सधैँ अनुमति दिइयोस्"</string>
- <string name="permission_access_only_foreground" msgid="7801170728159326195">"यो एप प्रयोग गरिरहेका बेला मात्र अनुमति दिइयोस्"</string>
+ <string name="permission_access_always" msgid="1474641821883823446">"सधैँ अनुमति दिनुहोस्"</string>
+ <string name="permission_access_only_foreground" msgid="7801170728159326195">"यो एप प्रयोग गरिरहेका बेला मात्र अनुमति दिनुहोस्"</string>
<string name="permission_access_never" msgid="4647014230217936900">"अनुमति नदिनुहोस्"</string>
<string name="loading" msgid="4789365003890741082">"लोड गर्दै…"</string>
<string name="all_permissions" msgid="6911125611996872522">"सबै अनुमति"</string>
@@ -183,28 +185,28 @@
<string name="app_permission_usage_title" msgid="6676802437831981822">"एपको अनुमतिको उपयोग"</string>
<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_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>
- <string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"सधैँ सबै अनुमति दिइयोस्"</string>
- <string name="app_permission_button_ask" msgid="3342950658789427">"प्रत्येक पटक सोधियोस्"</string>
- <string name="app_permission_button_deny" msgid="6016454069832050300">"अनुमति नदिइयोस्"</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_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>
+ <string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"सधैँ सबै अनुमति दिनुहोस्"</string>
+ <string name="app_permission_button_ask" msgid="3342950658789427">"प्रत्येक पटक सोध्नुहोस्"</string>
+ <string name="app_permission_button_deny" msgid="6016454069832050300">"अनुमति नदिनुहोस्"</string>
<string name="precise_image_description" msgid="6349638632303619872">"सटीक स्थान"</string>
<string name="approximate_image_description" msgid="938803699637069884">"अनुमानित स्थान"</string>
- <string name="app_permission_location_accuracy" msgid="7166912915040018669">"सटीक लोकेसन प्रयोग गरियोस्"</string>
- <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"सटीक लोकेसन अफ हुँदा एपहरूलाई मेरो अनुमानित लोकेसन प्रयोग गर्न दिइयोस्"</string>
+ <string name="app_permission_location_accuracy" msgid="7166912915040018669">"सटीक लोकेसन प्रयोग गर्नुहोस्"</string>
+ <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"सटीक लोकेसन अफ हुँदा एपहरूलाई मेरो अनुमानित लोकेसन प्रयोग गर्न दिनुहोस्"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g>सम्बन्धी अनुमति"</string>
<string name="app_permission_header" msgid="2951363137032603806">"यो एप <xliff:g id="PERM">%1$s</xliff:g> प्रयोग गर्ने अनुमति दिने वा नदिने"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"<xliff:g id="APP">%1$s</xliff:g> सँग भएका सबै अनुमति हेर्नुहोस्"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"यो अनुमति पाएका सबै एपहरू हेर्नुहोस्"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"सहायकको माइक्रोफोन प्रयोगसम्बन्धी डेटा देखाउनुहोस्"</string>
<string name="unused_apps_category_title" msgid="2988455616845243901">"प्रयोग नगरिएको एपसम्बन्धी सेटिङ"</string>
- <string name="auto_revoke_label" msgid="5068393642936571656">"यो एप प्रयोग नहुँदा यसलाई दिइएका अनुमतिहरू रद्द गरियोस्"</string>
- <string name="unused_apps_label" msgid="2595428768404901064">"अनुमतिहरू हटाई ठाउँ खाली गरियोस्"</string>
- <string name="unused_apps_label_v2" msgid="7058776770056517980">"एप प्रयोग नगरिएको अवस्थामा उक्त एपमा बिताएको समय रेकर्ड नगरियोस्"</string>
- <string name="unused_apps_summary" msgid="8839466950318403115">"अनुमतिहरू रद्द गरियोस्, अस्थायी फाइलहरू मेटाइयोस् र एपसम्बन्धी सूचना नपठाइयोस्"</string>
+ <string name="auto_revoke_label" msgid="5068393642936571656">"यो एप प्रयोग नहुँदा यसलाई दिइएका अनुमतिहरू रद्द गर्नुहोस्"</string>
+ <string name="unused_apps_label" msgid="2595428768404901064">"अनुमतिहरू हटाई ठाउँ खाली गर्नुहोस्"</string>
+ <string name="unused_apps_label_v2" msgid="7058776770056517980">"एप प्रयोग नगरिएको अवस्थामा उक्त एपमा गरिएको गतिविधि रेकर्ड नगर्नुहोस्"</string>
+ <string name="unused_apps_summary" msgid="8839466950318403115">"अनुमतिहरू रद्द गर्नुहोस्, अस्थायी फाइलहरू मेटाउनुहोस् र एपसम्बन्धी सूचना नपठाउनुहोस्"</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_revoked_apps_page_summary" msgid="6594753657893756536">"तपाईंका डेटाको सुरक्षार्थ तपाईंले केही महिनादेखि प्रयोग नगरेका एपलाई दिइएका अनुमति रद्द गरिएका छन्।"</string>
@@ -214,7 +216,7 @@
<string name="auto_revocable_permissions_one" msgid="5299112369449458176">"<xliff:g id="PERM">%1$s</xliff:g> सम्बन्धी अनुमति हटाइने छ।"</string>
<string name="auto_revocable_permissions_two" msgid="4874067408752041716">"<xliff:g id="PERM_0">%1$s</xliff:g> र <xliff:g id="PERM_1">%2$s</xliff:g> सम्बन्धी अनुमतिहरू हटाइने छन्।"</string>
<string name="auto_revocable_permissions_many" msgid="1521807896206032992">"निम्न अनुमतिहरू हटाइने छन्: <xliff:g id="PERMS">%1$s</xliff:g>।"</string>
- <string name="auto_manage_title" msgid="7693181026874842935">"अनुमतिहरू स्वतः व्यवस्थापन गरियोस्"</string>
+ <string name="auto_manage_title" msgid="7693181026874842935">"अनुमतिहरू स्वतः व्यवस्थापन गर्नुहोस्"</string>
<string name="auto_revoked_app_summary_one" msgid="7093213590301252970">"<xliff:g id="PERMISSION_NAME">%s</xliff:g> सम्बन्धी अनुमति हटाइयो"</string>
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"<xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g> र <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g> सम्बन्धी अनुमतिहरू हटाइए"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> र अन्य <xliff:g id="NUMBER">%2$s</xliff:g> अनुमतिहरू हटाइए"</string>
@@ -250,7 +252,7 @@
<string name="allowed_foreground_header" msgid="6845655788447833353">"प्रयोग भइरहेका बेला मात्र अनुमति दिइने"</string>
<string name="allowed_storage_scoped" msgid="5383645873719086975">"मिडिया मात्र प्रयोग गर्ने अनुमति दिइएको"</string>
<string name="allowed_storage_full" msgid="5356699280625693530">"सबै फाइल व्यवस्थापन गर्ने अनुमति दिइएको"</string>
- <string name="ask_header" msgid="2633816846459944376">"प्रत्येक पटक सोधियोस्"</string>
+ <string name="ask_header" msgid="2633816846459944376">"प्रत्येक पटक सोध्नुहोस्"</string>
<string name="denied_header" msgid="903209608358177654">"अनुमति नदिइएका"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"सबै फाइल हेर्ने र प्रयोग गर्ने अनुमति भएका थप एपहरू हेर्नुहोस्"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{१ दिन}other{# दिन}}"</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"यी एपहरूले तपाईंको स्क्रिन, कारबाही र इनपुट हेर्न, कारबाहीहरू सम्पादन गर्न र प्रदर्शन नियन्त्रण गर्न सक्छन्।"</string>
<string name="role_assistant_label" msgid="4727586018198208128">"डिफल्ट डिजिटल सहायक एप"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"डिजिटल सहायक एप"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"सहायक एपहरूले तपाईंले हेर्दै गर्नुभएको स्क्रिनबाट प्राप्त जानकारीमा आधारित भई तपाईंलाई मद्दत गर्न सक्छन्।केही एपहरूले तपाईंलाई एकीकृत सहायता दिन दुवै लन्चर र आवाज संलग्न इनपुट सेवाहरूलाई समर्थन गर्दछन्।"</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"सहायक एपहरूले तपाईंले हेर्दै गर्नुभएको स्क्रिनबाट प्राप्त जानकारीमा आधारित भई तपाईंलाई मद्दत गर्न सक्छन्। केही एपहरूले तपाईंलाई एकीकृत सहायता दिन दुवै लन्चर र आवाज संलग्न इनपुट सेवाहरूलाई समर्थन गर्छन्।"</string>
<string name="role_browser_label" msgid="2877796144554070207">"डिफल्ट ब्राउजर एप"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"ब्राउजर"</string>
<string name="role_browser_description" msgid="3465253637499842671">"तपाईंलाई इन्टरनेट चलाउने दिने र तपाईंले ट्याप गर्ने लिंकहरू देखाउने एपहरू"</string>
@@ -427,7 +429,7 @@
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"लिंकहरू खोल्दा"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"कार्यका लागि डिफल्ट"</string>
<string name="default_app_none" msgid="9084592086808194457">"कुनै पनि होइन"</string>
- <string name="default_app_system_default" msgid="6218386768175513760">"(डिफल्ट सिस्टम एप)"</string>
+ <string name="default_app_system_default" msgid="6218386768175513760">"(सिस्टम डिफल्ट)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"कुनै पनि एप छैन"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"चयन गरिएको"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"चयन गरिएको - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
@@ -443,19 +445,19 @@
<string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> डिबग प्रक्रियासम्बन्धी जानकारी अपलोड गर्न चाहन्छ।"</string>
<string name="incident_report_dialog_title" msgid="669104389325204095">"डिबग प्रक्रियासम्बन्धी डेटा सेयर गर्ने हो?"</string>
<string name="incident_report_dialog_intro" msgid="5897733669850951832">"प्रणालीले कुनै समस्या फेला पारेको छ"</string>
- <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ले <xliff:g id="DATE">%2$s</xliff:g> <xliff:g id="TIME">%3$s</xliff:g> मा यो यन्त्रबाट लिएको बगको रिपोर्ट अपलोड गर्ने अनुरोध गर्दै छ। बग रिपोर्टमा प्रयोगकर्ताका नाम, स्ठानसम्बन्धी डेटा, डिभाइसका पहिचानकर्ता र नेटवर्कसम्बन्धी जानकारी जस्ता तपाईंको डिभाइसको व्यक्तिगत जानकारी वा अनुप्रयोगले लग गरेको जानकारी समावेश छ। तपाईंलाई यो जानकारी दिँदा फरक पर्दैन जस्तो लाग्ने विश्वसनीय मान्छे वा एपसँग मात्र बग रिपोर्टहरू सेयर गर्नुहोस्। <xliff:g id="APP_NAME_1">%4$s</xliff:g> लाई बग रिपोर्ट अपलोड गर्न दिने हो?"</string>
+ <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ले <xliff:g id="DATE">%2$s</xliff:g> <xliff:g id="TIME">%3$s</xliff:g> मा यो यन्त्रबाट लिएको बगको रिपोर्ट अपलोड गर्ने अनुरोध गर्दै छ। बग रिपोर्टमा प्रयोगकर्ताका नाम, स्ठानसम्बन्धी डेटा, डिभाइसका पहिचानकर्ता र नेटवर्कसम्बन्धी जानकारी जस्ता तपाईंको डिभाइसको व्यक्तिगत जानकारी वा एपले लग गरेको जानकारी समावेश छ। तपाईंलाई यो जानकारी दिँदा फरक पर्दैन जस्तो लाग्ने विश्वसनीय मान्छे वा एपसँग मात्र बग रिपोर्टहरू सेयर गर्नुहोस्। <xliff:g id="APP_NAME_1">%4$s</xliff:g> लाई बग रिपोर्ट अपलोड गर्न दिने हो?"</string>
<string name="incident_report_error_dialog_text" msgid="4189647113387092272">"<xliff:g id="APP_NAME">%1$s</xliff:g> को बगसम्बन्धी रिपोर्ट प्रक्रियामा लैजाने क्रममा त्रुटि भयो। त्यस कारण विस्तृत डिबग प्रक्रियासम्बन्धी डेटा आदान प्रदान गर्ने कार्य अस्वीकार गरिएको छ। व्यवधानका लागि क्षमा गर्नुहोला।"</string>
<string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"अनुमति दिनुहोस्"</string>
<string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"अनुमति नदिनुहोस्"</string>
<string name="adjust_user_sensitive_title" msgid="4196724451314280527">"उन्नत सेटिङहरू"</string>
<string name="menu_adjust_user_sensitive" msgid="6497923610654425780">"उन्नत सेटिङहरू"</string>
<string name="adjust_user_sensitive_globally_title" msgid="8649190949066029174">"प्रणालीका एपको प्रयोगसम्बन्धी जानकारी देखाउनुहोस्"</string>
- <string name="adjust_user_sensitive_globally_summary" msgid="129467818433773912">"प्रणालीका अनुप्रयोगले गर्ने अनुमतिको प्रयोगसम्बन्धी जानकारी स्टाटस बार, ड्यासबोर्ड र अन्यत्र देखाउनुहोस्"</string>
+ <string name="adjust_user_sensitive_globally_summary" msgid="129467818433773912">"प्रणालीका एपले गर्ने अनुमतिको प्रयोगसम्बन्धी जानकारी स्टाटस बार, ड्यासबोर्ड र अन्यत्र देखाउनुहोस्"</string>
<string name="adjust_user_sensitive_per_app_header" msgid="4543506440989005648">"निम्न एपको प्रयोगसम्बन्धी जानकारी हाइलाइट गर्नुहोस्"</string>
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"सहायक ट्रिगर भएको पत्ता लागेमा देखाउनुहोस्"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"आवाज सहायक सक्रिय गर्न माइक्रोफोनको प्रयोग गरिँदा स्टाटस बारमा आइकन देखाउनुहोस्"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई आफ्नो डिभाइसका फोटो र मिडियामाथि पहुँच राख्न दिने हो?"</string>
- <string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई कन्ट्याक्ट प्रयोग गर्न दिने हो?"</string>
+ <string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई तपाईंका कन्ट्याक्टहरू एक्सेस गर्ने अनुमति दिने हो?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो डिभाइसको लोकेसन प्रयोग दिने हो?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"तपाईंले एप प्रयोग गरिरहेका बेला मात्र उक्त एपले स्थानमाथि पहुँच राख्न सक्ने छ"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो डिभाइसको लोकेसन प्रयोग दिने हो?"</string>
@@ -468,13 +470,13 @@
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो डिभाइसको अनुमानित लोकेसन प्रयोग गर्न दिने हो?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"सटीक"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"अनुमानित"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई आफ्नो पात्रोमाथि पहुँच राख्न दिने हो?"</string>
- <string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; SMS म्यासेज पठाउन र हेर्न दिने हो?"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई तपाईंको पात्रो एक्सेस गर्ने अनुमति दिने हो?"</string>
+ <string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई SMS म्यासेज पठाउने र हेर्ने अनुमति दिने हो?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई आफ्नो डिभाइसमा रहेका फोटो, मिडिया र फाइलहरू प्रयोग गर्न दिने हो?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो डिभाइसमा रहेका &lt;b&gt;फोटो, भिडियो, सङ्गीत र अडियो&lt;/b&gt; प्रयोग गर्न दिने हो?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो डिभाइसमा रहेका &lt;b&gt;फोटो, भिडियो, सङ्गीत, अडियो तथा अन्य फाइलहरू&lt;/b&gt; प्रयोग गर्न दिने हो?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यस डिभाइसमा रहेका सङ्गीत तथा अन्य अडियो फाइलहरू प्रयोग गर्न दिने हो?"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यस डिभाइसमा रहेका फोटो र भिडियोहरू प्रयोग गर्न दिने हो?"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यस डिभाइसमा रहेका फोटो र भिडियोहरू एक्सेस गर्ने अनुमति दिने हो?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो डिभाइसमा भएका थप फोटो तथा भिडियोहरू प्रयोग गर्न दिने हो?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई अडियो रेकर्ड गर्न दिने हो?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"तपाईंले यो एप प्रयोग गरिरहेका बेलामा मात्र यसले अडियो रेकर्ड गर्न सक्ने छ"</string>
@@ -482,21 +484,21 @@
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"तपाईंले यो एप प्रयोग नगरेका बेलामा पनि यसले अडियो रेकर्ड गर्न सक्छ। "<annotation id="link">"सेटिङमा गई यो अनुमति दिनुहोस्।"</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई माइक्रोफोन प्रयोग गर्न दिइएको अनुमति परिवर्तन गर्ने हो?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"तपाईंले यो एप प्रयोग नगरेका बेलामा पनि यसले अडियो रेकर्ड गर्न चाहन्छ। "<annotation id="link">"सेटिङमा गई यो अनुमति दिनुहोस्।"</annotation></string>
- <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई आफ्नो शारीरिक क्रियाकलाप प्रयोग गर्न दिने हो?"</string>
+ <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई तपाईंको शारीरिक क्रियाकलाप एक्सेस गर्ने अनुमति दिने हो?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई फोटो खिच्न र भिडियो रेकर्ड गर्न दिने हो?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"तपाईंले यो एप प्रयोग गरिरहेका बेलामा मात्र यसले फोटो खिच्न र भिडियो रेकर्ड गर्न सक्ने छ"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई फोटो खिच्न र भिडियो रेकर्ड गर्न दिने हो?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"तपाईंले यो एप प्रयोग नगरेका बेलामा पनि यसले फोटो खिच्न तथा भिडियो रेकर्ड गर्न सक्छ। "<annotation id="link">"सेटिङमा गई यो अनुमति दिनुहोस्।"</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई क्यामेरा प्रयोग गर्न दिइएको अनुमति परिवर्तन गर्ने हो?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"तपाईंले यो एप प्रयोग नगरेका बेलामा पनि यसले फोटो खिच्न तथा भिडियो रेकर्ड गर्न चाहन्छ। "<annotation id="link">"सेटिङमा गई यो अनुमति दिनुहोस्।"</annotation></string>
- <string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई आफ्ना कल लग प्रयोग गर्ने अनुमति दिने हो?"</string>
- <string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई फोन कल गर्न र ती कलको व्यवस्थापन गर्न दिने हो?"</string>
+ <string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई तपाईंका कल लग एक्सेस गर्ने अनुमति दिने हो?"</string>
+ <string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई फोन कल गर्ने र ती कल व्यवस्थापन गर्ने अनुमति दिने हो?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई नाडी आदि जस्ता महत्त्वपूर्ण संकेतसम्बन्धी सेन्सर डेटा हेर्ने अनुमति दिने हो?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"यो एप जुनसुकै बेला (तपाईंले एप प्रयोग नगरेका बेलासमेत) तपाईंका नाडी, धड्कज जस्ता सेन्सर डेटा हेर्न र प्रयोग गर्न चाहन्छ।। यस्तो परिवर्तन गर्न "<annotation id="link">"सेटिङमा जानुहोस्।"</annotation></string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"यो एप जुनसुकै बेला (तपाईंले एप प्रयोग नगरेका बेलासमेत) तपाईंका नाडी, धड्कन जस्ता सेन्सर डेटा हेर्न र प्रयोग गर्न चाहन्छ।। यस्तो परिवर्तन गर्न "<annotation id="link">"सेटिङमा जानुहोस्।"</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई नाडी, धड्कन जस्ता सेन्सर डेटा प्रयोग गर्न र हेर्न दिने हो?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"यो एपलाई जुनसुकै बेला (तपाईंले एप नचलाएका बेलासमेत) बडी सेन्सरसम्बन्धी डेटा हेर्न र प्रयोग गर्न दिन "<annotation id="link">"सेटिङमा जानुहोस्।"</annotation></string>
- <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"यो एप प्रयोग गरिँदै गरेका बेला यसलाई &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; बडी सेन्सरसम्बन्धी डेटा हेर्ने र प्रयोग गर्ने अनुमति दिइरहने हो?"</string>
- <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई तपाईंलाई सूचना पठाउन दिने हो?"</string>
+ <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; प्रयोग गरिरहेका बेला यसलाई बडी सेन्सरसम्बन्धी डेटा हेर्ने र प्रयोग गर्ने अनुमति दिइरहने हो?"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई तपाईंलाई नोटिफिकेसन पठाउने अनुमति दिने हो?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"नियन्त्रित अनुमतिहरू"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> लाई लोकेसन प्रयोग गर्ने अनुमति दिइएको छ"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"तपाईंको सङ्गठनले <xliff:g id="APP_NAME">%1$s</xliff:g> लाई तपाईंको लोकेसन प्रयोग गर्ने अनुमति दिएको छ"</string>
@@ -536,7 +538,7 @@
<string name="privacy_controls_qs" msgid="5780144882040591169">"तपाईंका गोपनीयतासम्बन्धी सेटिङ"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"थप सेटिङ"</string>
<string name="camera_toggle_label_qs" msgid="3880261453066157285">"क्यामेरा प्रयोग गर्ने अनुमति"</string>
- <string name="microphone_toggle_label_qs" msgid="8132912469813396552">"माइक प्रयोग गर्ने अनुमति"</string>
+ <string name="microphone_toggle_label_qs" msgid="8132912469813396552">"माइक एक्सेस"</string>
<string name="permissions_removed_qs" msgid="8957319130625294572">"अनुमति हटाइएको छ"</string>
<string name="camera_usage_qs" msgid="4394233566086665994">"हालसालै गरिएको क्यामेराको प्रयोगसम्बन्धी जानकारी हेर्नुहोस्"</string>
<string name="microphone_usage_qs" msgid="8527666682168170417">"हालसालै गरिएको माइकको प्रयोगसम्बन्धी जानकारी हेर्नुहोस्"</string>
@@ -573,7 +575,7 @@
<string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"ब्याकग्राउन्डमा लोकेसन प्रयोग गर्ने अनुमति दिइएको एपको समीक्षा गर्नुहोस्"</string>
<string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> बन्द हुँदा पनि यो एपले जुनसुकै बेला तपाईंको लोकेसन प्रयोग गर्न सक्छ"</string>
<string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"ब्याकग्राउन्डमा लोकेसन प्रयोग गर्ने अनुमति दिइएको एपको समीक्षा गर्नुहोस्"</string>
- <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"यो एप बन्द हुँदा पनि यसले जुनसुकै बेला तपाईंको लोकेसन प्रयोग गर्न सक्छ।\n\nसुरक्षा तथा आपत्कालीन प्रयोजनका लागि बनाइएका केही एपहरूले राम्रोसँग काम गर्नका निम्ति तपाईंले ती एपहरूलाई ब्याकग्राउन्डमा तपाईंको लोकेसन प्रयोग गर्ने अनुमति दिनु पर्ने हुन्छ।"</string>
+ <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"यो एप बन्द हुँदा पनि यसले जुनसुकै बेला तपाईंको लोकेसन प्रयोग गर्न सक्छ।\n\nसुरक्षा तथा आपत्‌कालीन प्रयोजनका लागि बनाइएका केही एपहरूले राम्रोसँग काम गर्नका निम्ति तपाईंले ती एपहरूलाई ब्याकग्राउन्डमा तपाईंको लोकेसन प्रयोग गर्ने अनुमति दिनु पर्ने हुन्छ।"</string>
<string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"अनुमति बदलियो"</string>
<string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"हालसालै कहिले लोकेसनसम्बन्धी प्रयोग गरिएको थियो हेर्नुहोस्"</string>
<string name="privacy_controls_title" msgid="7605929972256835199">"गोपनीयतासम्बन्धी सेटिङ"</string>
@@ -582,10 +584,10 @@
<string name="perm_toggle_description" msgid="7801326363741451379">"एप तथा सेवाहरूका हकमा"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"एप तथा सेवाहरूका हकमा। यो सेटिङ अफ गरिएको अवस्थामा तपाईंले आपत्‍कालीन नम्बरमा कल गर्नुभयो भने माइक्रोफोनसम्बन्धी डेटा अझै पनि सेयर गरिन सक्छ।"</string>
<string name="location_settings_subtitle" msgid="2328360561197430695">"लोकेसन प्रयोग गर्ने अनुमति दिइएका एप तथा सेवाहरू हेर्नुहोस्"</string>
- <string name="show_clip_access_notification_title" msgid="5168467637351109096">"क्लिपबोर्डका सामग्री प्रयोग गरिँदा सूचना देखाइयोस्"</string>
- <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"मैले कपी गरेका टेक्स्ट, फोटो वा अन्य सामग्री एपहरूले प्रयोग गर्दा म्यासेज देखाइयोस्"</string>
- <string name="show_password_title" msgid="2877269286984684659">"पासवर्डहरू देखाइयोस्"</string>
- <string name="show_password_summary" msgid="1110166488865981610">"टाइप गर्दै गर्दा वर्णहरू झलक्क देखाइयोस्"</string>
+ <string name="show_clip_access_notification_title" msgid="5168467637351109096">"क्लिपबोर्डका सामग्री प्रयोग गरिँदा सूचना देखाउनुहोस्"</string>
+ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"मैले कपी गरेका टेक्स्ट, फोटो वा अन्य सामग्री एपहरूले प्रयोग गर्दा म्यासेज देखाउनुहोस्"</string>
+ <string name="show_password_title" msgid="2877269286984684659">"पासवर्डहरू देखाउनुहोस्"</string>
+ <string name="show_password_summary" msgid="1110166488865981610">"टाइप गर्दै गर्दा वर्णहरू झलक्क देखाउनुहोस्"</string>
<string name="permission_rationale_message_location" msgid="2153841534298068414">"यो एपले यसले लोकेसन डेटा तेस्रो पक्षसँग सेयर गर्न सक्छ भन्ने जानकारी दिएको छ"</string>
<string name="permission_rationale_location_title" msgid="2404797182678793506">"लोकेसन डेटा सेयर गर्नेसम्बन्धी अभ्यास"</string>
<string name="permission_rationale_data_sharing_source_title" msgid="6874604543125814316">"जानकारी सेयर गर्नेसम्बन्धी अभ्यासको स्रोत"</string>
@@ -615,7 +617,7 @@
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"तपाईंको लोकेसन डेटा अहिले तेस्रो पक्षहरूसँग सेयर गरिँदै छ"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"विज्ञापन तथा मार्केटिङ गर्ने प्रयोजनका लागि तपाईंको लोकेसन डेटा अहिले तेस्रो पक्षहरूसँग सेयर गरिँदै छ"</string>
<string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{पछिल्लो एक दिनभित्र अपडेट गरिएको}=1{पछिल्लो एक दिनभित्र अपडेट गरिएको}other{पछिल्ला # दिनभित्र अपडेट गरिएको}}"</string>
- <string name="no_updates_at_this_time" msgid="9031085635689982935">"अहिले कुनै अद्यावधिक जानकारी उपलब्ध छैन"</string>
+ <string name="no_updates_at_this_time" msgid="9031085635689982935">"अहिले कुनै अपडेट उपलब्ध छैन"</string>
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"जानकारी सेयर गर्नेसम्बन्धी अभ्यासका बारेमा अद्यावधिक जानकारी"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"केही एपहरूले तपाईंको लोकेसन डेटा सेयर गर्न सक्ने तरिका परिवर्तन गरेका छन्"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"सेटिङ"</string>
diff --git a/PermissionController/res/values-night-v33/themes.xml b/PermissionController/res/values-night-v33/themes.xml
index 6374ee088..9b6f638a6 100644
--- a/PermissionController/res/values-night-v33/themes.xml
+++ b/PermissionController/res/values-night-v33/themes.xml
@@ -16,10 +16,6 @@
-->
<resources>
- <style name="Theme.SafetyCenterQs" parent="Theme.SafetyCenterQsBase">
- <item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
- </style>
-
<style name="Theme.SafetyCenterBase" parent="Theme.PermissionController.Settings.FilterTouches">
<item name="colorSurface">@color/sc_surface_dark</item>
<item name="colorSurfaceVariant">@color/sc_surface_variant_dark</item>
@@ -49,8 +45,12 @@
<item name="scStatusButtonStyle">@style/SafetyCenterStatusButton.Responsive</item>
<!-- Buttons -->
- <item name="scActionButtonStyle">@style/SafetyCenterActionButton</item>
- <item name="scSecondaryActionButtonStyle">@style/SafetyCenterActionButton.Secondary</item>
+ <item name="scActionButtonListLayout">@layout/action_button_list_responsive</item>
+ <item name="scActionButtonTheme">@style/Theme.MaterialComponents.DayNight</item>
+ <item name="scActionButtonStyle">@style/SafetyCenterActionButton.Responsive</item>
+ <item name="scSecondaryActionButtonStyle">
+ @style/SecondarySafetyCenterActionButton.Responsive
+ </item>
<item name="textColorScActionButton">@color/sc_primary_action_button_text</item>
<item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
diff --git a/PermissionController/res/values-night-v34/themes.xml b/PermissionController/res/values-night-v34/themes.xml
index affc57027..b6328d782 100644
--- a/PermissionController/res/values-night-v34/themes.xml
+++ b/PermissionController/res/values-night-v34/themes.xml
@@ -21,11 +21,6 @@
<item name="android:colorBackground">@color/google_grey_800</item>
</style>
- <style name="Theme.SafetyCenterQs" parent="Theme.SafetyCenterQsBase">
- <item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
- <item name="colorScShieldAccent">@color/sc_shield_accent_dark</item>
- </style>
-
<style name="Theme.SafetyCenter" parent="Theme.SafetyCenterBase">
<item name="colorScShieldAccent">@color/sc_shield_accent_dark</item>
</style>
diff --git a/PermissionController/res/values-nl/strings.xml b/PermissionController/res/values-nl/strings.xml
index f845b1de4..0944b4be1 100644
--- a/PermissionController/res/values-nl/strings.xml
+++ b/PermissionController/res/values-nl/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Meer informatie"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Alles toestaan"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Altijd alles toestaan"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Beperkte toegang toestaan"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Foto\'s en video\'s selecteren"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Meer selecteren"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Niet meer selecteren"</string>
@@ -50,7 +51,7 @@
<string name="permission_revoked_none" msgid="9213345075484381180">"geen rechten ingetrokken"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"Toestaan"</string>
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"Altijd toestaan"</string>
- <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Tijdens gebruik van de app"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Tijdens gebruik van app"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"Wijzigen in exacte locatie"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"Geschatte locatie behouden"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"Alleen deze keer"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"App-rechten"</string>
<string name="unused_apps" msgid="2058057455175955094">"Niet-gebruikte apps"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Bewerk geselecteerde foto\'s voor deze app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Geen niet-gebruikte apps"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 niet-gebruikte apps"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Recente rechtenbeslissingen"</string>
@@ -219,7 +221,7 @@
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"Rechten voor <xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g> en <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g> verwijderd"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> en <xliff:g id="NUMBER">%2$s</xliff:g> andere rechten zijn verwijderd"</string>
<string name="unused_apps_page_title" msgid="6986983535677572559">"Niet-gebruikte apps"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"Als je een app een paar maanden niet gebruikt, gebeurt het volgende:\n\n• De rechten worden ingetrokken om je gegevens te beschermen.\n• Meldingen worden stopgezet om de batterij te sparen.\n• Tijdelijke bestanden worden verwijderd om ruimte vrij te maken.\n\nOpen de app als je de rechten en meldingen weer wilt toestaan."</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"Als je een app een paar maanden niet gebruikt, gebeurt het volgende:\n\n• De rechten worden ingetrokken om je gegevens te beschermen.\n• Meldingen worden stopgezet om de batterij te sparen.\n• Tijdelijke bestanden worden verwijderd om ruimte vrij te maken.\n\nOpen de app om rechten en meldingen opnieuw toe te staan."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Als je een app een maand niet gebruikt, gebeurt het volgende:\n\n• De rechten worden verwijderd om je gegevens te beschermen.\n• Tijdelijke bestanden worden verwijderd om ruimte vrij te maken.\n\nOpen de app als je de rechten weer wilt toestaan."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Meer dan # maand geleden voor het laatst geopend}other{Meer dan # maanden geleden voor het laatst geopend}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"App laatst geopend op <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -349,27 +351,27 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Deze apps kunnen je scherm, acties en invoer bekijken, acties uitvoeren en de weergave beheren."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"Standaard digitale-assistent-app"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"Digitale-assistent-app"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"Assistentie-apps kunnen je helpen op basis van de informatie op het scherm dat je bekijkt. Bepaalde apps ondersteunen launcher- en spraakinvoerservices voor geïntegreerde ondersteuning."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"Apps voor assistentie kunnen je helpen op basis van de informatie op het scherm dat je bekijkt. Bepaalde apps ondersteunen launcher- en spraakinvoerservices voor geïntegreerde ondersteuning."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Standaard browser-app"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Browser-app"</string>
- <string name="role_browser_description" msgid="3465253637499842671">"Apps die toegang tot internet geven en de links bekijken waarop je hebt getikt"</string>
+ <string name="role_browser_description" msgid="3465253637499842671">"Apps die toegang tot internet geven en de links tonen waarop je hebt getikt"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"Wil je <xliff:g id="APP_NAME">%1$s</xliff:g> instellen als je standaard browser-app?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"Geen rechten nodig"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"Standaard telefoon-app"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"Telefoon-app"</string>
- <string name="role_dialer_description" msgid="8768708633696539612">"Apps waarmee je kunt bellen en gebeld kunt worden op je apparaat."</string>
+ <string name="role_dialer_description" msgid="8768708633696539612">"Apps waarmee je kunt bellen en gebeld worden op je apparaat"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"<xliff:g id="APP_NAME">%1$s</xliff:g> instellen als standaard telefoon-app?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"Deze app krijgt toegang tot Camera, Contacten, Microfoon, Telefoon en Sms"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"kiezer"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Standaard sms-app"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"Sms-app"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Apps waarmee je met je telefoonnummer onder andere sms\'jes, foto\'s en video\'s kunt sturen en ontvangen."</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Apps waarmee je met je telefoonnummer onder andere korte tekstberichten, foto\'s en video\'s kunt sturen en ontvangen"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Wil je <xliff:g id="APP_NAME">%1$s</xliff:g> instellen als je standaard sms-app?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Deze app krijgt toegang tot Camera, Contacten, Bestanden en media, Microfoon, Telefoon en Sms"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"tekst bericht, tekstbericht, sms, sms\'en, berichten, verzenden, sturen"</string>
<string name="role_emergency_label" msgid="7028825857206842366">"Standaardapp voor noodgevallen"</string>
<string name="role_emergency_short_label" msgid="2388431453335350348">"Nood-app"</string>
- <string name="role_emergency_description" msgid="5051840234887686630">"Apps waarmee je je medische gegevens kunt vastleggen en toegankelijk kunt maken voor hulpverleners; apps waarmee je waarschuwingen ontvangt voor gevaarlijk weer of rampen; apps waarmee je mensen kunt laten weten dat je hulp nodig hebt"</string>
+ <string name="role_emergency_description" msgid="5051840234887686630">"Apps waarmee je je medische info kunt vastleggen en toegankelijk kunt maken voor hulpverleners; apps waarmee je waarschuwingen krijgt voor gevaarlijk weer of rampen; apps waarmee je mensen kunt laten weten dat je hulp nodig hebt"</string>
<string name="role_emergency_request_title" msgid="8469579020654348567">"Wil je <xliff:g id="APP_NAME">%1$s</xliff:g> instellen als je standaard-app voor noodgevallen?"</string>
<string name="role_emergency_request_description" msgid="131645948770262850">"Geen rechten nodig"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"bij noodgevallen"</string>
@@ -427,7 +429,7 @@
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Links openen"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Standaard voor werk"</string>
<string name="default_app_none" msgid="9084592086808194457">"Geen"</string>
- <string name="default_app_system_default" msgid="6218386768175513760">"(Systeemstandaard)"</string>
+ <string name="default_app_system_default" msgid="6218386768175513760">"(Systeem­standaard)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Geen apps"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Geselecteerd"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Geselecteerd: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
@@ -455,45 +457,45 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Activeringsdetectie van de assistent tonen"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Icoon op statusbalk tonen als microfoon wordt gebruikt om de Spraakassistent te activeren"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot foto\'s en media op je apparaat?"</string>
- <string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot je contacten?"</string>
+ <string name="permgrouprequest_contacts" msgid="8391550064551053695">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang heeft tot je contacten?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot de locatie van dit apparaat?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"De app heeft alleen toegang tot de locatie wanneer je de app gebruikt"</string>
- <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot de locatie van dit apparaat?"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang heeft tot de locatie van dit apparaat?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Deze app wil mogelijk altijd toegang tot je locatie, ook als je de app niet gebruikt. "<annotation id="link">"Je kunt dit toestaan via de instellingen."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Toegang tot locatie wijzigen voor &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Deze app wil altijd toegang tot je locatie, ook als je de app niet gebruikt. "<annotation id="link">"Je kunt dit toestaan via de instellingen."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; apparaten in de buurt vindt, er verbinding mee maakt en de relatieve positie bepaalt?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; apparaten in de buurt vindt, er verbinding mee maakt en de relatieve positie bepaalt? "<annotation id="link">"Toestaan in Instellingen"</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Locatietoegang van <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> wijzigen van geschatte in exacte locatie?"</string>
- <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot de geschatte locatie van dit apparaat?"</string>
+ <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang heeft tot de geschatte locatie van dit apparaat?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exact"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Geschat"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot je agenda?"</string>
- <string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om sms\'jes te verzenden en te bekijken?"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang heeft tot je agenda?"</string>
+ <string name="permgrouprequest_sms" msgid="5672063688745420991">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sms-berichten stuurt en bekijkt?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot foto\'s, media en bestanden op je apparaat?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot &lt;b&gt;foto\'s, video\'s, muziek en audio&lt;/b&gt; op dit apparaat?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot &lt;b&gt;foto\'s, video\'s, muziek, audio en andere bestanden&lt;/b&gt; op dit apparaat?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot muziek en audio op dit apparaat?"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot foto\'s en video\'s op dit apparaat?"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang heeft tot foto\'s en video\'s op dit apparaat?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot meer foto\'s en video\'s op dit apparaat?"</string>
- <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om audio op te nemen?"</string>
+ <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; audio opneemt?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Deze app kan alleen audio opnemen als je de app gebruikt"</string>
- <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om audio op te nemen?"</string>
+ <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; audio opneemt?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Deze app wil mogelijk altijd audio opnemen, ook als je de app niet gebruikt. "<annotation id="link">"Toestaan in instellingen."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Toegang tot microfoon wijzigen voor &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Deze app wil altijd audio opnemen, ook als je de app niet gebruikt. "<annotation id="link">"Toestaan in instellingen."</annotation></string>
- <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot je fysieke activiteit?"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om foto\'s te maken en video\'s op te nemen?"</string>
+ <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang heeft tot je fysieke activiteit?"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; foto\'s maakt en video\'s opneemt?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Deze app kan alleen foto\'s maken en video\'s opnemen als je de app gebruikt"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om foto\'s te maken en video\'s op te nemen?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Deze app wil mogelijk altijd foto\'s maken en video\'s opnemen, ook als je de app niet gebruikt. "<annotation id="link">"Toestaan in Instellingen."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Toegang tot camera wijzigen voor &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Deze app wil altijd foto\'s maken en video\'s opnemen, ook als je de app niet gebruikt. "<annotation id="link">"Toestaan in Instellingen."</annotation></string>
- <string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot je gesprekslijsten?"</string>
- <string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om telefoongesprekken te starten en te beheren?"</string>
+ <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang heeft tot je gesprekslijsten?"</string>
+ <string name="permgrouprequest_phone" msgid="1829234136997316752">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; telefoongesprekken start en beheert?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot sensorgegevens over je vitale functies?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Deze app wil altijd toegang tot gegevens van lichaamssensoren over je vitale functies, ook als je de app niet gebruikt. "<annotation id="link">"Ga naar Instellingen"</annotation>" als je deze wijziging wilt doorvoeren."</string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot sensorgegevens over je vitale functies?"</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Deze app wil altijd toegang tot gegevens van lichaamssensoren over je vitale functies, ook als je de app niet gebruikt. "<annotation id="link">"Ga naar Instellingen"</annotation>" om deze wijziging door te voeren."</string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang heeft tot sensorgegevens over je vitale functies?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Als je deze app altijd toegang tot gegevens van lichaamssensoren wilt geven, ook als je de app niet gebruikt, "<annotation id="link">"ga je naar Instellingen"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Instelling behouden dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot gegevens van lichaamssensoren heeft als de app wordt gebruikt?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; je meldingen stuurt?"</string>
@@ -581,7 +583,7 @@
<string name="mic_toggle_title" msgid="2649991093496110162">"Microfoontoegang"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Voor apps en services"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Voor apps en services. Als deze instelling uitstaat, kunnen microfoongegevens nog altijd worden gedeeld als je een alarmnummer belt."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"Bekijk apps en services die toegang hebben tot die locatie"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"Bekijk apps en services die toegang hebben tot de locatie"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Toegang tot klembord tonen"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Toon een bericht als apps toegang hebben tot tekst, afbeeldingen of andere content die je hebt gekopieerd"</string>
<string name="show_password_title" msgid="2877269286984684659">"Wachtwoorden tonen"</string>
@@ -608,9 +610,9 @@
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"Locatiegegevens kunnen worden gedeeld"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Deze app geeft aan dat je locatiegegevens met derden kunnen worden gedeeld"</string>
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Updates voor het delen van locatiegegevens"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"Check apps die de manier hebben veranderd waarop je locatiegegevens worden gedeeld"</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"Ga na welke apps de manier hebben veranderd waarop je locatiegegevens worden gedeeld"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Deze apps hebben de manier veranderd waarop ze je locatiegegevens kunnen delen. Misschien deelden ze de gegevens eerder niet, of kunnen ze deze nu delen voor reclame- en marketingdoeleinden."</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"De ontwikkelaars van deze apps hebben informatie gegeven aan een appstore over hun procedures voor gegevens delen. Ze kunnen deze informatie in de loop van de tijd updaten.\n\nProcedures voor gegevens delen kunnen verschillen op basis van je app-versie, gebruik, regio en leeftijd."</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"De ontwikkelaars van deze apps hebben informatie gegeven aan een appstore over de manier waarop ze gegevens delen. Ze kunnen deze informatie in de loop van de tijd updaten.\n\nProcedures voor gegevens delen kunnen verschillen op basis van je app-versie, gebruik, regio en leeftijd."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Meer informatie over gegevens delen"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Je locatiegegevens worden nu gedeeld met derden"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Je locatiegegevens worden nu gedeeld met derden voor advertentie- of marketingdoeleinden"</string>
diff --git a/PermissionController/res/values-or-television/strings.xml b/PermissionController/res/values-or-television/strings.xml
index 2859cc59e..181c9640c 100644
--- a/PermissionController/res/values-or-television/strings.xml
+++ b/PermissionController/res/values-or-television/strings.xml
@@ -20,7 +20,7 @@
<string name="grant_dialog_how_to_change" msgid="997462845048160559">"ଏହାକୁ ଆପଣ ପରେ ଆପ୍ସ ସେଟିଂସ୍‌ରେ&gt; ବଦଳାଇପାରିବେ"</string>
<string name="current_permission_template" msgid="6240787325714651204">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
<string name="preference_show_system_apps" msgid="4262140518693221093">"ସିଷ୍ଟମ୍‍ ଆପ୍ସ ଦେଖାନ୍ତୁ"</string>
- <string name="app_permissions_decor_title" msgid="7438716722786036814">"ଆପ ଅନୁମତିଗୁଡ଼ିକ"</string>
+ <string name="app_permissions_decor_title" msgid="7438716722786036814">"ଆପ ଅନୁମତି"</string>
<string name="manage_permissions_decor_title" msgid="4138423885439613577">"ଆପ ଅନୁମତିଗୁଡ଼ିକ"</string>
<string name="permission_apps_decor_title" msgid="2811550489429789828">"<xliff:g id="PERMISSION">%1$s</xliff:g> ଅନୁମତିଗୁଡ଼ିକ"</string>
<string name="additional_permissions_decor_title" msgid="5113847982502484225">"ଅତିରିକ୍ତ ଅନୁମତି"</string>
diff --git a/PermissionController/res/values-or/strings.xml b/PermissionController/res/values-or/strings.xml
index 840eae10f..40bf57e6b 100644
--- a/PermissionController/res/values-or/strings.xml
+++ b/PermissionController/res/values-or/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"ଅଧିକ ସୂଚନା"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"ସବୁକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"ସର୍ବଦା ସବୁକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"ସୀମିତ ଆକ୍ସେସକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ଫଟୋ ଏବଂ ଭିଡିଓଗୁଡ଼ିକୁ ଚୟନ କରନ୍ତୁ"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"ଅଧିକ ଚୟନ କରନ୍ତୁ"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"ଅଧିକ ଚୟନ କରନ୍ତୁ ନାହିଁ"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ଆପ୍ସ"</string>
<string name="app_permissions" msgid="3369917736607944781">"ଆପ ଅନୁମତିଗୁଡ଼ିକ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ଅବ୍ୟବହୃତ ଆପ୍ସ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"ଏହି ଆପ ପାଇଁ ଚୟନିତ ଫଟୋଗୁଡ଼ିକୁ ଏଡିଟ କରନ୍ତୁ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"କୌଣସି ଅବ୍ୟବହୃତ ଆପ୍ ନାହିଁ"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0ଟି ଅବ୍ୟବହୃତ ଆପ୍"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"ବର୍ତ୍ତମାନର ଅନୁମତି ନିଷ୍ପତ୍ତି"</string>
@@ -70,7 +72,7 @@
<string name="denied_permission_decision" msgid="5308961501779563781">"ଆପଣ <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>କୁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆକ୍ସେସ ଅଗ୍ରାହ୍ୟ କରିଛନ୍ତି"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{ଆଜି}=1{1 ଦିନ ପୂର୍ବେ}other{# ଦିନ ପୂର୍ବେ}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ଆପ୍ ଅକ୍ଷମ କରନ୍ତୁ"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"ଯଦି ଆପଣ ଏହି ଆପକୁ ଅକ୍ଷମ କରିବେ, ତେବେ Android ଓ ଅନ୍ୟ ଆପଗୁଡ଼ିକ ଆଉ ଆଶାନୁରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ। ମନେରଖନ୍ତୁ ଯେ, ଏହି ଆପ୍ ଆପଣଙ୍କ ଡିଭାଇସରେ ପୂର୍ବରୁ-ଇନଷ୍ଟଲ୍ କରାଯାଇଥିବା ଯୋଗୁଁ ଆପଣ ଏହାକୁ ଡିଲିଟ୍ କରିପାରିବେ ନାହିଁ। ଅକ୍ଷମ କରି, ଆପଣ ଏହି ଆପକୁ ବନ୍ଦ କରିଦିଅନ୍ତି ଏବଂ ଆପଣଙ୍କ ଡିଭାଇସରେ ଲୁଚାଇଦିଅନ୍ତି।"</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"ଯଦି ଆପଣ ଏହି ଆପକୁ ଅକ୍ଷମ କରିବେ, ତେବେ Android ଓ ଅନ୍ୟ ଆପ୍ସ ଆଉ ଆଶାନୁରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ। ମନେରଖନ୍ତୁ ଯେ, ଏହି ଆପ ଆପଣଙ୍କ ଡିଭାଇସରେ ପୂର୍ବରୁ-ଇନଷ୍ଟଲ କରାଯାଇଥିବା ଯୋଗୁଁ ଆପଣ ଏହାକୁ ଡିଲିଟ କରିପାରିବେ ନାହିଁ। ଅକ୍ଷମ କରିବା ଫଳରେ ଆପଣ ଏହି ଆପକୁ ବନ୍ଦ କରି ଆପଣଙ୍କ ଡିଭାଇସରେ ଲୁଚାଇଦିଅନ୍ତି।"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"ଅନୁମତି ପରିଚାଳକ"</string>
<string name="never_ask_again" msgid="4728762438198560329">"ପୁଣି ପଚାରନ୍ତୁ ନାହିଁ"</string>
<string name="no_permissions" msgid="3881676756371148563">"କୌଣସି ଅନୁମତିଗୁଡ଼ିକ ନାହିଁ"</string>
@@ -352,17 +354,17 @@
<string name="role_assistant_description" msgid="6622458130459922952">"ଆପଣ ଦେଖୁଥିବା ସ୍କ୍ରିନ୍‌ ସୂଚନାକୁ ଆଧାର କରି ସହାୟକ ଆପ୍‌ ଆପଣଙ୍କୁ ସାହାଯ୍ୟ କରିପାରିବ। କେତେକ ଆପ୍‌, ଆପଣଙ୍କୁ ଉତ୍ତମ ସହାୟତା ଦେବା ପାଇଁ, ଉଭୟ ଲଞ୍ଚର୍ ଓ ଭଏସ୍‍ ଇନପୁଟ୍‍ ସେବାକୁ ସମର୍ଥନ କରେ।"</string>
<string name="role_browser_label" msgid="2877796144554070207">"ଡିଫଲ୍ଟ ବ୍ରାଉଜର୍‌ ଆପ୍‌"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"ବ୍ରାଉଜର୍‌ ଆପ୍‌"</string>
- <string name="role_browser_description" msgid="3465253637499842671">"ଆପ୍ସ ଯାହା ଆପଣଙ୍କୁ ଇଣ୍ଟର୍ନେଟ୍ ପାଇଁ ଆକ୍ସେସ୍ ଦିଏ ଏବଂ ଆପଣ ଟାପ୍ କରିଥିବା ଲିଙ୍କ୍‌ଗୁଡ଼ିକ ପ୍ରଦର୍ଶନ କରେ"</string>
+ <string name="role_browser_description" msgid="3465253637499842671">"ଆପ୍ସ ଯାହା ଆପଣଙ୍କୁ ଇଣ୍ଟର୍ନେଟ ପାଇଁ ଆକ୍ସେସ ଦିଏ ଏବଂ ଆପଣ ଟାପ କରୁଥିବା ଲିଙ୍କଗୁଡ଼ିକୁ ଡିସପ୍ଲେ କରେ"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ ବ୍ରାଉଜର୍ ଭାବେ ସେଟ୍ କରିବେ କି?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"କୌଣସି ଅନୁମତି ଆବଶ୍ୟକ ନାହିଁ"</string>
- <string name="role_dialer_label" msgid="1100224146343237968">"ଡିଫଲ୍ଟ ଫୋନ୍‌ ଆପ୍‌"</string>
+ <string name="role_dialer_label" msgid="1100224146343237968">"ଡିଫଲ୍ଟ ଫୋନ ଆପ"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"ଫୋନ୍ ଆପ୍"</string>
<string name="role_dialer_description" msgid="8768708633696539612">"ଆପ୍ସ, ଯାହା ଆପଣଙ୍କୁ ଆପଣଙ୍କର ଡିଭାଇସ୍‌ରେ ଟେଲିଫୋନ୍ କଲ୍ କରିବାକୁ ଏବଂ ଗ୍ରହଣ କରିବାକୁ ଅନୁମତି ଦିଏ"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ ଫୋନ୍ ଆପ୍ ଭାବରେ ସେଟ୍ କରିବେ କି?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"ଏହି ଆପକୁ ଆପଣଙ୍କ କ୍ୟାମେରା, ଯୋଗାଯୋଗ, ମାଇକ୍ରୋଫୋନ, ଫୋନ ଏବଂ SMSକୁ ଆକ୍ସେସ ଦିଆଯିବ"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"ଡାୟଲର୍"</string>
<string name="role_sms_label" msgid="8456999857547686640">"ଡିଫଲ୍ଟ SMS ଆପ୍‌"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"SMS ଆପ୍‌"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"SMS ଆପ"</string>
<string name="role_sms_description" msgid="3424020199148153513">"ଆପ୍ସ, ଯାହା ଆପଣଙ୍କ ଫୋନ୍ ନମ୍ବର ବ୍ୟବହାର କରି ଛୋଟ ଟେକ୍ସଟ୍ ମେସେଜ୍, ଫଟୋ, ଭିଡିଓ, ଏବଂ ଅନେକ କିଛି ପଠାଇବା ଏବଂ ପ୍ରାପ୍ତ କରିବାକୁ ଅନୁମତି ଦିଏ"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ SMS ଆପ୍ ଭାବରେ ସେଟ୍ କରିବେ କି?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"ଏହି ଆପକୁ ଆପଣଙ୍କ କ୍ୟାମେରା, ଯୋଗାଯୋଗ, ଫାଇଲ ଓ ଡକ୍ୟୁମେଣ୍ଟ ମାଇକ୍ରୋଫୋନ, ଫୋନ ଏବଂ SMSକୁ ଆକ୍ସେସ ଦିଆଯିବ"</string>
@@ -384,7 +386,7 @@
<string name="role_call_redirection_description" msgid="6091669882014664420">"ସେହି ଆପ୍ସ ଯାହା ଆଉଟ୍‌ଗୋଇଂ କଲ୍‍ଗୁଡ଼ିକୁ ଅନ୍ୟ ଏକ ଫୋନ୍ ନମ୍ବରକୁ ଫର୍‍ୱାର୍ଡ କରିବାକୁ ଆପଣଙ୍କୁ ଅନୁମତି ଦିଏ"</string>
<string name="role_call_redirection_request_title" msgid="2816244455003562925">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ କଲ୍ ପୁନଃନିର୍ଦ୍ଦେଶନା ଆପ୍ ଭାବରେ ସେଟ୍ କରିବେ କି?"</string>
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"କୌଣସି ଅନୁମତି ଆବଶ୍ୟକ ନାହିଁ"</string>
- <string name="role_call_screening_label" msgid="883935222060878724">"ଡିଫଲ୍ଟ କଲର୍ ID &amp; ସ୍ପାମ୍ ଆପ୍"</string>
+ <string name="role_call_screening_label" msgid="883935222060878724">"ଡିଫଲ୍ଟ କଲର ID ଏବଂ ସ୍ପାମ ଆପ"</string>
<string name="role_call_screening_short_label" msgid="2048465565063130834">"କଲର୍ ID &amp; ସ୍ପାମ୍ ଆପ୍"</string>
<string name="role_call_screening_description" msgid="2349431420497468981">"ସେହି ଆପ୍ସ ଯାହା କଲଗୁଡ଼ିକୁ ଚିହ୍ନଟ କରିବା, ସ୍ପାମ୍ ଓ ରୋବୋକଲ୍ ବା ଅଦରକାରୀ ନମ୍ବରଗୁଡ଼ିକୁ ବ୍ଲକ୍ କରିବାକୁ ଅନୁମତି ଦିଏ"</string>
<string name="role_call_screening_request_title" msgid="7358309224566977290">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଆପଣଙ୍କର ଡିଫଲ୍ଟ କଲର୍ ଆଇଡି &amp; ସ୍ପାମ୍ ଆପ୍ ଭାବ୍ ସେଟ୍ କରିବେ କି?"</string>
@@ -491,7 +493,7 @@
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"ଆପଣ ଆପକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ, ଏହା ସବୁ ସମୟରେ ଛବି ନେବାକୁ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିବାକୁ ଚାହେଁ। "<annotation id="link">"ସେଟିଂସରେ ଅନୁମତି ଦିଅନ୍ତୁ।"</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"ଆପଣଙ୍କ ଫୋନର କଲ ଲଗ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଫୋନ କଲ କରିବାକୁ ତଥା ପରିଚାଳନା କରିବାକୁ ଅନୁମତି ଦେବେ?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଆପଣଙ୍କ ଗୁରୁତ୍ୱପୂର୍ଣ୍ଣ ଲକ୍ଷଣଗୁଡ଼ିକ ବିଷୟରେ ସେନ୍ସର୍‍ ଡାଟା ଆକ୍ସେସ୍‍ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଆପଣଙ୍କ ଗୁରୁତ୍ୱପୂର୍ଣ୍ଣ ଲକ୍ଷଣଗୁଡ଼ିକ ବିଷୟରେ ସେନ୍ସର ଡାଟା ଆକ୍ସେସ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"ଆପଣ ଆପ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ, ଏହି ଆପ ସବୁ ସମୟରେ ଆପଣଙ୍କ ମହତ୍ତ୍ୱପୂର୍ଣ୍ଣ ଲକ୍ଷଣଗୁଡ଼ିକ ବିଷୟରେ ସେନ୍ସର ଡାଟାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଚାହେଁ। ଏହି ପରିବର୍ତ୍ତନ କରିବାକୁ, "<annotation id="link">"ସେଟିଂସକୁ ଯାଆନ୍ତୁ।"</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"ଆପଣଙ୍କ ମହତ୍ତ୍ୱପୂର୍ଣ୍ଣ ଲକ୍ଷଣଗୁଡ଼ିକ ବିଷୟରେ ସେନ୍ସର ଡାଟାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ କି?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"ଆପଣ ଆପ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ, ବଡି ସେନ୍ସର ଡାଟାକୁ ସର୍ବଦା ଆକ୍ସେସ କରିବା ନିମନ୍ତେ ଏହି ଆପକୁ ଅନୁମତି ଦେବା ପାଇଁ, "<annotation id="link">"ସେଟିଂସକୁ ଯାଆନ୍ତୁ।"</annotation></string>
diff --git a/PermissionController/res/values-pa-v33/strings.xml b/PermissionController/res/values-pa-v33/strings.xml
index 03e18ae50..0aa143302 100644
--- a/PermissionController/res/values-pa-v33/strings.xml
+++ b/PermissionController/res/values-pa-v33/strings.xml
@@ -28,9 +28,9 @@
<string name="safety_center_entry_group_item_content_description" msgid="7348298582877249787">"ਸੂਚੀ ਆਈਟਮ। <xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string>
<string name="safety_center_entry_content_description" msgid="3639565652938224321">"<xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string>
<string name="safety_center_more_issues_card_title" msgid="7425844746197493312">"ਹੋਰ ਅਲਰਟ"</string>
- <string name="safety_center_dismissed_issues_card_title" msgid="2340129842725145733">"ਖਾਰਜ ਕੀਤੀਆਂ ਗਈਆਂ ਸੁਚੇਤਨਾਵਾਂ"</string>
- <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{ਵਿਸਤਾਰ ਕਰੋ ਅਤੇ ਇੱਕ ਹੋਰ ਸੁਚੇਤਨਾ ਦੇਖੋ}one{ਵਿਸਤਾਰ ਕਰੋ ਅਤੇ # ਹੋਰ ਸੁਚੇਤਨਾ ਦੇਖੋ}other{ਵਿਸਤਾਰ ਕਰੋ ਅਤੇ # ਹੋਰ ਸੁਚੇਤਨਾਵਾਂ ਦੇਖੋ}}"</string>
- <string name="safety_center_issue_card_prefix_content_description" msgid="1447445289637043544">"ਸੁਚੇਤਨਾ। <xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>"</string>
+ <string name="safety_center_dismissed_issues_card_title" msgid="2340129842725145733">"ਖਾਰਜ ਕੀਤੇ ਅਲਰਟ"</string>
+ <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{ਵਿਸਤਾਰ ਕਰੋ ਅਤੇ ਇੱਕ ਹੋਰ ਅਲਰਟ ਦੇਖੋ}one{ਵਿਸਤਾਰ ਕਰੋ ਅਤੇ # ਹੋਰ ਅਲਰਟ ਦੇਖੋ}other{ਵਿਸਤਾਰ ਕਰੋ ਅਤੇ # ਹੋਰ ਅਲਰਟ ਦੇਖੋ}}"</string>
+ <string name="safety_center_issue_card_prefix_content_description" msgid="1447445289637043544">"ਅਲਰਟ। <xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>"</string>
<string name="safety_center_resolved_issue_fallback" msgid="8548932070610766651">"ਕਾਰਵਾਈ ਪੂਰੀ ਹੋਈ"</string>
<string name="safety_center_qs_status_summary" msgid="5193925895830451177">"ਉਨ੍ਹਾਂ ਸੈਟਿੰਗਾਂ ਦੀ ਜਾਂਚ ਕਰੋ ਜੋ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਵਿੱਚ ਸੁਰੱਖਿਆ ਸ਼ਾਮਲ ਕਰ ਸਕਦੀਆਂ ਹਨ"</string>
<string name="safety_center_qs_page_landing" msgid="1717368301679228128">"ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ ਸੰਬੰਧੀ ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ"</string>
diff --git a/PermissionController/res/values-pa-v34/strings.xml b/PermissionController/res/values-pa-v34/strings.xml
index c0b70fb57..cc6dc2521 100644
--- a/PermissionController/res/values-pa-v34/strings.xml
+++ b/PermissionController/res/values-pa-v34/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"ਸਿਕਿਊਰਟੀ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"ਕੰਟਰੋਲ"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"ਸਿਹਤ ਸੰਬੰਧੀ ਡਾਟੇ ਤੱਕ ਐਪ ਦੀ ਪਹੁੰਚ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
diff --git a/PermissionController/res/values-pa/strings.xml b/PermissionController/res/values-pa/strings.xml
index f4e26680c..2f8f109d2 100644
--- a/PermissionController/res/values-pa/strings.xml
+++ b/PermissionController/res/values-pa/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"ਹੋਰ ਜਾਣਕਾਰੀ"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"ਸਭ \'ਤੇ ਕਰਨ ਦਿਓ"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"ਹਮੇਸ਼ਾਂ ਸਭ ਨੂੰ ਆਗਿਆ ਦਿਓ"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"ਸੀਮਤ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ ਚੁਣੋ"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"ਹੋਰ ਚੁਣੋ"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"ਹੋਰ ਡਾਟਾ ਨਾ ਚੁਣੋ"</string>
@@ -48,7 +49,7 @@
<string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g> ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਗਿਆ"</string>
<string name="permission_revoked_all" msgid="3397649017727222283">"ਸਾਰੀਆਂ ਇਜਾਜ਼ਤਾਂ ਬੰਦ ਕਰ ਦਿੱਤੀਆਂ ਗਈਆਂ"</string>
<string name="permission_revoked_none" msgid="9213345075484381180">"ਕਿਸੇ ਨੂੰ ਵੀ ਬੰਦ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
- <string name="grant_dialog_button_allow" msgid="5314677880021102550">"ਕਰਨ ਦਿਓ"</string>
+ <string name="grant_dialog_button_allow" msgid="5314677880021102550">"ਆਗਿਆ ਦਿਓ"</string>
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"ਹਰ ਵੇਲੇ ਕਰਨ ਦਿਓ"</string>
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"ਐਪ ਵਰਤਣ ਦੌਰਾਨ"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"ਸਹੀ ਟਿਕਾਣੇ \'ਤੇ ਬਦਲੋ"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ਐਪਾਂ"</string>
<string name="app_permissions" msgid="3369917736607944781">"ਐਪ ਇਜਾਜ਼ਤਾਂ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ਅਣਵਰਤੀਆਂ ਐਪਾਂ"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"ਇਸ ਐਪ ਲਈ ਚੁਣੀਆਂ ਫ਼ੋਟੋਆਂ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ਕੋਈ ਅਣਵਰਤੀ ਐਪ ਨਹੀਂ"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ਅਣਵਰਤੀਆਂ ਐਪਾਂ"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਹਾਲੀਆ ਫ਼ੈਸਲੇ"</string>
@@ -70,7 +72,7 @@
<string name="denied_permission_decision" msgid="5308961501779563781">"ਤੁਸੀਂ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੀ <xliff:g id="PERMISSION_NAME">%2$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਨੂੰ ਅਸਵੀਕਾਰ ਕੀਤਾ"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{ਅੱਜ}=1{1 ਦਿਨ ਪਹਿਲਾਂ}other{# ਦਿਨ ਪਹਿਲਾਂ}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ਐਪ ਨੂੰ ਬੰਦ ਕਰੋ"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"ਜੇ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਬੰਦ ਕਰਦੇ ਹੋ, ਤਾਂ ਹੋ ਸਕਦਾ ਹੈ ਕਿ Android ਅਤੇ ਹੋਰ ਐਪਾਂ ਉਸ ਤਰ੍ਹਾਂ ਕੰਮ ਨਾ ਕਰਨ ਜਿਵੇਂ ਇਹਨਾਂ ਤੋਂ ਉਮੀਦ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ, ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਵਿੱਚ ਇਹ ਐਪ ਪਹਿਲਾਂ ਤੋਂ ਸਥਾਪਤ ਮਿਲੀ ਹੋਣ ਕਰਕੇ ਤੁਸੀਂ ਇਸ ਨੂੰ ਮਿਟਾ ਨਹੀਂ ਸਕਦੇ। ਬੰਦ ਕਰਕੇ, ਤੁਸੀਂ ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਇਹ ਐਪ ਬੰਦ ਕਰ ਅਤੇ ਲੁਕਾ ਰਹੇ ਹੋ।"</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"ਜੇ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਬੰਦ ਕਰਦੇ ਹੋ, ਤਾਂ ਹੋ ਸਕਦਾ ਹੈ ਕਿ Android ਅਤੇ ਹੋਰ ਐਪਾਂ ਉਸ ਤਰ੍ਹਾਂ ਕੰਮ ਨਾ ਕਰਨ ਜਿਵੇਂ ਇਨ੍ਹਾਂ ਤੋਂ ਉਮੀਦ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ, ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਵਿੱਚ ਇਹ ਐਪ ਪਹਿਲਾਂ ਤੋਂ ਸਥਾਪਤ ਮਿਲੀ ਹੋਣ ਕਰਕੇ ਤੁਸੀਂ ਇਸ ਨੂੰ ਮਿਟਾ ਨਹੀਂ ਸਕਦੇ। ਇਸਨੂੰ ਬੰਦ ਕਰਨ \'ਤੇ, ਇਹ ਐਪ ਬੰਦ ਹੋ ਜਾਵੇਗੀ ਅਤੇ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਦਿਖਾਈ ਨਹੀਂ ਦੇਵੇਗੀ।"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"ਇਜਾਜ਼ਤ ਪ੍ਰਬੰਧਕ"</string>
<string name="never_ask_again" msgid="4728762438198560329">"ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"</string>
<string name="no_permissions" msgid="3881676756371148563">"ਕੋਈ ਇਜਾਜ਼ਤਾਂ ਨਹੀਂ"</string>
@@ -163,7 +165,7 @@
<string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"ਪਿਛਲੇ 1 ਮਿੰਟ ਵਿੱਚ ਇਜਾਜ਼ਤ ਵਰਤੋਂ"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{ਪਿਛਲੇ # ਦਿਨ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}one{ਪਿਛਲੇ # ਦਿਨ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}other{ਪਿਛਲੇ # ਦਿਨਾਂ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}}"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{ਪਿਛਲੇ # ਘੰਟੇ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}one{ਪਿਛਲੇ # ਘੰਟੇ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}other{ਪਿਛਲੇ # ਘੰਟਿਆਂ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}}"</string>
- <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 ਐਪ ਵੱਲੋਂ ਇਜਾਜ਼ਤਾਂ ਵਰਤੀਆਂ ਗਈਆਂ}one{# ਐਪ ਵੱਲੋਂ ਇਜਾਜ਼ਤਾਂ ਵਰਤੀਆਂ ਗਈਆਂ}other{# ਐਪਾਂ ਵੱਲੋਂ ਇਜਾਜ਼ਤਾਂ ਵਰਤੀਆਂ ਗਈਆਂ}}"</string>
+ <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{ਇਜਾਜ਼ਤ 1 ਐਪ ਵੱਲੋਂ ਵਰਤੀ ਗਈ}one{ਇਜਾਜ਼ਤ # ਐਪ ਵੱਲੋਂ ਵਰਤੀ ਗਈ}other{ਇਜਾਜ਼ਤ # ਐਪਾਂ ਵੱਲੋਂ ਵਰਤੀ ਗਈ}}"</string>
<string name="permission_usage_view_details" msgid="6675335735468752787">"ਸਭ ਡੈਸ਼ਬੋਰਡ ਵਿੱਚ ਦੇਖੋ"</string>
<string name="app_permission_usage_filter_label" msgid="7182861154638631550">"ਇਸ ਮੁਤਾਬਕ ਫਿਲਟਰ ਕੀਤਾ ਗਿਆ: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"ਫਿਲਟਰ ਹਟਾਓ"</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"ਇਹ ਐਪਾਂ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ, ਕਾਰਵਾਈਆਂ ਅਤੇ ਇਨਪੁੱਟਾਂ ਨੂੰ ਦੇਖ ਸਕਦੀਆਂ ਹਨ, ਕਾਰਵਾਈਆਂ ਕਰ ਸਕਦੀਆਂ ਹਨ ਅਤੇ ਡਿਸਪਲੇ ਕੰਟਰੋਲ ਕਰ ਸਕਦੀਆਂ ਹਨ।"</string>
<string name="role_assistant_label" msgid="4727586018198208128">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਡਿਜੀਟਲ ਸਹਾਇਕ ਐਪ"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"ਡਿਜੀਟਲ ਸਹਾਇਕ ਐਪ"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"ਸਹਾਇਕ ਐਪਾਂ ਤੁਹਾਡੇ ਵੱਲੋਂ ਦੇਖੀ ਜਾਂਦੀ ਸਕ੍ਰੀਨ ਤੋਂ ਪ੍ਰਾਪਤ ਜਾਣਕਾਰੀ ਦੇ ਆਧਾਰ \'ਤੇ ਤੁਹਾਡੀ ਮਦਦ ਕਰ ਸਕਦੀਆਂ ਹਨ। ਕੁਝ ਐਪਾਂ ਤੁਹਾਨੂੰ ਏਕੀਕ੍ਰਿਤ ਸਹਾਇਤਾ ਦੇਣ ਲਈ ਲਾਂਚਰ ਅਤੇ ਵੌਇਸ ਇਨਪੁੱਟ ਸੇਵਾਵਾਂ ਦੋਵਾਂ ਦਾ ਸਮਰਥਨ ਕਰਦੇ ਹਨ।"</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"ਸਹਾਇਕ ਐਪਾਂ ਤੁਹਾਡੇ ਵੱਲੋਂ ਦੇਖੀ ਜਾਂਦੀ ਸਕ੍ਰੀਨ ਤੋਂ ਪ੍ਰਾਪਤ ਜਾਣਕਾਰੀ ਦੇ ਆਧਾਰ \'ਤੇ ਤੁਹਾਡੀ ਮਦਦ ਕਰ ਸਕਦੀਆਂ ਹਨ। ਕੁਝ ਐਪਾਂ ਤੁਹਾਨੂੰ ਏਕੀਕ੍ਰਿਤ ਸਹਾਇਤਾ ਦੇਣ ਲਈ ਲਾਂਚਰ ਅਤੇ ਵੌਇਸ ਇਨਪੁੱਟ ਸੇਵਾਵਾਂ ਦੋਵਾਂ ਦਾ ਸਮਰਥਨ ਕਰਦੀਆਂ ਹਨ।"</string>
<string name="role_browser_label" msgid="2877796144554070207">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਬ੍ਰਾਊਜ਼ਰ ਐਪ"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"ਬ੍ਰਾਊਜ਼ਰ ਐਪ"</string>
<string name="role_browser_description" msgid="3465253637499842671">"ਐਪਾਂ ਜੋ ਤੁਹਾਨੂੰ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦਿੰਦੀਆਂ ਹਨ ਅਤੇ ਤੁਹਾਡੇ ਵੱਲੋਂ ਟੈਪ ਕੀਤੇ ਲਿੰਕ ਦਿਖਾਉਂਦੀਆਂ ਹਨ"</string>
@@ -369,7 +371,7 @@
<string name="role_sms_search_keywords" msgid="8022048144395047352">"ਲਿਖਤ ਸੁਨੇਹਾ, ਲਿਖਤ ਭੇਜਣਾ, ਸੁਨੇਹੇ, ਸੁਨੇਹਾ ਭੇਜਣਾ"</string>
<string name="role_emergency_label" msgid="7028825857206842366">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੰਕਟਕਾਲੀਨ ਐਪ"</string>
<string name="role_emergency_short_label" msgid="2388431453335350348">"ਸੰਕਟਕਾਲੀਨ ਐਪ"</string>
- <string name="role_emergency_description" msgid="5051840234887686630">"ਐਪਾਂ ਜੋ ਤੁਹਾਨੂੰ ਤੁਹਾਡੀ ਡਾਕਟਰੀ ਜਾਣਕਾਰੀ ਰਿਕਾਰਡ ਕਰਨ ਅਤੇ ਇਸਨੂੰ ਸੰਕਟਕਾਲੀਨ ਮਦਦਗਾਰਾਂ ਤੱਕ ਪਹੁੰਚਯੋਗ ਬਣਾਉਣ ਦਿੰਦੀਆਂ ਹਨ; ਗੰਭੀਰ ਮੌਸਮੀ ਘਟਨਾਵਾਂ ਅਤੇ ਆਫ਼ਤਾਂ ਬਾਰੇ ਸੁਚੇਤਨਾਵਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਦਿੰਦੀਆਂ ਹਨ; ਤੁਹਾਨੂੰ ਮਦਦ ਦੀ ਲੋੜ ਪੈਣ \'ਤੇ ਹੋਰਾਂ ਨੂੰ ਸੂਚਿਤ ਕਰਨ ਦਿੰਦੀਆਂ ਹਨ"</string>
+ <string name="role_emergency_description" msgid="5051840234887686630">"ਐਪਾਂ ਜੋ ਤੁਹਾਨੂੰ ਤੁਹਾਡੀ ਡਾਕਟਰੀ ਜਾਣਕਾਰੀ ਰਿਕਾਰਡ ਕਰਨ ਅਤੇ ਇਸਨੂੰ ਐਮਰਜੈਂਸੀ ਮਦਦਗਾਰਾਂ ਤੱਕ ਪਹੁੰਚਯੋਗ ਬਣਾਉਣ ਦਿੰਦੀਆਂ ਹਨ; ਬਹੁਤ ਜ਼ਿਆਦਾ ਖਰਾਬ ਮੌਸਮੀ ਘਟਨਾਵਾਂ ਅਤੇ ਆਫ਼ਤਾਂ ਬਾਰੇ ਅਲਰਟ ਪ੍ਰਾਪਤ ਕਰਨ ਦਿੰਦੀਆਂ ਹਨ; ਤੁਹਾਨੂੰ ਮਦਦ ਦੀ ਲੋੜ ਪੈਣ \'ਤੇ ਹੋਰਾਂ ਨੂੰ ਸੂਚਿਤ ਕਰਨ ਦਿੰਦੀਆਂ ਹਨ"</string>
<string name="role_emergency_request_title" msgid="8469579020654348567">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਤੁਹਾਡੀ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੰਕਟਕਾਲੀਨ ਐਪ ਵਜੋਂ ਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
<string name="role_emergency_request_description" msgid="131645948770262850">"ਕਿਸੇ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"ice"</string>
@@ -398,8 +400,8 @@
<string name="role_app_streaming_description" msgid="7341638576226183992">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਤੁਹਾਡੀਆਂ ਸੂਚਨਾਵਾਂ ਨਾਲ ਅੰਤਰਕਿਰਿਆ ਕਰਨ ਅਤੇ ਤੁਹਾਡੀਆਂ ਐਪਾਂ ਨੂੰ ਕਨੈਕਟ ਕੀਤੇ ਡੀਵਾਈਸ \'ਤੇ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ।"</string>
<string name="role_companion_device_computer_description" msgid="416099879217066377">"ਇਹ ਸੇਵਾ ਤੁਹਾਡੀਆਂ ਫ਼ੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਸੂਚਨਾਵਾਂ ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਹੋਰ ਡੀਵਾਈਸਾਂ ਨਾਲ ਸਾਂਝਾ ਕਰਦੀ ਹੈ।"</string>
<string name="role_notes_label" msgid="7451627001058089536">"ਨੋਟ-ਕਥਨਾਂ ਲਈ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਐਪ"</string>
- <string name="role_notes_short_label" msgid="8796604147546125285">"ਨੋਟ-ਕਥਨਾਂ ਲਈ ਐਪ"</string>
- <string name="role_notes_description" msgid="8496852798616883551">"ਐਪਾਂ ਜੋ ਤੁਹਾਨੂੰ ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਨੋਟ-ਕਥਨ ਲੈਣ ਦੀ ਆਗਿਆ ਦਿੰਦੀਆਂ ਹਨ"</string>
+ <string name="role_notes_short_label" msgid="8796604147546125285">"ਨੋਟ ਲਈ ਐਪ"</string>
+ <string name="role_notes_description" msgid="8496852798616883551">"ਐਪਾਂ ਜੋ ਤੁਹਾਨੂੰ ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਨੋਟ ਲਿਖਣ ਦੀ ਸੁਵਿਧਾ ਦਿੰਦੀਆਂ ਹਨ"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"ਨੋਟ-ਕਥਨ"</string>
<string name="request_role_current_default" msgid="738722892438247184">"ਮੌਜੂਦਾ ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"</string>
@@ -466,9 +468,9 @@
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ ਲੱਭਣ, ਉਨ੍ਹਾਂ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਅਤੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨੂੰ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੇਣਾ ਹੈ? "<annotation id="link">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਆਗਿਆ ਦਿਓ।"</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"ਕੀ <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> ਦੀ ਟਿਕਾਣਾ ਪਹੁੰਚ ਨੂੰ ਅੰਦਾਜ਼ਨ ਤੋਂ ਸਹੀ ਟਿਕਾਣੇ \'ਤੇ ਬਦਲਣਾ ਹੈ?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਇਸ ਡੀਵਾਈਸ ਦੇ ਅੰਦਾਜ਼ਨ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣੀ ਹੈ?"</string>
- <string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"ਸਹੀ"</string>
+ <string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"ਸਟੀਕ"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"ਅੰਦਾਜ਼ਨ"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨੀ ਦੇਣੀ ਹੈ?"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ SMS ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਦੇਖਣ ਦੇਣੇ ਹਨ?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਫ਼ੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਫ਼ਾਈਲਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਇਸ ਡੀਵਾਈਸ \'ਤੇ &lt;b&gt;ਫ਼ੋਟੋਆਂ, ਵੀਡੀਓ, ਸੰਗੀਤ ਅਤੇ ਆਡੀਓ&lt;/b&gt; ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
@@ -483,7 +485,7 @@
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਲਈ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਪਹੁੰਚ ਨੂੰ ਬਦਲਣਾ ਹੈ?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"ਇਹ ਐਪ ਹਰ ਵੇਲੇ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗਦੀ ਹੈ, ਉਦੋਂ ਵੀ ਜਦੋਂ ਤੁਸੀਂ ਐਪ ਦੀ ਵਰਤੋਂ ਨਾ ਕਰ ਰਹੇ ਹੋਵੋ। "<annotation id="link">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਜਾਜ਼ਤ ਦਿਓ।"</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਆਪਣੀ ਸਰੀਰਕ ਸਰਗਰਮੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਤਸਵੀਰਾਂ ਖਿੱਚਣ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੇਣਾ ਹੈ?"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਤਸਵੀਰਾਂ ਖਿੱਚਣ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਐਪ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਹੀ ਐਪ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕੇਗੀ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕੇਗੀ"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਤਸਵੀਰਾਂ ਖਿੱਚਣ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣੀ ਹੈ?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"ਇਹ ਐਪ ਹਰ ਵੇਲੇ ਤਸਵੀਰਾਂ ਖਿੱਚਣ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਸਕਦੀ ਹੈ, ਉਦੋਂ ਵੀ ਜਦੋਂ ਤੁਸੀਂ ਐਪ ਦੀ ਵਰਤੋਂ ਨਾ ਕਰ ਰਹੇ ਹੋਵੋ। "<annotation id="link">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਜਾਜ਼ਤ ਦਿਓ।"</annotation></string>
@@ -520,18 +522,18 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"ਜਦੋਂ ਤੁਸੀਂ ਐਮਰਜੈਂਸੀ ਨੰਬਰ \'ਤੇ ਕਾਲ ਕਰਦੇ ਹੋ ਤਾਂ ਸ਼ਾਇਦ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਡਾਟਾ ਹਾਲੇ ਵੀ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"ਬਦਲੋ"</string>
- <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
+ <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"ਸਿਕਿਊਰਟੀ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"ਡੀਵਾਈਸ ਨੂੰ ਸਕੈਨ ਕਰੋ"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"ਖਾਰਜ ਕਰੋ"</string>
- <string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"ਕੀ ਇਸ ਸੁਚੇਤਨਾ ਨੂੰ ਖਾਰਜ ਕਰਨਾ ਹੈ?"</string>
- <string name="safety_center_issue_card_dismiss_confirmation_message" msgid="3775418736671093563">"ਹੋਰ ਸੁਰੱਖਿਆ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਕਿਸੇ ਵੀ ਸਮੇਂ ਆਪਣੀਆਂ ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ ਸੈਟਿੰਗਾਂ ਦੀ ਸਮੀਖਿਆ ਕਰੋ"</string>
+ <string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"ਕੀ ਇਸ ਅਲਰਟ ਨੂੰ ਖਾਰਜ ਕਰਨਾ ਹੈ?"</string>
+ <string name="safety_center_issue_card_dismiss_confirmation_message" msgid="3775418736671093563">"ਹੋਰ ਸੁਰੱਖਿਆ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਕਿਸੇ ਵੀ ਸਮੇਂ ਆਪਣੀਆਂ ਸਿਕਿਊਰਟੀ ਅਤੇ ਪਰਦੇਦਾਰੀ ਸੈਟਿੰਗਾਂ ਦੀ ਸਮੀਖਿਆ ਕਰੋ"</string>
<string name="safety_center_issue_card_confirm_dismiss_button" msgid="5884137843083634556">"ਖਾਰਜ ਕਰੋ"</string>
<string name="safety_center_issue_card_cancel_dismiss_button" msgid="2874578798877712346">"ਰੱਦ ਕਰੋ"</string>
<string name="safety_center_entries_category_title" msgid="34356964062813115">"ਸੈਟਿੰਗਾਂ"</string>
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ ਸਥਿਤੀ। <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"ਸੁਰੱਖਿਆ ਸੈਟਿੰਗਾਂ"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"ਇਜਾਜ਼ਤਾਂ"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"ਸਿਕਿਊਰਟੀ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"ਸਥਿਤੀ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"ਤੁਹਾਡੇ ਪਰਦੇਦਾਰੀ ਕੰਟਰੋਲ"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
diff --git a/PermissionController/res/values-pl/strings.xml b/PermissionController/res/values-pl/strings.xml
index a41e7459d..012c21327 100644
--- a/PermissionController/res/values-pl/strings.xml
+++ b/PermissionController/res/values-pl/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Więcej"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Zezwalaj na wszystko"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Zawsze zezwalaj na wszystko"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Zezwól na ograniczony dostęp"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Wybierz zdjęcia i filmy"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Wybierz więcej"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Nie wybieraj więcej"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikacje"</string>
<string name="app_permissions" msgid="3369917736607944781">"Uprawnienia aplikacji"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nieużywane aplikacje"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Edytuj zdjęcia wybrane dla tej aplikacji"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Brak nieużywanych aplikacji"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 nieużywanych aplikacji"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Ostatnie decyzje dotyczące uprawnień"</string>
@@ -70,7 +72,7 @@
<string name="denied_permission_decision" msgid="5308961501779563781">"Odmówiono aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> dostępu do: <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Dzisiaj}=1{1 dzień temu}few{# dni temu}many{# dni temu}other{# dnia temu}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Wyłącz aplikację"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"Jeśli wyłączysz tę aplikację, Android i inne aplikacje mogą działać nieprawidłowo. Pamiętaj, że nie możesz usunąć tej aplikacji, bo została ona fabrycznie zainstalowana na Twoim urządzeniu. Wyłączone aplikacje są ukrywane na urządzeniu."</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"Jeśli wyłączysz tę aplikację, Android i inne aplikacje mogą działać nieprawidłowo. Pamiętaj, że nie możesz usunąć tej aplikacji, bo została ona fabrycznie zainstalowana na Twoim urządzeniu. Wyłączona aplikacja zostanie ukryta na urządzeniu."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Menedżer uprawnień"</string>
<string name="never_ask_again" msgid="4728762438198560329">"Nie pytaj ponownie"</string>
<string name="no_permissions" msgid="3881676756371148563">"Brak uprawnień"</string>
@@ -340,7 +342,7 @@
<string name="no_apps_allowed" msgid="7718822655254468631">"Nie zezwolono żadnym aplikacjom"</string>
<string name="no_apps_allowed_full" msgid="8011716991498934104">"Brak aplikacji z uprawnieniami dla wszystkich plików"</string>
<string name="no_apps_allowed_scoped" msgid="4908850477787659501">"Brak aplikacji z uprawnieniami tylko dla multimediów"</string>
- <string name="no_apps_denied" msgid="7663435886986784743">"Nie zabroniono żadnym aplikacjom"</string>
+ <string name="no_apps_denied" msgid="7663435886986784743">"Nie zabroniono dostępu żadnym aplikacjom"</string>
<string name="car_permission_selected" msgid="180837028920791596">"Wybrana"</string>
<string name="settings" msgid="5409109923158713323">"Ustawienia"</string>
<string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"Usługa <xliff:g id="SERVICE_NAME">%s</xliff:g> ma pełny dostęp do urządzenia"</string>
@@ -362,7 +364,7 @@
<string name="role_dialer_request_description" msgid="6288839625724909320">"Ta aplikacja będzie miała dostęp do aparatu, kontaktów, mikrofonu, telefonu i SMS-ów"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"telefon"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Domyślna aplikacja SMS"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"Aplikacja do SMS-ów"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"aplikacja do SMS-ów"</string>
<string name="role_sms_description" msgid="3424020199148153513">"Aplikacje umożliwiające użycie Twojego numeru telefonu do wysyłania i odbierania SMS-ów, zdjęć, filmów i innych danych"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Czy aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> ma być domyślną aplikacją do SMS-ów?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Ta aplikacja będzie miała dostęp do aparatu, kontaktów, plików i multimediów, mikrofonu, telefonu i SMS-ów"</string>
@@ -495,7 +497,7 @@
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ta aplikacja chce mieć dostęp do danych z czujnika dotyczących parametrów życiowych przez cały czas, nawet kiedy jej nie używasz. Aby dokonać tej zmiany, "<annotation id="link">"otwórz ustawienia."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do danych z czujnika podstawowych funkcji życiowych?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Aby zezwolić tej aplikacji na dostęp do danych z czujników na ciele przez cały czas, nawet kiedy jej nie używasz, "<annotation id="link">"przejdź do ustawień"</annotation>"."</string>
- <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Czy nadal zezwalać aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do czujników na ciele podczas jej używania?"</string>
+ <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Czy nadal zezwalać aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do danych z czujników na ciele podczas jej używania?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na wysyłanie powiadomień?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Pozwolenia kontrolowane"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> ma dostęp do lokalizacji"</string>
diff --git a/PermissionController/res/values-pt-rBR-v34/strings.xml b/PermissionController/res/values-pt-rBR-v34/strings.xml
index 6e77e0432..d18d7e83c 100644
--- a/PermissionController/res/values-pt-rBR-v34/strings.xml
+++ b/PermissionController/res/values-pt-rBR-v34/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"Segurança e privacidade"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"Segurança física e privacidade"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Controles"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Conexão Saúde"</string>
<string name="health_connect_summary" msgid="815473513776882296">"Gerenciar o acesso de apps aos dados de saúde"</string>
diff --git a/PermissionController/res/values-pt-rBR/strings.xml b/PermissionController/res/values-pt-rBR/strings.xml
index 7bb03ff08..d33df3f05 100644
--- a/PermissionController/res/values-pt-rBR/strings.xml
+++ b/PermissionController/res/values-pt-rBR/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mais inform."</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permitir tudo"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Sempre permitir tudo"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Permitir acesso limitado"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Selecionar fotos e vídeos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Selecionar mais"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Não selecionar mais"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"Permissões do app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps não usados"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Editar fotos selecionadas para este app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nenhum app não usado"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 app não usado"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Decisões recentes de permissão"</string>
@@ -221,7 +223,7 @@
<string name="unused_apps_page_title" msgid="6986983535677572559">"Apps não usados"</string>
<string name="unused_apps_page_summary" msgid="1867593913217272155">"Se um app fica sem uso por alguns meses:\n\n• as permissões são removidas para proteger seus dados;\n• as notificações são interrompidas para economizar bateria;\n• os arquivos temporários são removidos para liberar espaço.\n\nPara retomar as permissões e notificações, abra o app."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Quando um app fica sem uso por um mês:\n\n• As permissões são removidas para proteger seus dados.\n• Os arquivos temporários são removidos para liberar espaço.\n\nSe quiser conceder as permissões novamente, abra o app."</string>
- <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Usado pela última vez há mais de # mês}one{Usado pela última vez há mais de # mês}many{Usado pela última vez há mais de # de meses}other{Usado pela última vez há mais de # meses}}"</string>
+ <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Usado pela última vez há mais de # mês}one{Usados pela última vez há mais de # mês}many{Usados pela última vez há mais de # de meses}other{Usados pela última vez há mais de # meses}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"App aberto pela última vez em <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="last_opened_summary_short" msgid="1646067226191176825">"Aberto pela última vez em <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"Se você permitir o gerenciamento de todos os arquivos, o app poderá acessar, modificar e excluir qualquer arquivo no armazenamento comum do dispositivo ou no armazenamento de dispositivos conectados. O app poderá acessar arquivos sem pedir autorização."</string>
@@ -246,8 +248,8 @@
<string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Nunca acessou"</string>
<string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Negado/nunca acessado"</string>
<string name="allowed_header" msgid="7769277978004790414">"Permitido"</string>
- <string name="allowed_always_header" msgid="6455903312589013545">"Permitido sempre"</string>
- <string name="allowed_foreground_header" msgid="6845655788447833353">"Permitido durante o uso"</string>
+ <string name="allowed_always_header" msgid="6455903312589013545">"Permitidos sempre"</string>
+ <string name="allowed_foreground_header" msgid="6845655788447833353">"Permitidos durante o uso"</string>
<string name="allowed_storage_scoped" msgid="5383645873719086975">"Com permissão para acessar apenas mídia"</string>
<string name="allowed_storage_full" msgid="5356699280625693530">"Com permissão para gerenciar todos os arquivos"</string>
<string name="ask_header" msgid="2633816846459944376">"Perguntar sempre"</string>
@@ -356,14 +358,14 @@
<string name="role_browser_request_title" msgid="2895200507835937192">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como navegador padrão?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"Nenhuma permissão necessária"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"App de telefone padrão"</string>
- <string name="role_dialer_short_label" msgid="7186888549465352489">"App Telefone"</string>
+ <string name="role_dialer_short_label" msgid="7186888549465352489">"App de Telefone"</string>
<string name="role_dialer_description" msgid="8768708633696539612">"Apps que permitem fazer e receber chamadas no seu dispositivo."</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como seu app de telefone padrão?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"Este app poderá acessar contatos, câmera, microfone, telefone e SMS"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"discador"</string>
<string name="role_sms_label" msgid="8456999857547686640">"App de SMS padrão"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"App de SMS"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Apps que permitem usar o número do telefone para enviar e receber mensagens de texto curtas, fotos, vídeos e muito mais."</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Apps que permitem usar seu número do telefone para enviar e receber mensagens de texto curtas, fotos, vídeos e muito mais."</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como app de SMS padrão?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Este app poderá acessar contatos, câmera, arquivos e mídia, microfone, telefone e SMS"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"mensagem de texto, enviar mensagens de texto, mensagens"</string>
@@ -426,7 +428,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Mais padrões"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Abrir links"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Padrão para trabalho"</string>
- <string name="default_app_none" msgid="9084592086808194457">"Nenhum"</string>
+ <string name="default_app_none" msgid="9084592086808194457">"Nenhuma"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Padrão do sistema)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nenhum app"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Selecionado"</string>
@@ -464,7 +466,7 @@
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Este app quer acessar sua localização o tempo todo, mesmo quando não estiver em uso. "<annotation id="link">"Permita o acesso nas configurações"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; encontre, conecte-se e determine a posição relativa de dispositivos por perto?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; encontre, conecte-se e determine a posição relativa de dispositivos por perto? "<annotation id="link">"Permita nas configurações."</annotation></string>
- <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Permitir a troca para que o app <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> acesse o local exato (em vez do aproximado)?"</string>
+ <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Mudar a permissão de acesso ao local do app <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>, de aproximado para exato?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse o local aproximado deste dispositivo?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exata"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Aproximada"</string>
@@ -608,8 +610,8 @@
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"Os dados de local podem ser compartilhados"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Este app indicou que pode compartilhar seus dados de local com terceiros"</string>
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Atualizações no compartilhamento de dados de local"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"Conferir apps que mudaram a forma de compartilhar dados de local"</string>
- <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Esses apps mudaram a forma de compartilhar os dados de local. É possível que eles não tenham compartilhado antes ou que agora compartilhem para fins de publicidade ou marketing."</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"Conferir apps que mudaram a forma de compartilhar seus dados de local"</string>
+ <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Esses apps mudaram a forma de compartilhar os dados de local. É possível que eles não os tenham compartilhado antes ou que agora os compartilhem para fins de publicidade ou marketing."</string>
<string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Os desenvolvedores desses apps oferecem informações sobre as práticas deles de compartilhamento de dados com uma app store. Eles podem atualizar essas informações com o tempo.\n\nAs práticas de compartilhamento de dados podem variar de acordo com a versão do app e com a idade, o uso e a região do usuário."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Saiba mais sobre o compartilhamento de dados"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Seus dados de local agora são compartilhados com terceiros"</string>
diff --git a/PermissionController/res/values-pt-rPT/strings.xml b/PermissionController/res/values-pt-rPT/strings.xml
index 012608eee..de2a09d61 100644
--- a/PermissionController/res/values-pt-rPT/strings.xml
+++ b/PermissionController/res/values-pt-rPT/strings.xml
@@ -29,11 +29,12 @@
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"Aplicação não encontrada"</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"Não permitir"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Não permitir e não perguntar novamente"</string>
- <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter \"Enquanto a app está a ser utilizada”"</string>
+ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter \"Enquanto a app está a ser usada”"</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter “Apenas desta vez”"</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mais informação"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permitir todos"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Permitir sempre todos"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Permitir acesso limitado"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Selecionar fotos e vídeos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Selecionar mais"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Não selecionar mais"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"Autorizações da app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps não usadas"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Edite as fotos selecionadas para esta app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nenhuma app não usada"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 apps não utilizadas"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Decisões de autorização recentes"</string>
@@ -193,7 +195,7 @@
<string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Localização exata"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Localização aproximada"</string>
- <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usar localização exata"</string>
+ <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Localização exata"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Quando a localização exata está desativada, as apps podem aceder à sua localização aproximada"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Autorização de <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Acesso desta app a <xliff:g id="PERM">%1$s</xliff:g>"</string>
@@ -203,8 +205,8 @@
<string name="unused_apps_category_title" msgid="2988455616845243901">"Definições de apps não usadas"</string>
<string name="auto_revoke_label" msgid="5068393642936571656">"Remover autorizações se a app não for utilizada"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Remover autorizações e libertar espaço"</string>
- <string name="unused_apps_label_v2" msgid="7058776770056517980">"Pausar atividade de apps, se não usadas"</string>
- <string name="unused_apps_summary" msgid="8839466950318403115">"Remova autorizações, elimine ficheiros temporários e pare notificações"</string>
+ <string name="unused_apps_label_v2" msgid="7058776770056517980">"Pausar atividade de apps se não usadas"</string>
+ <string name="unused_apps_summary" msgid="8839466950318403115">"Remover autorizações, eliminar ficheiros temporários e parar notificações"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Para proteger os seus dados, as autorizações desta app serão removidas se a mesma não for utilizada durante alguns meses."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Para proteger os seus dados, se a app não for utilizada há alguns meses, serão removidas as seguintes autorizações: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Para proteger os seus dados, foram removidas as autorizações para as apps que não utiliza há alguns meses."</string>
@@ -347,9 +349,9 @@
<string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"<xliff:g id="NUM_SERVICES">%s</xliff:g> aplicações de acessibilidade têm acesso total ao seu dispositivo"</string>
<string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"O serviço <xliff:g id="SERVICE_NAME">%s</xliff:g> pode ver o seu ecrã, as ações e as entradas, efetuar ações e controlar o ecrã."</string>
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Estas aplicações podem ver o ecrã, as ações e as entradas, efetuar ações e controlar o ecrã."</string>
- <string name="role_assistant_label" msgid="4727586018198208128">"App assistente digital predef."</string>
+ <string name="role_assistant_label" msgid="4727586018198208128">"App assistente digital predefinida"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"App assistente digital"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"As apps de assistência podem ajudá-lo com base em informações do ecrã que está a ver. Algumas apps são compatíveis com serviços de iniciação e de entrada de texto por voz para oferecer assistência integrada."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"As apps de assistência podem ser-lhe úteis com base em informações do ecrã que está a ver. Algumas apps são compatíveis com serviços de iniciação e de entrada de texto por voz para oferecer assistência integrada."</string>
<string name="role_browser_label" msgid="2877796144554070207">"App navegador predefinida"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"App de navegador"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Apps que lhe dão acesso à Internet e apresentam links em que pode tocar."</string>
@@ -357,13 +359,13 @@
<string name="role_browser_request_description" msgid="5888803407905985941">"Não são necessárias autorizações."</string>
<string name="role_dialer_label" msgid="1100224146343237968">"App de telefone predefinida"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"App Telefone"</string>
- <string name="role_dialer_description" msgid="8768708633696539612">"Apps que permitem efetuar e receber chamadas no seu dispositivo."</string>
+ <string name="role_dialer_description" msgid="8768708633696539612">"Apps que permitem fazer e receber chamadas no seu dispositivo"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"Quer definir o <xliff:g id="APP_NAME">%1$s</xliff:g> como a app de telefone predefinida?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"Esta app fica com acesso à sua Câmara, Contactos, Microfone, Telefone e SMS"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"telefone"</string>
<string name="role_sms_label" msgid="8456999857547686640">"App de SMS predefinida"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"App de SMS"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Apps que permitem utilizar o seu número de telefone para enviar e receber mensagens de texto, fotos, vídeos e muito mais."</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"app de SMS"</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Apps que permitem usar o seu número de telefone para enviar e receber mensagens de texto, fotos, vídeos e muito mais."</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Quer definir o <xliff:g id="APP_NAME">%1$s</xliff:g> como app SMS predefinida?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Esta app fica com acesso à sua Câmara, Contactos, Ficheiros e multimédia, Microfone, Telefone e SMS"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"mensagem de texto, enviar mensagens de texto, mensagens"</string>
@@ -375,7 +377,7 @@
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"em caso de emergência"</string>
<string name="role_home_label" msgid="3871847846649769412">"App página inicial predefinida"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"App Página inicial"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Apps, frequentemente denominadas iniciadores, que substituem os ecrãs principais no dispositivo Android e dão acesso aos conteúdos e às funcionalidades do seu dispositivo."</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Também conhecidas por \"launcher\" são apps que substituem os ecrãs principais no Android e dão acesso aos conteúdos e às funcionalidades do seu dispositivo"</string>
<string name="role_home_request_title" msgid="738136983453341081">"Quer definir o <xliff:g id="APP_NAME">%1$s</xliff:g> como a app Página inicial predefinida?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Não são necessárias autorizações."</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"iniciador"</string>
@@ -432,7 +434,7 @@
<string name="car_default_app_selected" msgid="5416420830430644174">"Selecionada"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selecionada – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
<string name="special_app_access_search_keyword" msgid="8032347212290774210">"acesso especial a apps"</string>
- <string name="special_app_access" msgid="5019319067120213797">"Acesso especial a apps"</string>
+ <string name="special_app_access" msgid="5019319067120213797">"Acesso especial para apps"</string>
<string name="no_special_app_access" msgid="6950277571805106247">"Sem acesso especial a app"</string>
<string name="special_app_access_no_apps" msgid="4102911722787886970">"Sem apps"</string>
<string name="home_missing_work_profile_support" msgid="1756855847669387977">"Não suporta o perfil de trabalho."</string>
@@ -490,7 +492,7 @@
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Quer alterar o acesso à câmara para a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Esta app quer tirar fotos e gravar vídeos sempre, mesmo quando não a está a utilizar. "<annotation id="link">"Permita-o nas Definições."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda aos registos de chamadas do seu telemóvel?"</string>
- <string name="permgrouprequest_phone" msgid="1829234136997316752">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faça e gira chamadas telefónicas?"</string>
+ <string name="permgrouprequest_phone" msgid="1829234136997316752">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; passe a fazer e gerir chamadas telefónicas?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda aos dados de sensores acerca dos seus sinais vitais?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Esta app quer aceder sempre aos dados de sensores sobre os seus sinais vitais, mesmo quando não a estiver a usar. Para fazer esta alteração, "<annotation id="link">"aceda às definições."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda aos dados de sensores acerca dos seus sinais vitais?"</string>
@@ -583,9 +585,9 @@
<string name="mic_toggle_description" msgid="9163104307990677157">"Para apps e serviços. Se esta definição estiver desativada, os dados do microfone ainda podem ser partilhados quando ligar para um número de emergência."</string>
<string name="location_settings_subtitle" msgid="2328360561197430695">"Veja as apps e os serviços que têm acesso à localização"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Mostrar acesso à área de transferência"</string>
- <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Apresente uma mensagem quando as apps acedem a texto, imagens ou outro conteúdo que copiou"</string>
+ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostra uma mensagem quando as apps acedem a texto, imagens ou outro conteúdo que copiou"</string>
<string name="show_password_title" msgid="2877269286984684659">"Mostrar palavras-passe"</string>
- <string name="show_password_summary" msgid="1110166488865981610">"Apresente rapidamente os carateres ao escrever"</string>
+ <string name="show_password_summary" msgid="1110166488865981610">"Mostra brevemente os carateres ao escrever"</string>
<string name="permission_rationale_message_location" msgid="2153841534298068414">"Esta app declarou que pode partilhar dados de localização com terceiros"</string>
<string name="permission_rationale_location_title" msgid="2404797182678793506">"Localização e partilha de dados"</string>
<string name="permission_rationale_data_sharing_source_title" msgid="6874604543125814316">"Qual é a origem das informações da partilha de dados"</string>
@@ -608,8 +610,8 @@
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"Os dados de localização podem ser partilhados"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Esta app declarou que pode partilhar os seus dados de localização com terceiros"</string>
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Atualizações da partilha de dados para a localização"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"Reveja apps que mudaram a forma como podem partilhar os seus dados de localização"</string>
- <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Estas apps mudaram a forma como podem partilhar os seus dados de localização. Podem não os ter partilhado antes ou podem partilhá-los agora para fins de publicidade ou marketing."</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"Reveja apps que mudaram a forma de partilhar os seus dados de localização"</string>
+ <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Estas apps mudaram a forma de partilhar os seus dados de localização. É possível que não os tenham partilhado antes ou que passem a partilhá-los agora para fins de publicidade ou marketing."</string>
<string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Os programadores destas apps deram informações sobre as respetivas práticas de partilha de dados a uma loja de apps. Podem atualizá-las ao longo do tempo.\n\nAs práticas de partilha de dados podem variar consoante a versão da app, a utilização, a região e a idade."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Saiba mais sobre a partilha de dados"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Os seus dados de localização são agora partilhados com terceiros"</string>
diff --git a/PermissionController/res/values-pt-v34/strings.xml b/PermissionController/res/values-pt-v34/strings.xml
index 6e77e0432..d18d7e83c 100644
--- a/PermissionController/res/values-pt-v34/strings.xml
+++ b/PermissionController/res/values-pt-v34/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"Segurança e privacidade"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"Segurança física e privacidade"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Controles"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Conexão Saúde"</string>
<string name="health_connect_summary" msgid="815473513776882296">"Gerenciar o acesso de apps aos dados de saúde"</string>
diff --git a/PermissionController/res/values-pt/strings.xml b/PermissionController/res/values-pt/strings.xml
index 7bb03ff08..d33df3f05 100644
--- a/PermissionController/res/values-pt/strings.xml
+++ b/PermissionController/res/values-pt/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mais inform."</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permitir tudo"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Sempre permitir tudo"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Permitir acesso limitado"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Selecionar fotos e vídeos"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Selecionar mais"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Não selecionar mais"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Apps"</string>
<string name="app_permissions" msgid="3369917736607944781">"Permissões do app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps não usados"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Editar fotos selecionadas para este app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nenhum app não usado"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 app não usado"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Decisões recentes de permissão"</string>
@@ -221,7 +223,7 @@
<string name="unused_apps_page_title" msgid="6986983535677572559">"Apps não usados"</string>
<string name="unused_apps_page_summary" msgid="1867593913217272155">"Se um app fica sem uso por alguns meses:\n\n• as permissões são removidas para proteger seus dados;\n• as notificações são interrompidas para economizar bateria;\n• os arquivos temporários são removidos para liberar espaço.\n\nPara retomar as permissões e notificações, abra o app."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Quando um app fica sem uso por um mês:\n\n• As permissões são removidas para proteger seus dados.\n• Os arquivos temporários são removidos para liberar espaço.\n\nSe quiser conceder as permissões novamente, abra o app."</string>
- <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Usado pela última vez há mais de # mês}one{Usado pela última vez há mais de # mês}many{Usado pela última vez há mais de # de meses}other{Usado pela última vez há mais de # meses}}"</string>
+ <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Usado pela última vez há mais de # mês}one{Usados pela última vez há mais de # mês}many{Usados pela última vez há mais de # de meses}other{Usados pela última vez há mais de # meses}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"App aberto pela última vez em <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="last_opened_summary_short" msgid="1646067226191176825">"Aberto pela última vez em <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"Se você permitir o gerenciamento de todos os arquivos, o app poderá acessar, modificar e excluir qualquer arquivo no armazenamento comum do dispositivo ou no armazenamento de dispositivos conectados. O app poderá acessar arquivos sem pedir autorização."</string>
@@ -246,8 +248,8 @@
<string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Nunca acessou"</string>
<string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Negado/nunca acessado"</string>
<string name="allowed_header" msgid="7769277978004790414">"Permitido"</string>
- <string name="allowed_always_header" msgid="6455903312589013545">"Permitido sempre"</string>
- <string name="allowed_foreground_header" msgid="6845655788447833353">"Permitido durante o uso"</string>
+ <string name="allowed_always_header" msgid="6455903312589013545">"Permitidos sempre"</string>
+ <string name="allowed_foreground_header" msgid="6845655788447833353">"Permitidos durante o uso"</string>
<string name="allowed_storage_scoped" msgid="5383645873719086975">"Com permissão para acessar apenas mídia"</string>
<string name="allowed_storage_full" msgid="5356699280625693530">"Com permissão para gerenciar todos os arquivos"</string>
<string name="ask_header" msgid="2633816846459944376">"Perguntar sempre"</string>
@@ -356,14 +358,14 @@
<string name="role_browser_request_title" msgid="2895200507835937192">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como navegador padrão?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"Nenhuma permissão necessária"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"App de telefone padrão"</string>
- <string name="role_dialer_short_label" msgid="7186888549465352489">"App Telefone"</string>
+ <string name="role_dialer_short_label" msgid="7186888549465352489">"App de Telefone"</string>
<string name="role_dialer_description" msgid="8768708633696539612">"Apps que permitem fazer e receber chamadas no seu dispositivo."</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como seu app de telefone padrão?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"Este app poderá acessar contatos, câmera, microfone, telefone e SMS"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"discador"</string>
<string name="role_sms_label" msgid="8456999857547686640">"App de SMS padrão"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"App de SMS"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Apps que permitem usar o número do telefone para enviar e receber mensagens de texto curtas, fotos, vídeos e muito mais."</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Apps que permitem usar seu número do telefone para enviar e receber mensagens de texto curtas, fotos, vídeos e muito mais."</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como app de SMS padrão?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Este app poderá acessar contatos, câmera, arquivos e mídia, microfone, telefone e SMS"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"mensagem de texto, enviar mensagens de texto, mensagens"</string>
@@ -426,7 +428,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Mais padrões"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Abrir links"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Padrão para trabalho"</string>
- <string name="default_app_none" msgid="9084592086808194457">"Nenhum"</string>
+ <string name="default_app_none" msgid="9084592086808194457">"Nenhuma"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Padrão do sistema)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nenhum app"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Selecionado"</string>
@@ -464,7 +466,7 @@
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Este app quer acessar sua localização o tempo todo, mesmo quando não estiver em uso. "<annotation id="link">"Permita o acesso nas configurações"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; encontre, conecte-se e determine a posição relativa de dispositivos por perto?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; encontre, conecte-se e determine a posição relativa de dispositivos por perto? "<annotation id="link">"Permita nas configurações."</annotation></string>
- <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Permitir a troca para que o app <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> acesse o local exato (em vez do aproximado)?"</string>
+ <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Mudar a permissão de acesso ao local do app <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>, de aproximado para exato?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse o local aproximado deste dispositivo?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exata"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Aproximada"</string>
@@ -608,8 +610,8 @@
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"Os dados de local podem ser compartilhados"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Este app indicou que pode compartilhar seus dados de local com terceiros"</string>
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Atualizações no compartilhamento de dados de local"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"Conferir apps que mudaram a forma de compartilhar dados de local"</string>
- <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Esses apps mudaram a forma de compartilhar os dados de local. É possível que eles não tenham compartilhado antes ou que agora compartilhem para fins de publicidade ou marketing."</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"Conferir apps que mudaram a forma de compartilhar seus dados de local"</string>
+ <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Esses apps mudaram a forma de compartilhar os dados de local. É possível que eles não os tenham compartilhado antes ou que agora os compartilhem para fins de publicidade ou marketing."</string>
<string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Os desenvolvedores desses apps oferecem informações sobre as práticas deles de compartilhamento de dados com uma app store. Eles podem atualizar essas informações com o tempo.\n\nAs práticas de compartilhamento de dados podem variar de acordo com a versão do app e com a idade, o uso e a região do usuário."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Saiba mais sobre o compartilhamento de dados"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Seus dados de local agora são compartilhados com terceiros"</string>
diff --git a/PermissionController/res/values-ro/strings.xml b/PermissionController/res/values-ro/strings.xml
index defbf3177..af7101200 100644
--- a/PermissionController/res/values-ro/strings.xml
+++ b/PermissionController/res/values-ro/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mai multe info."</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Permite-le pe toate"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Permite-le întotdeauna pe toate"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Permite cu acces limitat"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Alege fotografii și videoclipuri"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Selectează mai multe"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Nu selecta mai multe"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplicații"</string>
<string name="app_permissions" msgid="3369917736607944781">"Permisiuni pentru aplicații"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplicații nefolosite"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Editează fotografiile selectate pentru această aplicație"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nu există aplicații nefolosite"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 aplicații nefolosite"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Decizii recente privind permisiunile"</string>
@@ -349,7 +351,7 @@
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Aceste aplicații pot vedea ecranul, acțiunile și textul introdus, să facă acțiuni și să controleze afișajul."</string>
<string name="role_assistant_label" msgid="4727586018198208128">"Aplicația asistent digital prestabilită"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"Aplicația asistent digital"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"Aplicațiile asistent te pot ajuta pe baza informațiilor din ecranul afișat. Pentru a-ți oferi o asistență integrată, unele aplicații acceptă atât serviciile cu lansatoare, cât și pe cele de intrare vocală."</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"Aplicațiile asistent te pot ajuta pe baza informațiilor din ecranul afișat. Pentru a-ți oferi o asistență integrată, unele aplicații acceptă atât serviciile cu lansatoare, cât și pe cele de intrare vocală."</string>
<string name="role_browser_label" msgid="2877796144554070207">"Aplicația browser prestabilită"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Aplicația browser"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Aplicații care îți oferă acces la internet și afișează linkurile pe care le atingi"</string>
@@ -357,7 +359,7 @@
<string name="role_browser_request_description" msgid="5888803407905985941">"Nu este nevoie de permisiuni"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"Aplicația pentru telefon"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"Aplicația Telefon"</string>
- <string name="role_dialer_description" msgid="8768708633696539612">"Aplicațiile cu ajutorul cărora faci și primești apeluri telefonice pe dispozitiv"</string>
+ <string name="role_dialer_description" msgid="8768708633696539612">"Aplicațiile cu ajutorul cărora efectuezi și primești apeluri telefonice pe dispozitiv"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"Setezi <xliff:g id="APP_NAME">%1$s</xliff:g> ca aplicație prestabilită pentru telefon?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"Aplicația va avea acces la Camera foto, Agendă, Microfon, Telefon și SMS"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"tastatură telefon"</string>
@@ -427,7 +429,7 @@
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Deschiderea linkurilor"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Prestabilite pentru serviciu"</string>
<string name="default_app_none" msgid="9084592086808194457">"Niciuna"</string>
- <string name="default_app_system_default" msgid="6218386768175513760">"(Valoare prestabilită de sistem)"</string>
+ <string name="default_app_system_default" msgid="6218386768175513760">"(Prestabilită de sistem)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nicio aplicație"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Selectată"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selectat - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-ru-v34/strings.xml b/PermissionController/res/values-ru-v34/strings.xml
index 64a927b69..cd4d6c683 100644
--- a/PermissionController/res/values-ru-v34/strings.xml
+++ b/PermissionController/res/values-ru-v34/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"Защита и конфиденциальность"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"Защита и кон­фи­ден­циаль­ность"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Настройки"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Здоровье и спорт"</string>
<string name="health_connect_summary" msgid="815473513776882296">"Настроить доступ приложений к данным о здоровье"</string>
diff --git a/PermissionController/res/values-ru/strings.xml b/PermissionController/res/values-ru/strings.xml
index 3c8d4ea58..90e886326 100644
--- a/PermissionController/res/values-ru/strings.xml
+++ b/PermissionController/res/values-ru/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Подробнее"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Разрешить ко всем"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Постоянный полный доступ"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Разрешить ограниченный доступ"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Выбрать фотографии и видео"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Выбрать больше"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Не выбирать"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Приложения"</string>
<string name="app_permissions" msgid="3369917736607944781">"Разрешения приложений"</string>
<string name="unused_apps" msgid="2058057455175955094">"Неиспользуемые приложения"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Изменить список фото для этого приложения"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Неиспользуемых приложений нет"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Нет неиспользуемых приложений"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Действия с разрешениями"</string>
@@ -163,7 +165,7 @@
<string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Разрешения, использованные за последнюю минуту"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{С момента последнего использования прошел # день}one{С момента последнего использования прошел # день}few{С момента последнего использования прошло # дня}many{С момента последнего использования прошло # дней}other{С момента последнего использования прошло # дня}}"</string>
<string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{С момента последнего использования прошел # час}one{С момента последнего использования прошел # час}few{С момента последнего использования прошло # часа}many{С момента последнего использования прошло # часов}other{С момента последнего использования прошло # часа}}"</string>
- <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Использует 1 приложение}one{Использует # приложение}few{Используют # приложения}many{Используют # приложений}other{Используют # приложения}}"</string>
+ <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Использовано 1 приложением}one{Использовано # приложением}few{Использовано # приложениями}many{Использовано # приложениями}other{Использовано # приложения}}"</string>
<string name="permission_usage_view_details" msgid="6675335735468752787">"Показать все в панели управления"</string>
<string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Фильтр: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"Удалить фильтр"</string>
@@ -193,7 +195,7 @@
<string name="app_permission_button_deny" msgid="6016454069832050300">"Запретить"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Точное местоположение"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Приблизительное местоположение"</string>
- <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Использовать точное местоположение"</string>
+ <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Точное местоположение"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Если определение точного местоположения отключено, приложения могут использовать данные о примерном местоположении устройства"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Разрешение \"<xliff:g id="PERM">%1$s</xliff:g>\""</string>
<string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g>: доступ для этого приложения"</string>
@@ -219,7 +221,7 @@
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"Отозваны разрешения \"<xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g>\" и \"<xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g>\"."</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"Отозвано разрешение \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\" и ещё <xliff:g id="NUMBER">%2$s</xliff:g>."</string>
<string name="unused_apps_page_title" msgid="6986983535677572559">"Неиспользуемые приложения"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"Если вы не пользовались приложением несколько месяцев:\n\n• разрешения будут отозваны для защиты ваших данных;\n• уведомления будут отключены для экономии заряда батареи;\n• временные файлы будут удалены, чтобы освободить место.\n\nЧтобы заново предоставить разрешения и включить уведомления, откройте приложение."</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"Если вы не пользовались приложением несколько месяцев:\n\n• Разрешения будут отозваны, чтобы защитить ваши данные\n• Уведомления будут отключены, чтобы экономить заряд батареи\n• Временные файлы будут удалены, чтобы освободить место\n\nЧтобы заново предоставить разрешения и включить уведомления, откройте приложение."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Если вы не пользовались приложением в течение месяца:\n\n• разрешения будут отключены для защиты ваших данных;\n• временные файлы будут удалены, чтобы освободить место.\n\nЧтобы заново предоставить разрешения, откройте приложение."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Использовались более # месяца назад}one{Использовались более # месяца назад}few{Использовались более # месяцев назад}many{Использовались более # месяцев назад}other{Использовались более # месяца назад}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"Вы открывали это приложение <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -464,7 +466,7 @@
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Этому приложению требуется доступ к вашему местоположению, даже когда вы им не пользуетесь. Предоставьте разрешение в "<annotation id="link">"настройках"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; находить устройства поблизости, подключаться к ним и определять их относительное положение?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; находить устройства поблизости, подключаться к ним и определять их относительное положение? "<annotation id="link">"Открыть настройки"</annotation></string>
- <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Изменить местоположение в приложении \"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\" с приблизительного на точное?"</string>
+ <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Разрешить приложению \"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\" доступ к данным о точном местоположении вместо приблизительного?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным о приблизительном местоположении устройства?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Точно"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Приблизительно"</string>
@@ -491,12 +493,12 @@
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Это приложение делает фотографии и снимает видео в любое время, даже когда вы им не пользуетесь. "<annotation id="link">"Предоставьте разрешение в настройках."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к списку вызовов?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; совершать звонки и управлять ими?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным датчиков о состоянии организма?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Этому приложению требуется доступ к данным нательных датчиков, даже когда вы им не пользуетесь. Предоставить разрешение можно в "<annotation id="link">"настройках"</annotation>"."</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Разрешить &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным нательных датчиков?"</string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Это приложение запрашивает постоянный доступ к данным нательных датчиков, даже в то время, когда вы им не пользуетесь. Изменить его разрешения можно в "<annotation id="link">"настройках"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Разрешить приложению \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" доступ к данным датчиков на теле?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Чтобы предоставить приложению доступ к данным нательных датчиков, даже когда вы им не пользуетесь, "<annotation id="link">"измените настройки"</annotation>"."</string>
- <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Предоставлять приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным нательных датчиков, только когда оно используется?"</string>
- <string name="permgrouprequest_notifications" msgid="6396739062335106181">"Разрешить <xliff:g id="APP_NAME">%1$s</xliff:g> отправлять уведомления?"</string>
+ <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Оставить для приложения &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным нательных датчиков, только когда оно используется?"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; отправлять уведомления?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Контролируемые разрешения"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"У приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" есть доступ к геоданным"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"В вашей организации приложению \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" разрешен доступ к геоданным."</string>
@@ -520,7 +522,7 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Для приложений и сервисов"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Данные микрофона могут передаваться при звонке на номер экстренной службы"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Изменить"</string>
- <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Защита и конфиденциальность"</string>
+ <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Защита и кон­фи­ден­циаль­ность"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Сканировать устройство"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Закрыть"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"Закрыть оповещение?"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"Статус безопасности и конфиденциальности. <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"Настройки безопасности"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"Разрешения"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Защита и конфиденциальность"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Защита и кон­фи­ден­циаль­ность"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Проверьте статус."</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Ваши настройки конфиденциальности"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"Другие настройки"</string>
@@ -607,15 +609,15 @@
<string name="app_permission_rationale_message" msgid="8511466916077100713">"Безопасность данных"</string>
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"Возможна передача геоданных"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Это приложение уведомило, что оно может передавать данные о вашем местоположении третьим лицам."</string>
- <string name="data_sharing_updates_title" msgid="7996933386875213859">"Обновления в передаче данных о местоположении"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"Проверить приложения, которые изменили подход к передаче данных о вашем местоположении"</string>
- <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Эти приложения изменили подход к передаче данных о вашем местоположении. Возможно, такие сведения ранее не передавались или теперь передаются в рекламных или маркетинговых целях."</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Разработчики этих приложений предоставили магазину информацию о своем подходе к передаче данных. Эти сведения могут обновляться.\n\nПодход к передаче данных может зависеть от версии приложения, способа его использования, а также от вашего региона и возраста."</string>
+ <string name="data_sharing_updates_title" msgid="7996933386875213859">"Изменения в передаче геоданных"</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"Проверить приложения, в которых изменились разрешения на передачу геоданных"</string>
+ <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"В этих приложениях изменились разрешения на передачу геоданных. Возможно, эти данные ранее не передавались вообще или теперь передаются в рекламных или маркетинговых целях."</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Разработчики этих приложений указали в магазине приложений, как их приложения передают данные. Эта информация может изменяться.\n\nРазрешения на передачу данных могут зависеть от версии приложения, способа его использования, а также от вашего региона и возраста."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Сведения о передаче данных"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"Теперь данные о вашем местоположении передаются третьим лицам."</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Теперь данные о вашем местоположении передаются третьим лицам в рекламных или маркетинговых целях."</string>
<string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Обновлено за последний день}=1{Обновлено за последний день}one{Обновлено за последний # день}few{Обновлено за последние # дня}many{Обновлено за последние # дней}other{Обновлено за последние # дня}}"</string>
- <string name="no_updates_at_this_time" msgid="9031085635689982935">"Обновлений пока нет"</string>
+ <string name="no_updates_at_this_time" msgid="9031085635689982935">"Изменений нет"</string>
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Обновление сведений о передаче данных"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Некоторые приложения изменили подход к передаче данных о вашем местоположении."</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Настройки"</string>
diff --git a/PermissionController/res/values-si/strings.xml b/PermissionController/res/values-si/strings.xml
index 1899c6e70..06623151a 100644
--- a/PermissionController/res/values-si/strings.xml
+++ b/PermissionController/res/values-si/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"තවත් තතු"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"සියල්ලට ඉඩ දෙන්න"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"සැම විටම සියල්ලට ඉඩ දෙන්න"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"සීමිත ප්‍රවේශයට ඉඩ දෙන්න"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ඡායාරූප සහ වීඩියෝ තෝරන්න"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"තවත් තෝරන්න"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"තවත් තෝරා නොගන්න"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"යෙදුම්"</string>
<string name="app_permissions" msgid="3369917736607944781">"යෙදුම් අවසර"</string>
<string name="unused_apps" msgid="2058057455175955094">"භාවිත නොකළ යෙදුම්"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"මෙම යෙදුම සඳහා තෝරන ලද ඡායාරූප සංස්කරණය කරන්න"</string>
<string name="no_unused_apps" msgid="12809387670415295">"භාවිත නොකළ යෙදුම් නැත"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"භාවිත නොකළ යෙදුම් 0"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"මෑත අවසර තීරණ"</string>
diff --git a/PermissionController/res/values-sk/strings.xml b/PermissionController/res/values-sk/strings.xml
index ae8ea524c..dd4856297 100644
--- a/PermissionController/res/values-sk/strings.xml
+++ b/PermissionController/res/values-sk/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Ďalšie info"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Povoliť všetko"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Vždy povoliť všetko"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Povoliť obmedzený prístup"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Vybrať fotky a videá"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Vybrať ďalšie"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Nevybrať ďalšie"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikácie"</string>
<string name="app_permissions" msgid="3369917736607944781">"Povolenia aplikácií"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nepoužívané aplikácie"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Upraviť vybrané fotky pre túto aplikáciu"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Žiadne nepoužívané aplikácie"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Žiadne nepoužívané aplikácie"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nedávne rozhodnutia o povolení"</string>
@@ -84,7 +86,7 @@
<string name="app_permissions_group_summary" msgid="8788419008958284002">"Povolené pri <xliff:g id="COUNT_0">%1$d</xliff:g> z(o) <xliff:g id="COUNT_1">%2$d</xliff:g> aplik."</string>
<string name="app_permissions_group_summary2" msgid="4329922444840521150">"Počet povolených aplikácií: <xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
<string name="menu_show_system" msgid="4254021607027872504">"Zobraziť systémové"</string>
- <string name="menu_hide_system" msgid="3855390843744028465">"Skryť systémové"</string>
+ <string name="menu_hide_system" msgid="3855390843744028465">"Skryť systémové aplikácie"</string>
<string name="menu_show_7_days_data" msgid="8979611198508523706">"Zobraziť 7 dní"</string>
<string name="menu_show_24_hours_data" msgid="8228054833323380780">"Zobraziť 24 hodín"</string>
<string name="manage_permission" msgid="2895385393037061964">"Spravovať povolenie"</string>
@@ -219,7 +221,7 @@
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"Boli odstránené povolenia <xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g> a <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g>"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"Bolo odstránené povolenie <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> a ďalšie (<xliff:g id="NUMBER">%2$s</xliff:g>)"</string>
<string name="unused_apps_page_title" msgid="6986983535677572559">"Nepoužívané aplikácie"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"Ak aplikácia nebola používaná niekoľko mesiacov:\n\n• povolenia budú z dôvodu ochrany vašich údajov odstránené,\n• upozornenia sa zastavia, aby sa šetrila batéria,\n• dočasné súbory budú odstránené, aby sa uvoľnilo miesto.\n\nAk budete chcieť povolenia a upozornenia znova aktivovať, otvorte aplikáciu."</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"Ak aplikácia nebola používaná niekoľko mesiacov:\n\n• povolenia budú z dôvodu ochrany vašich údajov odstránené;\n• upozornenia sa zastavia, aby sa šetrila batéria;\n• dočasné súbory budú odstránené, aby sa uvoľnilo miesto.\n\nAk budete chcieť povolenia a upozornenia znova aktivovať, otvorte aplikáciu."</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Ak aplikácia nebola použitá mesiac:\n\n• povolenia budú z dôvodu ochrany vašich údajov odstránené;\n• dočasné súbory budú odstránené, aby sa uvoľnilo miesto.\n\nAk budete chcieť povolenia znova udeliť, otvorte aplikáciu."</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Naposledy otvorené pred viac než # mesiacom}few{Naposledy otvorené pred viac než # mesiacmi}many{Naposledy otvorené pred viac než # mesiaca}other{Naposledy otvorené pred viac než # mesiacmi}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"Aplikácia bola naposledy otvorená <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -267,7 +269,7 @@
<string name="unused_apps_notification_content" msgid="9195026773244581246">"Povolenia a dočasné súbory boli odstránené a upozornenia boli zastavené. Skontrolujte to klepnutím."</string>
<string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Skontrolujte aplikácie s odstránenými povoleniami"</string>
<string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"V prípade aplikácií, ktoré ste dlhšie nepoužívali, boli povolenia a dočasné súbory odstránené a upozornenia deaktivované."</string>
- <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"Skontrolovať aplikácie"</string>
+ <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"Kontrola aplikácií"</string>
<string name="post_drive_permission_decision_reminder_title" msgid="1290697371418139976">"Skontrolujte nedávne povolenia"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Počas jazdy ste udelili aplikácii <xliff:g id="APP">%1$s</xliff:g> povolenie <xliff:g id="PERMISSION">%2$s</xliff:g>"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Počas jazdy ste udelili aplikácii <xliff:g id="APP">%1$s</xliff:g> prístup k povoleniam <xliff:g id="PERMISSION_1">%2$s</xliff:g> &amp; <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string>
@@ -362,8 +364,8 @@
<string name="role_dialer_request_description" msgid="6288839625724909320">"Tejto aplikácii bude udelený prístup k vašej kamere, kontaktom, mikrofónu, telefónu a správam SMS"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"vytáčanie"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Predvolená aplikácia na SMS"</string>
- <string name="role_sms_short_label" msgid="4371444488034692243">"Aplikácia na SMS"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Aplikácie, ktoré vám umožňujú prostredníctvom vášho telefónneho čísla odosielať a prijímať krátke textové správy, fotky, videá a ďalší obsah"</string>
+ <string name="role_sms_short_label" msgid="4371444488034692243">"aplikáciou na SMS"</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Aplikácie, ktoré vám pomocou telefónneho čísla umožňujú posielať a prijímať krátke textové správy, fotky, videá a iné"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Chcete <xliff:g id="APP_NAME">%1$s</xliff:g> nastaviť ako predvolenú aplikáciu na SMS?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Tejto aplikácii bude udelený prístup k vašej kamere, kontaktom, súborom a médiám, mikrofónu, telefónu aj správam SMS"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"textová správa, poslať textovú správu, správy, posielanie textových správ"</string>
@@ -427,7 +429,7 @@
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Otváranie odkazov"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Predvolené na prácu"</string>
<string name="default_app_none" msgid="9084592086808194457">"Žiadna"</string>
- <string name="default_app_system_default" msgid="6218386768175513760">"(Predvolená systémová)"</string>
+ <string name="default_app_system_default" msgid="6218386768175513760">"(Predvolené systémom)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Žiadne aplikácie"</string>
<string name="car_default_app_selected" msgid="5416420830430644174">"Vybraná"</string>
<string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Vybrané – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
@@ -468,11 +470,11 @@
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k približnej polohe tohto zariadenia?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Presná"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Približná"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup ku kalendáru?"</string>
- <string name="permgrouprequest_sms" msgid="5672063688745420991">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; odosielať a zobrazovať správy SMS?"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup ku kalendáru?"</string>
+ <string name="permgrouprequest_sms" msgid="5672063688745420991">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; odosielať a zobrazovať správy SMS?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k fotkám, médiám a súborom v zariadení?"</string>
- <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k &lt;b&gt;fotkám, videám, hudbe a zvuku&lt;/b&gt; v tomto zariadení?"</string>
- <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k &lt;b&gt;fotkám, videám, hudbe, zvuku a ďalším súborom&lt;/b&gt; v tomto zariadení?"</string>
+ <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k &lt;b&gt;fotkám, videám, hudbe a zvuku&lt;/b&gt; v tomto zariadení?"</string>
+ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k &lt;b&gt;fotkám, videám, hudbe, zvuku a ďalším súborom&lt;/b&gt; v tomto zariadení?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k hudbe a zvuku v tomto zariadení?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k fotkám a videám v tomto zariadení?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k ďalším fotkám a videám v tomto zariadení?"</string>
@@ -482,14 +484,14 @@
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Táto aplikácia môže požadovať nepretržitý prístup k nahrávaniu zvuku, aj keď ju nepoužívate. "<annotation id="link">"Povolíte ho v nastaveniach."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Chcete aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; zmeniť prístup k mikrofónu?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Táto aplikácia požaduje nepretržitý prístup k nahrávaniu zvuku, aj keď ju nepoužívate. "<annotation id="link">"Povolíte ho v nastaveniach."</annotation></string>
- <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k vašej fyzickej aktivite?"</string>
+ <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k vašej fyzickej aktivite?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotiť a nahrávať video?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Táto aplikácia bude môcť fotiť a nahrávať videá iba vtedy, keď ju budete používať"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotiť a nahrávať videá?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Táto aplikácia môže požadovať nepretržitý prístup k foteniu a nahrávaniu videí, aj keď ju nepoužívate. "<annotation id="link">"Povolíte ho v nastaveniach."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Chcete aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; zmeniť prístup k fotoaparátu?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Táto aplikácia požaduje nepretržitý prístup k foteniu a nahrávaniu videí, aj keď ju nepoužívate. "<annotation id="link">"Povolíte ho v nastaveniach."</annotation></string>
- <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k zoznamu tel. hovorov?"</string>
+ <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k zoznamu hovorov?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uskutočňovať a spravovať telefonické hovory?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; používať údaje senzorov o vašich životných funkciách?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Táto aplikácia požaduje nepretržitý prístup k dátam senzorov o vašich životných funkciách, a to aj vtedy, keď ju nepoužívate. Povolíte to "<annotation id="link">"v nastaveniach"</annotation>"."</string>
@@ -505,7 +507,7 @@
<string name="not_used_permissions_description" msgid="7595514824169388718">"Povolenia používané iba systémovými aplikáciami."</string>
<string name="additional_permissions_label" msgid="7693557637462569046">"Ďalšie povolenia"</string>
<string name="additional_permissions_description" msgid="2186611950890732112">"Povolenia definované aplikáciami."</string>
- <string name="privdash_label_camera" msgid="1426440033626198096">"Kamera"</string>
+ <string name="privdash_label_camera" msgid="1426440033626198096">"Fotoaparát"</string>
<string name="privdash_label_microphone" msgid="8415035835803511693">"Mikrofón"</string>
<string name="privdash_label_location" msgid="6882400763866489291">"Poloha"</string>
<string name="privdash_label_other" msgid="3710394147423236033">"Iné"</string>
@@ -581,9 +583,9 @@
<string name="mic_toggle_title" msgid="2649991093496110162">"Prístup k mikrofónu"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Pre aplikácie a služby"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Pre aplikácie a služby. Keď je toto nastavenie vypnuté a zavoláte na tiesňovú linku, údaje mikrofónu sa môžu stále zdieľať."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"Zobraziť aplikácie a služby, ktoré majú prístup k polohe"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"Zoznam aplikácií a služieb, ktoré majú prístup k polohe"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Zobrazovať prístup k schránke"</string>
- <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Zobrazovať správu, keď aplikácie získajú pristup k textu, obrázkom alebo inému obsahu, ktorý ste skopírovali"</string>
+ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Zobrazovať správu, keď aplikácie získajú prístup k textu, obrázkom alebo inému obsahu, ktorý ste skopírovali"</string>
<string name="show_password_title" msgid="2877269286984684659">"Zobrazovať heslá"</string>
<string name="show_password_summary" msgid="1110166488865981610">"Pri písaní nakrátko zobrazovať zadávané znaky"</string>
<string name="permission_rationale_message_location" msgid="2153841534298068414">"V tejto aplikácii bolo uvedené, že môže zdieľať údaje o polohe s tretími stranami"</string>
@@ -608,7 +610,7 @@
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"Môžu sa zdieľať údaje o polohe"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"V tejto aplikácii je uvedené, že môže zdieľať údaje o polohe s tretími stranami"</string>
<string name="data_sharing_updates_title" msgid="7996933386875213859">"Aktualizácie zdieľania údajov o polohe"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"Skontrolujte aplikácie, ktoré zmenili spôsob zdieľania údajov o vašej polohe"</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"Kontrola aplikácií, ktoré zmenili spôsob zdieľania údajov o vašej polohe"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Tieto aplikácie zmenili spôsob zdieľania údajov o vašej polohe. Možno ich v minulosti nezdieľali alebo ich teraz zdieľajú na účely reklamy a marketingu."</string>
<string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Vývojári týchto aplikácií poskytli informácie o ich postupoch zdieľania údajov v obchode s aplikáciami. Priebežne ich môžu aktualizovať.\n\nPostupy zdieľania údajov sa môžu líšiť v závislosti od verzie a používania vašej aplikácie, ako aj regiónu a veku jej používateľa."</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"Ďalšie informácie o zdieľaní údajov"</string>
diff --git a/PermissionController/res/values-sl/strings.xml b/PermissionController/res/values-sl/strings.xml
index 5265a0724..ceee467c0 100644
--- a/PermissionController/res/values-sl/strings.xml
+++ b/PermissionController/res/values-sl/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Več informacij"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Dovoli vse"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Vedno dovoli vse"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Dovoli omejen dostop"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Izberi fotografije in videoposnetke"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Izberi več"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Brez izbire dodatnih"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikacije"</string>
<string name="app_permissions" msgid="3369917736607944781">"Dovoljenja za aplikacije"</string>
<string name="unused_apps" msgid="2058057455175955094">"Neuporabljene aplikacije"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Urejanje izbranih fotografij za to aplikacijo"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ni neuporabljenih aplikacij."</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 neuporabljenih aplikacij"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Nedavne odločitve o dovoljenjih"</string>
@@ -84,7 +86,7 @@
<string name="app_permissions_group_summary" msgid="8788419008958284002">"Dovoljene aplikacije: <xliff:g id="COUNT_0">%1$d</xliff:g> od <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
<string name="app_permissions_group_summary2" msgid="4329922444840521150">"Dovoljene aplikacije: <xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
<string name="menu_show_system" msgid="4254021607027872504">"Prikaži sistemske procese"</string>
- <string name="menu_hide_system" msgid="3855390843744028465">"Skrivanje sistemskih procesov"</string>
+ <string name="menu_hide_system" msgid="3855390843744028465">"Skrij sistemske procese"</string>
<string name="menu_show_7_days_data" msgid="8979611198508523706">"Pokaži 7 dni"</string>
<string name="menu_show_24_hours_data" msgid="8228054833323380780">"Pokaži 24 ur"</string>
<string name="manage_permission" msgid="2895385393037061964">"Upravljanje dovoljenja"</string>
@@ -491,12 +493,12 @@
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ta aplikacija želi vseskozi snemati fotografije in videoposnetke, tudi ko aplikacije ne uporabljate. "<annotation id="link">"To omogočite v nastavitvah."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do dnevnikov klicev v telefonu?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; opravljanje in upravljanje telefonskih klicev?"</string>
- <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do podatkov tipala o vitalnih znakih?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do podatkov tipal o vitalnih znakih?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ta aplikacija želi stalen dostop do podatkov tipal vitalnih znakov, tudi ko aplikacije ne uporabljate. Če želite izvesti to spremembo, "<annotation id="link">"pojdite v nastavitve"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti dostop do podatkov tipal o vitalnih znakih?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Če želite tej aplikaciji omogočiti stalen dostop do podatkov tipal telesnih funkcij, tudi ko je ne uporabljate, "<annotation id="link">"pojdite v nastavitve"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Ali želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; še naprej dovoliti dostop do podatkov tipal telesnih funkcij, ko je v uporabi?"</string>
- <string name="permgrouprequest_notifications" msgid="6396739062335106181">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti, da vam pošilja obvestila?"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti, da vam pošilja obvestila?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Zunanje upravljana dovoljenja"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> ima dostop do lokacije"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Vaša organizacija aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dovoljuje dostop do vaše lokacije."</string>
diff --git a/PermissionController/res/values-sq/strings.xml b/PermissionController/res/values-sq/strings.xml
index bfda0b905..451764b4b 100644
--- a/PermissionController/res/values-sq/strings.xml
+++ b/PermissionController/res/values-sq/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Më shumë info."</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Lejoji të gjitha"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Lejoji gjithmonë të gjitha"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Lejo qasjen e kufizuar"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Zgjidh fotografi dhe video"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Zgjidh më shumë"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Mos zgjidh të tjera"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikacionet"</string>
<string name="app_permissions" msgid="3369917736607944781">"Lejet e aplikacionit"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplikacionet e papërdorura"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Modifiko fotografitë e zgjedhura për këtë aplikacion"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nuk ka aplikacione të papërdorura"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 aplikacione të papërdorura"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Vendimet e fundit për lejet"</string>
@@ -384,7 +386,7 @@
<string name="role_call_redirection_description" msgid="6091669882014664420">"Aplikacione që të lejojnë t\'i transferosh telefonatat dalëse te një numër tjetër telefoni"</string>
<string name="role_call_redirection_request_title" msgid="2816244455003562925">"Dëshiron ta caktosh <xliff:g id="APP_NAME">%1$s</xliff:g> si aplikacionin e parazgjedhur për ridrejtimin e telefonatave?"</string>
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"Nuk ka nevojë për asnjë autorizim"</string>
- <string name="role_call_screening_label" msgid="883935222060878724">"Aplikacioni i parazgjedhur për ID-në dhe telefonuesit e bezdisshëm"</string>
+ <string name="role_call_screening_label" msgid="883935222060878724">"Apl. i parazgjedhur për ID-në dhe telefonuesit e bezdisshëm"</string>
<string name="role_call_screening_short_label" msgid="2048465565063130834">"Aplikacioni për ID-në e telefonuesit dhe telefonuesit e bezdisshëm"</string>
<string name="role_call_screening_description" msgid="2349431420497468981">"Aplikacionet që të lejojnë të identifikosh telefonatat dhe të bllokosh komunikimet e bezdisshme, telefonatat e automatizuara ose numrat e padëshiruar"</string>
<string name="role_call_screening_request_title" msgid="7358309224566977290">"Dëshiron të caktosh <xliff:g id="APP_NAME">%1$s</xliff:g> si apl. e parazgjedhur për ID-në e telefonuesit dhe filtrimin telefonatave të bezdisshme?"</string>
diff --git a/PermissionController/res/values-sr/strings.xml b/PermissionController/res/values-sr/strings.xml
index 0fd94a069..6d90000a3 100644
--- a/PermissionController/res/values-sr/strings.xml
+++ b/PermissionController/res/values-sr/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Више информација"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Дозволи све"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Увек дозволи све"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Дозволи ограничен приступ"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Изаберите слике и видео снимке"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Изаберите још"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Ништа више"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Апликације"</string>
<string name="app_permissions" msgid="3369917736607944781">"Дозволе за апликације"</string>
<string name="unused_apps" msgid="2058057455175955094">"Апликације које се не користе"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Мењај изабране слике за ову апликацију"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Нема аплик. које се не користе"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 апликац. које се не користе"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Недавне одлуке о дозволама"</string>
@@ -93,7 +95,7 @@
<string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> пружа услуге локације за овај уређај. Приступ локацији можете да измените у подешавањима локације."</string>
<string name="system_warning" msgid="1173400963234358816">"Ако одбијете ову дозволу, основне функције уређаја можда неће више исправно радити."</string>
<string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Ова апликација је дизајнирана за старију верзију Android-а. Ако овој апликацији одбијете приступ за слике и видео снимке, повлачи се и приступ музици и другим аудио снимцима."</string>
- <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Ова апликација је дизајнирана за старију верзију Android-а. Ако овој апликацији одбијете приступ за музику и друге аудио снимке, повлачи се и приступ сликама и видео снимцима."</string>
+ <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Ова апликација је дизајнирана за старију верзију Android-а. Ако овој апликацији одбијете приступ за музику и друге аудио снимке, повлачи се и приступ сликама и видеима."</string>
<string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"Ако одбијете ову дозволу, неке функције уређаја којима управља ова апликација можда неће више исправно радити."</string>
<string name="permission_summary_enforced_by_policy" msgid="4443598170942950519">"Примењује се у складу са смерницама"</string>
<string name="permission_summary_disabled_by_policy_background_only" msgid="221995005556362660">"Приступ у позадини је онемогућен смерницама"</string>
@@ -240,7 +242,7 @@
<string name="permission_description_summary_sms" msgid="725999468547768517">"Апликације са овом дозволом могу да шаљу и прегледају SMS-ове"</string>
<string name="permission_description_summary_storage" msgid="6575759089065303346">"Апликације са овом дозволом могу да приступају сликама, медијском садржају и фајловима на уређају"</string>
<string name="permission_description_summary_read_media_aural" msgid="3354728149930482199">"Апликације са овом дозволом могу да приступају музици и другим аудио фајловима на овом уређају"</string>
- <string name="permission_description_summary_read_media_visual" msgid="4991801977881732641">"Апликације са овом дозволом могу да приступају сликама и видео снимцима на овом уређају"</string>
+ <string name="permission_description_summary_read_media_visual" msgid="4991801977881732641">"Апликације са овом дозволом могу да приступају сликама и видеима на овом уређају"</string>
<string name="app_permission_most_recent_summary" msgid="4292074449384040590">"Последњи приступ: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
<string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"Тренутно одбијено/последњи приступ: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
<string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Без приступа"</string>
@@ -373,7 +375,7 @@
<string name="role_emergency_request_title" msgid="8469579020654348567">"Желите ли да подесите <xliff:g id="APP_NAME">%1$s</xliff:g> као подразумевану апликацију за хитне случајеве?"</string>
<string name="role_emergency_request_description" msgid="131645948770262850">"Није потребна ниједна дозвола"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"у хитном случају"</string>
- <string name="role_home_label" msgid="3871847846649769412">"Подраз. апл. почетне странице"</string>
+ <string name="role_home_label" msgid="3871847846649769412">"Подразумевана апл. почетне странице"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Апликација почетне странице"</string>
<string name="role_home_description" msgid="7997371519626556675">"Апликације које се често зову покретачи и замењују почетне екране на Android уређају и пружају приступ садржају и функцијама на уређају"</string>
<string name="role_home_request_title" msgid="738136983453341081">"Желите ли да подесите <xliff:g id="APP_NAME">%1$s</xliff:g> као подразумевану апликацију почетне странице?"</string>
@@ -385,13 +387,13 @@
<string name="role_call_redirection_request_title" msgid="2816244455003562925">"Желите ли да подесите <xliff:g id="APP_NAME">%1$s</xliff:g> као подразумевану апликацију за преусмеравање позива?"</string>
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"Није потребна ниједна дозвола"</string>
<string name="role_call_screening_label" msgid="883935222060878724">"Апликација за ИД позиваоца и непожељне поруке"</string>
- <string name="role_call_screening_short_label" msgid="2048465565063130834">"Апл. за ИД поз. и непож. поз."</string>
+ <string name="role_call_screening_short_label" msgid="2048465565063130834">"ИД позиваоца и непожељни позиви"</string>
<string name="role_call_screening_description" msgid="2349431420497468981">"Апликације које вам омогућавају да идентификујете позиве, блокирате непожељне и аутоматизоване позиве и нежељене бројеве"</string>
<string name="role_call_screening_request_title" msgid="7358309224566977290">"Желите ли да подесите <xliff:g id="APP_NAME">%1$s</xliff:g> као подразумевану апликацију за ИД позиваоца и непожељне поруке?"</string>
<string name="role_call_screening_request_description" msgid="7338511921032446006">"Није потребна ниједна дозвола"</string>
<string name="role_automotive_navigation_label" msgid="2701890757955474751">"Подразумевана апликација за навигацију"</string>
<string name="role_automotive_navigation_short_label" msgid="5165823092506922457">"Апликација за навигацију"</string>
- <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Апликације које могу да пружају претрагу занимљивих места и детаљну помоћ при навигацији"</string>
+ <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Апликације које могу да пружају претрагу тачака од интереса и детаљну помоћ при навигацији"</string>
<string name="role_automotive_navigation_request_title" msgid="7525693151489384300">"Желите да подесите апликацију <xliff:g id="APP_NAME">%1$s</xliff:g> као подразумевану апликацију за навигацију?"</string>
<string name="role_automotive_navigation_request_description" msgid="7073023813249245540">"Није потребна ниједна дозвола"</string>
<string name="role_watch_description" msgid="267003778693177779">"<xliff:g id="APP_NAME">%1$s</xliff:g> ће добити дозволу за интеракцију са обавештењима и приступ дозволама за телефон, SMS поруке, контакте и календар."</string>
@@ -436,7 +438,7 @@
<string name="no_special_app_access" msgid="6950277571805106247">"Нема посебног приступа апл."</string>
<string name="special_app_access_no_apps" msgid="4102911722787886970">"Нема апликација"</string>
<string name="home_missing_work_profile_support" msgid="1756855847669387977">"Не подржава пословни профил"</string>
- <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Напомена: Ако рестартујете уређај и подесили сте закључавање екрана, ова апликација не може да се покрене док не откључате уређај."</string>
+ <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Напомена: Ако рестартујете уређај и подесили сте откључавање екрана, ова апликација не може да се покрене док не откључате уређај."</string>
<string name="assistant_confirmation_message" msgid="7476540402884416212">"Помоћник ће моћи да чита информације о апликацијама које се користе у систему, укључујући информације видљиве на екрану или којима може да се приступа у оквиру апликација."</string>
<string name="incident_report_channel_name" msgid="3144954065936288440">"Дељење података о отклањању грешака"</string>
<string name="incident_report_notification_title" msgid="4635984625656519773">"Делите детаљне податке за отклањање грешака?"</string>
@@ -472,10 +474,10 @@
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; шаље и прегледа SMS-ове?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа сликама, медијским и другим фајловима на уређају?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Приступ &lt;b&gt;сликама, видеу, музици и звуку&lt;/b&gt; на уређају за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Приступ сликама, видеу, музици, звуку и другом на уређају за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Дозвољавате ли приступ музици и звуку на овом уређају за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Дозвољавате ли приступ сликама и видеу на овом уређају за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgrouprequest_more_photos" msgid="128933814654231321">"Дозвољавате ли да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа и другим сликама и видео снимцима на овом уређају?"</string>
+ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Дозволићете приступ сликама, видеу, музици, звуку и другом на уређају за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Дозволићете приступ музици и звуку на овом уређају за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Дозвољавате да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа сликама и видеу на овом уређају?"</string>
+ <string name="permgrouprequest_more_photos" msgid="128933814654231321">"Дозвољавате ли да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа и другим сликама и видеима на овом уређају?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима звук?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Апликација ће моћи да снима звук само док користите апликацију"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима звук?"</string>
@@ -483,16 +485,16 @@
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Желите да промените приступ микрофону за апликацију &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Ова апликација жели да снима звук све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Желите ли да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа физичким активностима?"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима слике и видео снимке?"</string>
- <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Апликација ће моћи да снима слике и видео снимке само док користите апликацију"</string>
- <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима слике и видео снимке?"</string>
- <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ова апликација можда жели да снима слике и видео снимке све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима слике и видео?"</string>
+ <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Апликација ће моћи да снима слике и видео само док користите апликацију"</string>
+ <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима слике и видео?"</string>
+ <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ова апликација можда жели да снима слике и видео све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Желите да промените приступ камери за апликацију &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
- <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ова апликација жели да снима слике и видео снимке све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string>
+ <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ова апликација жели да снима слике и видео све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа евиденцијама позива на телефону?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; упућује позиве и управља њима?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа подацима сензора о виталним функцијама?"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ова апликација жели да све време приступа подацима сензора о виталним функцијама, чак и када не користите апликацију. Да бисте обавили ову измену, "<annotation id="link">"идите у подешавања."</annotation></string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ова апликација жели да све време приступа подацима сензора о виталним функцијама, чак и када не користите апликацију. Да бисте то променили, "<annotation id="link">"идите у подешавања."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Желите да омогућите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа подацима сензора о виталним функцијама?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Да бисте дозволили овој апликацији да све време приступа подацима сензора за тело, чак и када не користите апликацију, "<annotation id="link">"идите у подешавања."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Желите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; и даље приступа подацима сензора за тело док се апликација користи?"</string>
@@ -558,18 +560,18 @@
<string name="media_confirm_dialog_title_a_to_p_aural_deny" msgid="7841428716317307685">"Ни приступ другим фајловима неће бити дозвољен"</string>
<string name="media_confirm_dialog_title_a_to_p_visual_allow" msgid="6469086448310893751">"Биће дозвољен приступ и другим фајловима"</string>
<string name="media_confirm_dialog_title_a_to_p_visual_deny" msgid="5767849609024384226">"Ни приступ другим фајловима неће бити дозвољен"</string>
- <string name="media_confirm_dialog_title_q_to_s_aural_allow" msgid="3191904399336990537">"Биће дозвољен приступ и сликама и видео снимцима"</string>
- <string name="media_confirm_dialog_title_q_to_s_aural_deny" msgid="3128147568953297969">"Ни приступ сликама и видео снимцима неће бити дозвољен"</string>
+ <string name="media_confirm_dialog_title_q_to_s_aural_allow" msgid="3191904399336990537">"Биће дозвољен приступ и сликама и видеима"</string>
+ <string name="media_confirm_dialog_title_q_to_s_aural_deny" msgid="3128147568953297969">"Ни приступ сликама и видеима неће бити дозвољен"</string>
<string name="media_confirm_dialog_title_q_to_s_visual_allow" msgid="6310682466493330434">"Биће дозвољен приступ и музици и аудио фајловима"</string>
<string name="media_confirm_dialog_title_q_to_s_visual_deny" msgid="1123845663785900471">"Ни приступ музици и аудио фајловима неће бити дозвољен"</string>
- <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација може да приступа музици и аудио фајловима, биће јој дозвољено и да приступа сликама, видео снимцима и другим фајловима."</string>
- <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација не може да приступа музици и аудио фајловима, неће јој бити дозвољено ни да приступа сликама, видео снимцима и другим фајловима."</string>
- <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација може да приступа сликама и видео снимцима, биће јој дозвољено и да приступа музици, аудио и другим фајловима."</string>
- <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација не може да приступа сликама и видео снимцима, неће јој бити дозвољено ни да приступа музици, аудио и другим фајловима."</string>
- <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација може да приступа музици и аудио фајловима, биће јој дозвољено и да приступа сликама и видео снимцима."</string>
- <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација не може да приступа музици и аудио фајловима, неће јој бити дозвољено ни да приступа сликама и видео снимцима."</string>
- <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација може да приступа сликама и видео снимцима, биће јој дозвољено и да приступа музици и аудио фајловима."</string>
- <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација не може да приступа музици и аудио фајловима, неће јој бити дозвољено ни да приступа сликама и видео снимцима."</string>
+ <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација може да приступа музици и аудио фајловима, биће јој дозвољено и да приступа сликама, видеима и другим фајловима."</string>
+ <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација не може да приступа музици и аудио фајловима, неће јој бити дозвољено ни да приступа сликама, видеима и другим фајловима."</string>
+ <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација може да приступа сликама и видеима, биће јој дозвољено и да приступа музици, аудио и другим фајловима."</string>
+ <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација не може да приступа сликама и видеима, неће јој бити дозвољено ни да приступа музици, аудио и другим фајловима."</string>
+ <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација може да приступа музици и аудио фајловима, биће јој дозвољено и да приступа сликама и видеима."</string>
+ <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација не може да приступа музици и аудио фајловима, неће јој бити дозвољено ни да приступа сликама и видеима."</string>
+ <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација може да приступа сликама и видеима, биће јој дозвољено и да приступа музици и аудио фајловима."</string>
+ <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Ова апликација не подржава најновију верзију Android-а. Ако ова апликација не може да приступа музици и аудио фајловима, неће јој бити дозвољено ни да приступа сликама и видеима."</string>
<string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"Прегледајте апликацију са приступом локацији у позадини"</string>
<string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> увек може да приступа вашој локацији, чак и кад је апликација затворена"</string>
<string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Прегледајте апликацију са приступом локацији у позадини"</string>
diff --git a/PermissionController/res/values-sv/strings.xml b/PermissionController/res/values-sv/strings.xml
index 7d53baf1a..a9bf7b25f 100644
--- a/PermissionController/res/values-sv/strings.xml
+++ b/PermissionController/res/values-sv/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mer info"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Tillåt alla"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Tillåt alltid alla"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Tillåt begränsad åtkomst"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Välj foton och videor"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Välj fler"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Välj inte fler"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Appar"</string>
<string name="app_permissions" msgid="3369917736607944781">"Appbehörigheter"</string>
<string name="unused_apps" msgid="2058057455175955094">"Appar som inte används"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Redigera urvalet av foton för appen"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Inga appar som inte används"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 appar som inte används"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Senaste behörighetsbesluten"</string>
@@ -350,12 +352,12 @@
<string name="role_assistant_label" msgid="4727586018198208128">"Digital assistentapp, standard"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"Digital assistentapp"</string>
<string name="role_assistant_description" msgid="6622458130459922952">"Med assistentappar kan du få hjälp som baseras på den information som visas på den aktuella skärmen. Vissa appar har stöd för både översikts- och röstinmatningstjänster för att hjälpa dig."</string>
- <string name="role_browser_label" msgid="2877796144554070207">"Standard webbläsarapp"</string>
+ <string name="role_browser_label" msgid="2877796144554070207">"Standard­webbläsarapp"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"Webbläsarapp"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Appar som visar länkar du trycker på och du använder för att ansluta till internet"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"Vill du ställa in <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardwebbläsarapp?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"Inga behörigheter krävs"</string>
- <string name="role_dialer_label" msgid="1100224146343237968">"Standard telefonapp"</string>
+ <string name="role_dialer_label" msgid="1100224146343237968">"Standardtelefonapp"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"Telefonapp"</string>
<string name="role_dialer_description" msgid="8768708633696539612">"Appar som möjliggör att ringa och ta emot telefonsamtal på enheten"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"Vill du ställa in <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardtelefonapp?"</string>
@@ -375,7 +377,7 @@
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"ice"</string>
<string name="role_home_label" msgid="3871847846649769412">"Standard startskärmsapp"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"Startskärmsapp"</string>
- <string name="role_home_description" msgid="7997371519626556675">"Appar som kallas Översikter ersätter startskärmarna på Android-enheten. Du får åtkomst till innehåll och funktioner på enheten via översikten."</string>
+ <string name="role_home_description" msgid="7997371519626556675">"Appar som kallas appstartare ersätter startskärmarna på Android-enheten. Du får åtkomst till innehåll och funktioner på enheten via appstartaren."</string>
<string name="role_home_request_title" msgid="738136983453341081">"Vill du ställa in <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardapp för startskärm?"</string>
<string name="role_home_request_description" msgid="2658833966716057673">"Inga behörigheter krävs"</string>
<string name="role_home_search_keywords" msgid="3830755001192666285">"översikt"</string>
diff --git a/PermissionController/res/values-sw-v34/strings.xml b/PermissionController/res/values-sw-v34/strings.xml
index be6f2f72b..8d56e3cdd 100644
--- a/PermissionController/res/values-sw-v34/strings.xml
+++ b/PermissionController/res/values-sw-v34/strings.xml
@@ -17,11 +17,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"Usalama na faragha"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"Ulinzi na faragha"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Vidhibiti"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"Dhibiti uwezo wa programu wa kufikia data ya afya"</string>
<string name="location_settings" msgid="8863940440881290182">"Uwezo wa kufikia mahali"</string>
- <string name="mic_toggle_description" msgid="1504101620086616040">"Kwenye programu na huduma. Mipangilio hii ikizimwa, data ya maikrofoni bado inaweza ikashirikiwa unapopiga nambari ya dharura"</string>
+ <string name="mic_toggle_description" msgid="1504101620086616040">"Kwenye programu na huduma. Mipangilio hii ikizimwa, data ya maikrofoni bado inaweza ikashirikiwa unapopiga namba ya dharura"</string>
<string name="location_settings_subtitle" msgid="6846532794702613851">"Kwenye programu na huduma"</string>
</resources>
diff --git a/PermissionController/res/values-sw/strings.xml b/PermissionController/res/values-sw/strings.xml
index 83a4ebe03..dbc7a69b2 100644
--- a/PermissionController/res/values-sw/strings.xml
+++ b/PermissionController/res/values-sw/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Maelezo zaidi"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Ruhusu zote"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Ruhusu zote kila wakati"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Ruhusu kufikia baadhi ya vipengele"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Chagua picha na video"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Chagua zaidi"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Usichague zaidi"</string>
@@ -60,12 +61,13 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Programu"</string>
<string name="app_permissions" msgid="3369917736607944781">"Ruhusa za programu"</string>
<string name="unused_apps" msgid="2058057455175955094">"Programu zisizotumika"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Badilisha picha ulizochagua za programu hii"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Hakuna programu zisizotumika"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Hakuna programu zisizotumika"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Uamuzi wa ruhusa wa hivi majuzi"</string>
<string name="review_permission_decisions_view_all" msgid="90391040431566130">"Angalia uamuzi wa ruhusa zote za hivi majuzi"</string>
<string name="review_permission_decisions_empty" msgid="8120775336417279806">"Hakuna uamuzi wa ruhusa wa hivi majuzi"</string>
- <string name="auto_permission_manager_summary" msgid="9157438376234301354">"Dhibiti ufikiaji wa data kwenye kalenda, rekodi za nambari za simu na zaidi"</string>
+ <string name="auto_permission_manager_summary" msgid="9157438376234301354">"Dhibiti ufikiaji wa data kwenye kalenda, rekodi za namba za simu na zaidi"</string>
<string name="granted_permission_decision" msgid="7824827491551861365">"Umeipa <xliff:g id="APP_NAME">%1$s</xliff:g> ruhusa ya kufikia <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="denied_permission_decision" msgid="5308961501779563781">"Umeinyima <xliff:g id="APP_NAME">%1$s</xliff:g> ruhusa ya kufikia <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Leo}=1{Siku moja iliyopita}other{Siku # zilizopita}}"</string>
@@ -363,7 +365,7 @@
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"kipiga simu"</string>
<string name="role_sms_label" msgid="8456999857547686640">"Programu chaguomsingi ya SMS"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"Programu ya SMS"</string>
- <string name="role_sms_description" msgid="3424020199148153513">"Programu zinazokuruhusu kutumia nambari yako ya simu kutuma na kupokea ujumbe mfupi, picha, video na zaidi"</string>
+ <string name="role_sms_description" msgid="3424020199148153513">"Programu zinazokuruhusu kutumia namba yako ya simu kutuma na kupokea ujumbe mfupi, picha, video na zaidi"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"Ungependa kuweka <xliff:g id="APP_NAME">%1$s</xliff:g> iwe programu yako chaguomsingi ya SMS?"</string>
<string name="role_sms_request_description" msgid="2691004766132144886">"Programu hii itapewa uwezo wa kufikia Kamera, Anwani, Faili na maudhui, Maikrofoni, Simu na SMS kwenye kifaa chako"</string>
<string name="role_sms_search_keywords" msgid="8022048144395047352">"SMS, kutuma SMS, ujumbe, kutuma ujumbe"</string>
@@ -381,12 +383,12 @@
<string name="role_home_search_keywords" msgid="3830755001192666285">"kifungua programu"</string>
<string name="role_call_redirection_label" msgid="5785304207206147590">"Programu chaguomsingi ya kuelekeza simu kwingine"</string>
<string name="role_call_redirection_short_label" msgid="7568143419571217757">"Programu ya kuelekeza simu"</string>
- <string name="role_call_redirection_description" msgid="6091669882014664420">"Programu zinazokuruhusu usambaze simu unazopiga kwenye nambari nyingine ya simu"</string>
+ <string name="role_call_redirection_description" msgid="6091669882014664420">"Programu zinazokuruhusu usambaze simu unazopiga kwenye namba nyingine ya simu"</string>
<string name="role_call_redirection_request_title" msgid="2816244455003562925">"Je, ungependa kuweka <xliff:g id="APP_NAME">%1$s</xliff:g> iwe programu yako chaguomsingi ya kuelekeza simu kwingine?"</string>
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"Hakuna ruhusa zinazohitajika"</string>
<string name="role_call_screening_label" msgid="883935222060878724">"Kutambua taka na anayepiga: programu msingi"</string>
<string name="role_call_screening_short_label" msgid="2048465565063130834">"Kutambua anayepiga na taka"</string>
- <string name="role_call_screening_description" msgid="2349431420497468981">"Programu zinazokuruhusu utambue simu zinazoingia na kuzuia taka, simu zinazopigwa kiotomatiki au nambari zisizohitajika"</string>
+ <string name="role_call_screening_description" msgid="2349431420497468981">"Programu zinazokuruhusu utambue simu zinazoingia na kuzuia taka, simu zinazopigwa kiotomatiki au namba zisizohitajika"</string>
<string name="role_call_screening_request_title" msgid="7358309224566977290">"Je, ungependa kuweka <xliff:g id="APP_NAME">%1$s</xliff:g> iwe programu chaguomsingi ya kukagua kitambulisho cha anayepiga na taka?"</string>
<string name="role_call_screening_request_description" msgid="7338511921032446006">"Hakuna ruhusa zinazohitajika"</string>
<string name="role_automotive_navigation_label" msgid="2701890757955474751">"Programu chaguomsingi ya maelekezo"</string>
@@ -489,7 +491,7 @@
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Huenda programu hii ikataka kupiga picha na kurekodi video kila wakati, hata wakati huitumii."<annotation id="link">"Ruhusu katika mipangilio."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Ungependa kubadilisha ruhusa za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; za kufikia kamera?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Programu hii inataka kupiga picha na kurekodi video kila wakati, hata wakati huitumii."<annotation id="link">"Ruhusu katika mipangilio."</annotation></string>
- <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie rekodi zako za nambari za simu?"</string>
+ <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie rekodi zako za namba za simu?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kupiga na kudhibiti simu?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie data ya vitambuzi kuhusu viashiria muhimu vya mwili wako?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Programu hii inataka kufikia data ya vitambuzi ya viashiria muhimu vya mwili wako kila wakati, hata wakati huitumii. Ili ufanye mabadiliko haya, "<annotation id="link">"nenda kwenye mipangilio."</annotation></string>
@@ -518,9 +520,9 @@
<string name="blocked_microphone_title" msgid="1631517143648232585">"Maikrofoni ya kifaa imezuiwa"</string>
<string name="blocked_location_title" msgid="2005608279812892383">"Utambuzi wa mahali kifaa kilipo umezimwa"</string>
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Kwa ajili ya programu na huduma"</string>
- <string name="blocked_mic_summary" msgid="8960466941528458347">"Huenda bado data ya maikrofoni ikashirikiwa unapopigia nambari ya dharura."</string>
+ <string name="blocked_mic_summary" msgid="8960466941528458347">"Huenda bado data ya maikrofoni ikashirikiwa unapopigia namba ya dharura."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Badilisha"</string>
- <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Usalama na faragha"</string>
+ <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Ulinzi na faragha"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Kagua kifaa"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Ondoa"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"Ungependa kuondoa arifa hii?"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"Hali ya usalama na faragha. <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"Mipangilio ya Usalama"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"Ruhusa"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Usalama na faragha"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Ulinzi na faragha"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Kagua hali"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Vidhibiti vyako vya faragha"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"Mipangilio zaidi"</string>
@@ -580,7 +582,7 @@
<string name="camera_toggle_title" msgid="1251201397431837666">"Ufikiaji wa kamera"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"Ufikiaji wa maikrofoni"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Kwenye programu na huduma"</string>
- <string name="mic_toggle_description" msgid="9163104307990677157">"Kwenye programu na huduma. Mipangilio hii ikizimwa, huenda data ya maikrofoni ikaendelea kushirikiwa unapopiga nambari ya dharura."</string>
+ <string name="mic_toggle_description" msgid="9163104307990677157">"Kwenye programu na huduma. Mipangilio hii ikizimwa, huenda data ya maikrofoni ikaendelea kushirikiwa unapopiga namba ya dharura."</string>
<string name="location_settings_subtitle" msgid="2328360561197430695">"Angalia huduma na programu zenye uwezo wa kufikia mipangilio ya mahali"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Onyesha ufikiaji wa ubao wa kunakili"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Onyesha ujumbe programu zinapofikia maandishi, picha au maudhui mengine uliyonakili"</string>
diff --git a/PermissionController/res/values-ta/strings.xml b/PermissionController/res/values-ta/strings.xml
index ad2bea8a3..fcaab1a44 100644
--- a/PermissionController/res/values-ta/strings.xml
+++ b/PermissionController/res/values-ta/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"மேலும் தகவல்"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"அனைத்தையும் அனுமதி"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"எப்போதும் அனைத்தையும் அனுமதி"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"வரம்பிற்குட்பட்ட அணுகலை அனுமதி"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"படங்களையும் வீடியோக்களையும் தேர்ந்தெடுங்கள்"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"இன்னும் தேர்ந்தெடுங்கள்"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"கூடுதலாகத் தேர்ந்தெடுக்க வேண்டாம்"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ஆப்ஸ்"</string>
<string name="app_permissions" msgid="3369917736607944781">"ஆப்ஸ் அனுமதிகள்"</string>
<string name="unused_apps" msgid="2058057455175955094">"பயன்படுத்தாத ஆப்ஸ்"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"இந்த ஆப்ஸுக்கெனத் தேர்ந்தெடுத்த படங்களை மாற்றுங்கள்"</string>
<string name="no_unused_apps" msgid="12809387670415295">"பயன்படுத்தாத ஆப்ஸ் எதுவுமில்லை"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 பயன்படுத்தாத ஆப்ஸ்"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"அனுமதி தொடர்பான முடிவுகள்"</string>
diff --git a/PermissionController/res/values-te/strings.xml b/PermissionController/res/values-te/strings.xml
index dd105ae85..908129ca3 100644
--- a/PermissionController/res/values-te/strings.xml
+++ b/PermissionController/res/values-te/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"మరింత సమాచారం"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"అన్నీ అనుమతించండి"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"ఎల్లవేళలా అన్నీ అనుమతించండి"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"పరిమిత యాక్సెస్‌ను అనుమతించండి"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"ఫోటోలు, వీడియోలను ఎంచుకోండి"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"మరిన్ని ఫోటోలను ఎంచుకోండి"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"మరిన్ని ఫోటోలను ఎంచుకోవద్దు"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"యాప్‌లు"</string>
<string name="app_permissions" msgid="3369917736607944781">"యాప్ అనుమతులు"</string>
<string name="unused_apps" msgid="2058057455175955094">"ఉపయోగించని యాప్‌లు"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"ఈ యాప్ కోసం ఎంచుకున్న ఫోటోలను ఎడిట్ చేయండి"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ఉపయోగించని యాప్‌లు లేవు"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ఉపయోగించని యాప్‌లు"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"ఇటీవలి అనుమతి నిర్ణయాలు"</string>
@@ -69,7 +71,7 @@
<string name="granted_permission_decision" msgid="7824827491551861365">"మీరు <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>కు <xliff:g id="APP_NAME">%1$s</xliff:g> యాక్సెస్‌ను ఇచ్చారు"</string>
<string name="denied_permission_decision" msgid="5308961501779563781">"మీరు <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>‌కు <xliff:g id="APP_NAME">%1$s</xliff:g> యాక్సెస్‌ను తిరస్కరించారు"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{ఈరోజు}=1{1 రోజు క్రితం}other{# రోజుల క్రితం}}"</string>
- <string name="app_disable_dlg_positive" msgid="7418444149981904940">"యాప్‌ను డిజేబుల్‌ చేయి"</string>
+ <string name="app_disable_dlg_positive" msgid="7418444149981904940">"యాప్‌ను డిజేబుల్‌ చేయండి"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"మీరు ఈ యాప్‌ను డిజేబుల్‌ చేస్తే, Android మరియు ఇతర యాప్‌లు ఇకపై ఉద్దేశించిన రీతిలో పని చేయకపోవచ్చు. ఈ యాప్ మీ పరికరంలో ముందుగానే ఇన్‌స్టాల్ చేసి, అందించబడింది కాబట్టి మీరు దీనిని తొలగించలేరని గుర్తుంచుకోండి. డిజేబుల్‌ చేయడం ద్వారా, మీరు ఈ యాప్‌ను ఆఫ్ చేసి, మీ పరికరంలో దానిని దాస్తున్నారు."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"అనుమతి మేనేజర్"</string>
<string name="never_ask_again" msgid="4728762438198560329">"మళ్లీ అడగవద్దు"</string>
@@ -266,7 +268,7 @@
<string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# ఉపయోగించని యాప్}other{# ఉపయోగించని యాప్‌లు}}"</string>
<string name="unused_apps_notification_content" msgid="9195026773244581246">"అనుమతులు, తాత్కాలిక ఫైళ్లు తీసివేయబడ్డాయి అలాగే నోటిఫికేషన్‌లు ఆపివేయబడ్డాయి. రివ్యూ చేయడానికి ట్యాప్ చేయండి."</string>
<string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"అనుమతులు తీసివేయబడిన యాప్‌లను రివ్యూ చేయండి"</string>
- <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"కొంతకాలంగా మీరు ఉపయోగించని యాప్‌లకు సంబంధించిన అనుమతులు, తాత్కాలిక ఫైల్‌లు తొలగించబడ్డాయి, అలాగే నోటిఫికేషన్‌లు నిలిపివేయబడ్డాయి."</string>
+ <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"కొంతకాలంగా మీరు ఉపయోగించని యాప్‌లకు సంబంధించిన అనుమతులు, తాత్కాలిక ఫైళ్లు తొలగించబడ్డాయి, అలాగే నోటిఫికేషన్‌లు నిలిపివేయబడ్డాయి."</string>
<string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"యాప్‌లను రివ్యూ చేయండి"</string>
<string name="post_drive_permission_decision_reminder_title" msgid="1290697371418139976">"ఇటీవలి అనుమతులను చెక్ చేయండి"</string>
<string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"డ్రైవింగ్‌లో ఉన్నప్పుడు, మీరు <xliff:g id="APP">%1$s</xliff:g> యాప్‌నకు <xliff:g id="PERMISSION">%2$s</xliff:g>‌కు యాక్సెస్‌ను ఇచ్చారు"</string>
@@ -352,7 +354,7 @@
<string name="role_assistant_description" msgid="6622458130459922952">"సహాయక యాప్‌లు మీరు వీక్షిస్తున్న స్క్రీన్‌పై ఉన్న సమాచారం ఆధారంగా మీకు సహాయపడగలవు. కొన్ని యాప్‌లు మీకు సమగ్రమైన సహాయాన్ని అందించడానికి లాంచర్‌కు, వాయిస్ ఇన్‌పుట్ సర్వీసులకు రెండింటికీ సపోర్ట్‌ చేస్తాయి."</string>
<string name="role_browser_label" msgid="2877796144554070207">"ఆటోమేటిక్ బ్రౌజర్ యాప్"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"బ్రౌజర్ యాప్"</string>
- <string name="role_browser_description" msgid="3465253637499842671">"మీకు ఇంటర్నెట్ యాక్సెస్‌ను అందించే, అలాగే మీరు నొక్కే లింక్‌లను చూపించే యాప్‌లు"</string>
+ <string name="role_browser_description" msgid="3465253637499842671">"మీకు ఇంటర్నెట్‌కు యాక్సెస్‌ ఇచ్చి, ట్యాప్ చేయడానికి లింక్‌ల‌ను డిస్‌ప్లే చేసే యాప్‌లు"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"<xliff:g id="APP_NAME">%1$s</xliff:g>ను మీ ఆటోమేటిక్ బ్రౌజర్ యాప్‌గా సెట్ చేయాలా?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"అనుమతులు ఇవ్వనవసరం లేదు"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"ఆటోమేటిక్ ఫోన్ యాప్"</string>
@@ -462,13 +464,13 @@
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"ఈ యాప్‌నకు మీ లొకేషన్ యాక్సెస్ అన్ని సమయాలలో, అంటే యాప్‌ను ఉపయోగించనప్పుడు కూడా, అవసరం ఉండవచ్చు. "<annotation id="link">"సెట్టింగ్‌లలో అనుమతించండి."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; కోసం లొకేష‌న్‌ యాక్సెస్‌ను మార్చాలా?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"మీరు యాప్ ఉపయోగించనప్పుడు కూడా ఈ యాప్ మీ లొకేష‌న్‌ను ఎప్పటికప్పుడు యాక్సెస్ చేయాల‌ని అనుకుంటోంది."<annotation id="link">"సెట్టింగ్‌లలో అనుమతించండి."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి \"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;\"ను అనుమతించాలా?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా? "<annotation id="link">"సెట్టింగ్‌లలో అనుమతించండి."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>కు సంబంధించిన లొకేషన్ యాక్సెస్‌ను సుమారు నుండి ఖచ్చితమైనదిగా మార్చాలా?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"ఈ పరికరానికి సంబంధించి సుమారుగా ఉన్న లొకేషన్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"ఖచ్చితమైన"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"సుమారుగా"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"మీ క్యాలెండర్‌ని యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"మీ క్యాలెండర్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"SMS మెసేజ్‌లు పంపడం, చూడటం చేయగలిగేలా &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"మీ పరికరంలోని ఫోటోలు, మీడియా, ఫైళ్లను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"ఈ పరికరంలో &lt;b&gt;ఫోటోలు, వీడియోలు, మ్యూజిక్, ఆడియో&lt;/b&gt;ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
@@ -581,9 +583,9 @@
<string name="mic_toggle_title" msgid="2649991093496110162">"మైక్రోఫోన్ యాక్సెస్"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"యాప్‌లు, సర్వీస్‌ల కోసం"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"యాప్‌లు, సర్వీస్‌ల కోసం. ఈ సెట్టింగ్ ఆఫ్ చేయబడి ఉన్నట్లయితే, మీరు ఎమర్జెన్సీ నంబర్‌కు కాల్ చేసినప్పుడు మైక్రోఫోన్ డేటా ఇప్పటికీ షేర్ చేయబడవచ్చు."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"లొకేషన్‌కు యాక్సెస్ ఉన్న యాప్‌లు, సర్వీస్‌లను చూడండి"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"లొకేషన్ యాక్సెస్ ఉన్న యాప్‌లను, సర్వీస్‌లను చూడండి"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"క్లిప్‌బోర్డ్ యాక్సెస్‌ను చూపించండి"</string>
- <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"మీరు కాపీ చేసిన టెక్స్ట్, ఇమేజ్‌లను లేదా ఇతర కంటెంట్‌ను యాప్‌లు యాక్సెస్ చేసినప్పుడు మెసేజ్‌ను చూపుతుంది"</string>
+ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"మీరు కాపీ చేసిన టెక్స్ట్‌ను, ఇమేజ్‌లను లేదా ఇతర కంటెంట్‌ను యాప్‌లు యాక్సెస్ చేసినప్పుడు ఒక మెసేజ్‌ను చూపుతుంది"</string>
<string name="show_password_title" msgid="2877269286984684659">"పాస్‌వర్డ్‌లను చూపిస్తుంది"</string>
<string name="show_password_summary" msgid="1110166488865981610">"మీరు టైప్ చేస్తున్నప్పుడు అక్షరాలను క్లుప్తంగా చూపిస్తుంది"</string>
<string name="permission_rationale_message_location" msgid="2153841534298068414">"ఈ యాప్, అది లొకేషన్ డేటాను థర్డ్-పార్టీలతో షేర్ చేయవచ్చని పేర్కొంది"</string>
diff --git a/PermissionController/res/values-th-v34/strings.xml b/PermissionController/res/values-th-v34/strings.xml
index 8e6bae837..5b2e81b87 100644
--- a/PermissionController/res/values-th-v34/strings.xml
+++ b/PermissionController/res/values-th-v34/strings.xml
@@ -17,10 +17,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"ความปลอดภัยและความเป็นส่วนตัว"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"การรักษาความปลอดภัยและความเป็นส่วนตัว"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"การควบคุม"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
- <string name="health_connect_summary" msgid="815473513776882296">"จัดการสิทธิ์เข้าถึงข้อมูลสุขภาพของแอป"</string>
+ <string name="health_connect_summary" msgid="815473513776882296">"จัดการสิทธิ์ของแอปในการเข้าถึงข้อมูลสุขภาพ"</string>
<string name="location_settings" msgid="8863940440881290182">"สิทธิ์เข้าถึงตำแหน่ง"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"สำหรับแอปและบริการ หากปิดการตั้งค่านี้ ระบบอาจยังคงแชร์ข้อมูลไมโครโฟนเมื่อคุณโทรหาหมายเลขฉุกเฉิน"</string>
<string name="location_settings_subtitle" msgid="6846532794702613851">"สำหรับแอปและบริการ"</string>
diff --git a/PermissionController/res/values-th/strings.xml b/PermissionController/res/values-th/strings.xml
index c6b80ea1f..f05e9efe4 100644
--- a/PermissionController/res/values-th/strings.xml
+++ b/PermissionController/res/values-th/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"ข้อมูลเพิ่มเติม"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"อนุญาตทั้งหมด"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"อนุญาตทั้งหมดตลอดเวลา"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"อนุญาตสิทธิ์เข้าถึงแบบจำกัด"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"เลือกรูปภาพและวิดีโอ"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"เลือกเพิ่มเติม"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"ไม่เลือกเพิ่มแล้ว"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"แอป"</string>
<string name="app_permissions" msgid="3369917736607944781">"สิทธิ์ของแอป"</string>
<string name="unused_apps" msgid="2058057455175955094">"แอปที่ไม่ได้ใช้"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"แก้ไขรูปภาพที่เลือกสำหรับแอปนี้"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ไม่มีแอปที่ไม่ได้ใช้งาน"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"แอปที่ไม่ได้ใช้ 0 รายการ"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"สิทธิ์ที่กำหนดไว้ล่าสุด"</string>
@@ -195,8 +197,8 @@
<string name="approximate_image_description" msgid="938803699637069884">"ตำแหน่งโดยประมาณ"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"ใช้ตำแหน่งที่แน่นอน"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"แอปจะเข้าถึงตำแหน่งโดยประมาณเมื่อปิดใช้ตำแหน่งที่แน่นอน"</string>
- <string name="app_permission_title" msgid="2090897901051370711">"สิทธิ์เข้าถึง <xliff:g id="PERM">%1$s</xliff:g>"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"สิทธิ์การเข้าถึง <xliff:g id="PERM">%1$s</xliff:g> สำหรับแอปนี้"</string>
+ <string name="app_permission_title" msgid="2090897901051370711">"สิทธิ์เข้าถึง \"<xliff:g id="PERM">%1$s</xliff:g>\""</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"สิทธิ์เข้าถึง \"<xliff:g id="PERM">%1$s</xliff:g>\" สำหรับแอปนี้"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"ดูสิทธิ์ทั้งหมดของ \"<xliff:g id="APP">%1$s</xliff:g>\""</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"ดูแอปทั้งหมดที่มีสิทธิ์นี้"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"แสดงการใช้ไมโครโฟนของ Assistant"</string>
@@ -520,7 +522,7 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"สำหรับแอปและบริการ"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"ระบบอาจยังคงแชร์ข้อมูลไมโครโฟนเมื่อคุณโทรหาหมายเลขฉุกเฉิน"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"เปลี่ยน"</string>
- <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"ความปลอดภัยและความเป็นส่วนตัว"</string>
+ <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"การรักษาความปลอดภัยและความเป็นส่วนตัว"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"สแกนอุปกรณ์"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"ปิด"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"ปิดการแจ้งเตือนนี้ไหม"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"สถานะความปลอดภัยและความเป็นส่วนตัว <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g> <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"การตั้งค่าการรักษาความปลอดภัย"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"สิทธิ์"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"ความปลอดภัยและความเป็นส่วนตัว"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"การรักษาความปลอดภัยและความเป็นส่วนตัว"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"ตรวจสอบสถานะ"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"การควบคุมความเป็นส่วนตัวของคุณ"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"การตั้งค่าเพิ่มเติม"</string>
@@ -581,7 +583,7 @@
<string name="mic_toggle_title" msgid="2649991093496110162">"สิทธิ์เข้าถึงไมโครโฟน"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"สำหรับแอปและบริการ"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"สำหรับแอปและบริการ หากปิดการตั้งค่านี้ ระบบอาจยังคงแชร์ข้อมูลไมโครโฟนเมื่อคุณโทรหาหมายเลขฉุกเฉิน"</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"ดูแอปและบริการที่มีสิทธิ์เข้าถึงสถานที่ตั้ง"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"ดูแอปและบริการที่มีสิทธิ์เข้าถึงตำแหน่ง"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"แสดงการเข้าถึงคลิปบอร์ด"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"แสดงข้อความเมื่อแอปเข้าถึงข้อความ รูปภาพ หรือเนื้อหาอื่นๆ ที่คุณคัดลอก"</string>
<string name="show_password_title" msgid="2877269286984684659">"แสดงรหัสผ่าน"</string>
diff --git a/PermissionController/res/values-tl/strings.xml b/PermissionController/res/values-tl/strings.xml
index d4bdb66e0..afd9c08a5 100644
--- a/PermissionController/res/values-tl/strings.xml
+++ b/PermissionController/res/values-tl/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Higit pang info"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Pahintulutan lahat"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Palaging pahintulutan lahat"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Payagan ang limitadong access"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Pumili ng mga larawan at video"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Pumili pa"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Huwag nang pumili pa"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Mga App"</string>
<string name="app_permissions" msgid="3369917736607944781">"Mga pahintulot sa app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Mga hindi ginagamit na app"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"I-edit ang mga piniling larawan para sa app na ito"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Walang hindi ginagamit na app"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 hindi ginagamit na app"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Mga recent permission decision"</string>
diff --git a/PermissionController/res/values-tr/strings.xml b/PermissionController/res/values-tr/strings.xml
index 9bda44775..e5da72bcb 100644
--- a/PermissionController/res/values-tr/strings.xml
+++ b/PermissionController/res/values-tr/strings.xml
@@ -29,11 +29,12 @@
<string name="app_not_found_dlg_title" msgid="6029482906093859756">"Uygulama bulunamadı"</string>
<string name="grant_dialog_button_deny" msgid="88262611492697192">"İzin verme"</string>
<string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"İzin verme ve bir daha sorma"</string>
- <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“Uygulama kullanılırken” tut"</string>
+ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“Uygulama kullanılırken” olarak kalsın"</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Yalnızca bu defa\" sakla"</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Daha fazla bilgi"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Tümüne izin ver"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Tümüne her zaman izin ver"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Sınırlı erişime izin ver"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Fotoğraf ve video seçin"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Daha fazla seçin"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Başka seçilmesin"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Uygulamalar"</string>
<string name="app_permissions" msgid="3369917736607944781">"Uygulama izinleri"</string>
<string name="unused_apps" msgid="2058057455175955094">"Kullanılmayan uygulamalar"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Bu uygulamanın hangi fotoğraflara erişebileceğini düzenler"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Kullanılmayan uygulama yok"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Kullanılmayan 0 uygulama"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Son izin kararları"</string>
@@ -495,7 +497,7 @@
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Bu uygulama, kullanmadığınız sırada bile hayati belirtilerinizle ilgili sensör verilerine sürekli olarak erişmek istiyor. Bu değişikliği yapmak için "<annotation id="link">"ayarlara gidin."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının hayati belirtilerinizle ilgili sensör verilerine erişmesine izin verilsin mi?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Bu uygulamanın, kullanmadığınız sırada bile vücut sensörü verilerine sürekli olarak erişmesine izin vermek için "<annotation id="link">"ayarlara gidin."</annotation></string>
- <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının kullanımdayken vücut sensörü verilerine erişme izni sürdürülsün mü?"</string>
+ <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;, uygulama kullanılırken vücut sensörü verilerine erişmeye devam etsin mi?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının size bildirim göndermesine izin verilsin mi?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Kontrol edilen izinler"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> konuma erişebiliyor"</string>
diff --git a/PermissionController/res/values-uk-v34/strings.xml b/PermissionController/res/values-uk-v34/strings.xml
index 5d14c8ebe..6b74a3508 100644
--- a/PermissionController/res/values-uk-v34/strings.xml
+++ b/PermissionController/res/values-uk-v34/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"Безпека й конфіденційність"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"Захист і конфіденційність"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Параметри"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"Керуйте доступом додатків до даних про здоров’я"</string>
diff --git a/PermissionController/res/values-uk/strings.xml b/PermissionController/res/values-uk/strings.xml
index bf4b99d43..509f05904 100644
--- a/PermissionController/res/values-uk/strings.xml
+++ b/PermissionController/res/values-uk/strings.xml
@@ -32,8 +32,9 @@
<string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Дозволяти, лише коли додаток використовується"</string>
<string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Залишити дозвіл \"Лише цього разу\""</string>
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Докладніше"</string>
- <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Дозволити всі"</string>
+ <string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Дозволити все"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Завжди дозволяти всі"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Дозволити обмежений доступ"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Вибрати фото й відео"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Вибрати ще"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Не вибирати більше"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Додатки"</string>
<string name="app_permissions" msgid="3369917736607944781">"Дозволи додатка"</string>
<string name="unused_apps" msgid="2058057455175955094">"Непотрібні додатки"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Змінити вибрані фотографії для цього додатка"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Усі додатки використовуються"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 додатків не використовуються"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Останні рішення щодо дозволів"</string>
@@ -70,7 +72,7 @@
<string name="denied_permission_decision" msgid="5308961501779563781">"Ви заборонили додатку <xliff:g id="APP_NAME">%1$s</xliff:g> такий доступ: <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Сьогодні}=1{1 день тому}one{# день тому}few{# дні тому}many{# днів тому}other{# дня тому}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Вимкнути додаток"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"Якщо вимкнути цей додаток, система Android та інші додатки можуть працювати неналежним чином. Важливо: ви не можете видалити цей додаток, оскільки він був попередньо встановлений на пристрої. Вимкнений додаток буде приховано."</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"Якщо вимкнути цей додаток, система Android і інші додатки можуть працювати неналежним чином. Важливо: ви не можете видалити цей додаток, оскільки він був попередньо встановлений на пристрої. Вимкнений додаток буде приховано."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Менеджер дозволів"</string>
<string name="never_ask_again" msgid="4728762438198560329">"Більше не запитувати"</string>
<string name="no_permissions" msgid="3881676756371148563">"Немає дозволів"</string>
@@ -124,7 +126,7 @@
<string name="current_permissions_category" msgid="4292990083585728880">"Поточні дозволи"</string>
<string name="message_staging" msgid="9110563899955511866">"Підготовка додатка…"</string>
<string name="app_name_unknown" msgid="1319665005754048952">"Невідомо"</string>
- <string name="permission_usage_title" msgid="1568233336351734538">"Панель керування доступом"</string>
+ <string name="permission_usage_title" msgid="1568233336351734538">"Панель керування дозволами"</string>
<string name="auto_permission_usage_summary" msgid="7335667266743337075">"Показувати додатки, які нещодавно використовували дозволи"</string>
<string name="permission_group_usage_title" msgid="2595013198075285173">"<xliff:g id="PERMGROUP">%1$s</xliff:g>: використання"</string>
<string name="perm_usage_adv_info_title" msgid="3357831829538873708">"Переглянути інші дозволи"</string>
@@ -204,7 +206,7 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Відкликати дозволи, якщо додаток не використовується"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Вилучати дозволи й звільняти місце"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Призупиняти роботу в неактивний період"</string>
- <string name="unused_apps_summary" msgid="8839466950318403115">"Вилучити дозволи, видалити тимчасові файли та зупинити сповіщення"</string>
+ <string name="unused_apps_summary" msgid="8839466950318403115">"Вилучити дозволи, видалити тимчасові файли й зупинити сповіщення"</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_revoked_apps_page_summary" msgid="6594753657893756536">"Щоб захистити ваші дані, було скасовано дозволи додатків, які не використовувалися впродовж кількох місяців."</string>
@@ -230,7 +232,7 @@
<string name="permission_description_summary_activity_recognition" msgid="2652850576497070146">"Додатки з цим дозволом мають доступ до даних про вашу фізичну активність (ходьбу, поїздки на велосипеді й автомобілі, кількість кроків тощо)"</string>
<string name="permission_description_summary_calendar" msgid="103329982944411010">"Додатки з цим дозволом мають доступ до вашого календаря"</string>
<string name="permission_description_summary_call_log" msgid="7321437186317577624">"Додатки з цим дозволом можуть переглядати й редагувати ваш журнал викликів"</string>
- <string name="permission_description_summary_camera" msgid="108004375101882069">"Додатки з цим дозволом можуть робити знімки та записувати відео"</string>
+ <string name="permission_description_summary_camera" msgid="108004375101882069">"Додатки з цим дозволом можуть робити знімки й записувати відео"</string>
<string name="permission_description_summary_contacts" msgid="2337798886460408996">"Додатки з цим дозволом мають доступ до ваших контактів"</string>
<string name="permission_description_summary_location" msgid="2817531799933480694">"Додатки з цим дозволом мають доступ до геоданих цього пристрою"</string>
<string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Додатки з цим дозволом можуть знаходити пристрої поблизу, підключатися до них і визначати їх відносне розташування."</string>
@@ -350,14 +352,14 @@
<string name="role_assistant_label" msgid="4727586018198208128">"Цифровий помічник за умовчанням"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"Цифровий помічник"</string>
<string name="role_assistant_description" msgid="6622458130459922952">"Помічники надають допомогу на основі вмісту, який ви переглядаєте на екрані. Задля ефективності деякі додатки підтримують панель запуску й голосовий ввід."</string>
- <string name="role_browser_label" msgid="2877796144554070207">"Веб-переглядач за умовчанням"</string>
- <string name="role_browser_short_label" msgid="6745009127123292296">"Веб-переглядач"</string>
+ <string name="role_browser_label" msgid="2877796144554070207">"Вебпереглядач за умовчанням"</string>
+ <string name="role_browser_short_label" msgid="6745009127123292296">"Вебпереглядач"</string>
<string name="role_browser_description" msgid="3465253637499842671">"Додатки, за допомогою яких можна переглядати сайти й переходити за посиланнями"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"Зробити <xliff:g id="APP_NAME">%1$s</xliff:g> веб-переглядачем за умовчанням?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"Дозволи не потрібні"</string>
<string name="role_dialer_label" msgid="1100224146343237968">"Дзвінки за умовчанням"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"Додаток для викликів"</string>
- <string name="role_dialer_description" msgid="8768708633696539612">"Додатки, у яких можна здійснювати та приймати виклики на пристрої"</string>
+ <string name="role_dialer_description" msgid="8768708633696539612">"Додатки, у яких можна здійснювати й приймати виклики на пристрої"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"Чи має <xliff:g id="APP_NAME">%1$s</xliff:g> використовуватись як додаток для викликів за умовчанням?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"Цьому додатку буде надано дозволи \"Камера\", \"Контакти\", \"Мікрофон\", \"Телефон\" і \"SMS\""</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"телефон"</string>
@@ -386,7 +388,7 @@
<string name="role_call_redirection_request_description" msgid="3118895714178527164">"Дозволи не потрібні"</string>
<string name="role_call_screening_label" msgid="883935222060878724">"АВН і захист від спаму за умовчанням"</string>
<string name="role_call_screening_short_label" msgid="2048465565063130834">"АВН і захист від спаму"</string>
- <string name="role_call_screening_description" msgid="2349431420497468981">"Додатки для керування вхідними викликами, за допомогою яких можна визначати, хто та навіщо вам телефонує, а також блокувати спам, автоматичні виклики або дзвінки з небажаних номерів"</string>
+ <string name="role_call_screening_description" msgid="2349431420497468981">"Додатки для керування вхідними викликами, за допомогою яких можна визначати, хто й навіщо вам телефонує, а також блокувати спам, автоматичні виклики або дзвінки з небажаних номерів"</string>
<string name="role_call_screening_request_title" msgid="7358309224566977290">"Зробити <xliff:g id="APP_NAME">%1$s</xliff:g> додатком для автоматичного визначення номерів і захисту від спаму за умовчанням?"</string>
<string name="role_call_screening_request_description" msgid="7338511921032446006">"Дозволи не потрібні"</string>
<string name="role_automotive_navigation_label" msgid="2701890757955474751">"Навігаційний додаток за умовчанням"</string>
@@ -469,7 +471,7 @@
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Точне"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Приблизне"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до календаря?"</string>
- <string name="permgrouprequest_sms" msgid="5672063688745420991">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; надсилати та переглядати SMS?"</string>
+ <string name="permgrouprequest_sms" msgid="5672063688745420991">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; надсилати й переглядати SMS?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до фото, медіа та файлів на пристрої?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до &lt;b&gt;фото, відео, музики й аудіо&lt;/b&gt; на цьому пристрої?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до &lt;b&gt;фото, відео, музики, аудіо й інших файлів&lt;/b&gt; на пристрої?"</string>
@@ -483,14 +485,14 @@
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Змінити налаштування доступу до мікрофона для додатка &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Цей додаток може записувати звук, навіть коли ви не використовуєте його. Дозвіл можна надати в "<annotation id="link">"налаштуваннях"</annotation>"."</string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до даних про фізичну активність?"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; робити знімки та записувати відео?"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; робити знімки й записувати відео?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Додаток зможе робити фотографії та записувати відео, лише коли ви використовуєте його"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; робити знімки й записувати відео?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Цей додаток може робити фотографії та записувати відео, навіть коли ви не використовуєте його. Дозвіл можна надати в "<annotation id="link">"налаштуваннях"</annotation>"."</string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Змінити налаштування доступу до камери для додатка &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Цей додаток може робити фотографії та записувати відео, навіть коли ви не використовуєте його. Дозвіл можна надати в "<annotation id="link">"налаштуваннях"</annotation>"."</string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до журналів викликів телефона?"</string>
- <string name="permgrouprequest_phone" msgid="1829234136997316752">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; здійснювати телефонні дзвінки та керувати ними?"</string>
+ <string name="permgrouprequest_phone" msgid="1829234136997316752">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; здійснювати телефонні дзвінки й керувати ними?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до життєвих показників із датчиків?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Цей додаток хоче постійно отримувати дані життєвих показників від датчиків, навіть коли ви не використовуєте його. Щоб внести зміни, "<annotation id="link">"перейдіть у налаштування"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до життєвих показників із датчиків?"</string>
@@ -581,7 +583,7 @@
<string name="mic_toggle_title" msgid="2649991093496110162">"Доступ до мікрофона"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Для додатків і сервісів"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Для додатків і сервісів. Якщо це налаштування вимкнено, дані мікрофона можуть усе одно передаватися під час виклику екстреного номера."</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"Перегляньте додатки та сервіси, які мають доступ до геоданих"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"Перегляньте додатки й сервіси, які мають доступ до геоданих"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"Показувати сповіщення про доступ до буфера обміну"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"З’являтиметься сповіщення, коли будь-який додаток отримуватиме доступ до скопійованого вами тексту, зображень чи іншого контенту"</string>
<string name="show_password_title" msgid="2877269286984684659">"Показувати паролі"</string>
diff --git a/PermissionController/res/values-ur/strings.xml b/PermissionController/res/values-ur/strings.xml
index 4cb926518..ea3867f62 100644
--- a/PermissionController/res/values-ur/strings.xml
+++ b/PermissionController/res/values-ur/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"مزید معلومات"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"سبھی کو اجازت دیں"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"ہمیشہ سبھی کو اجازت دیں"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"محدود رسائی کی اجازت دیں"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"تصاویر اور ویڈیوز منتخب کریں"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"مزید منتخب کریں"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"مزید منتخب نہ کریں"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ایپس"</string>
<string name="app_permissions" msgid="3369917736607944781">"ایپ کی اجازتیں"</string>
<string name="unused_apps" msgid="2058057455175955094">"غیر مستعمل ایپس"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"منتخب کریں کہ کن تصاویر تک اس ایپ کو رسائی ہوگی"</string>
<string name="no_unused_apps" msgid="12809387670415295">"کوئی غیر مستعمل ایپ نہیں ہے"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 غیر مستعمل ایپس"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"اجازت کے حالیہ فیصلے"</string>
@@ -476,9 +478,9 @@
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‏اس آلے پر &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو موسیقی اور آڈیو تک رسائی کی اجازت دیں؟"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‏اس آلے پر &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"‏‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎ کو اس آلے پر مزید تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string>
- <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
+ <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏آڈیو ریکارڈ کرنے کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت دیں؟"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"جب آپ ایپ استعمال کر رہے ہوں تب ایپ صرف آڈیو ریکارڈ کر پائے گی"</string>
- <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
+ <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏آڈیو ریکارڈ کرنے کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت دیں؟"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"ممکن ہے یہ ایپ ہر وقت آڈیو ریکارڈ کرنا چاہے، اگرچہ آپ ایپ استعمال نہ کر رہے ہوں۔ "<annotation id="link">"ترتیبات میں اجازت دیں۔"</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;? کے ليے مائیکروفون تک رسائی تبدیل کریں؟"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"یہ ایپ ہر وقت آڈیو ریکارڈ کرنا چاہتی ہے، اگرچہ آپ ایپ استعمال نہ کر رہے ہوں۔ "<annotation id="link">"ترتیبات میں اجازت دیں۔"</annotation></string>
@@ -492,7 +494,7 @@
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"‏‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎ کو آپ کے فون کال لاگز تک رسائی کی اجازت دیں؟"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"‏&lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو فون کالز کرنے اور ان کا نظم کرنے کی اجازت دیں؟"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے اہم اشاروں کے متعلق سینسر ڈیٹا تک رسائی کی اجازت دیں؟"</string>
- <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"آپ کے ایپ کا استعمال نہ کرنے کے باوجود یہ ایپ ہر وقت آپ کے اہم علامتوں کے متعلق سینسر ڈیٹا تک رسائی حاصل کرنا چاہتی ہے۔ یہ تبدیلی کرنے کے لیے "<annotation id="link">"ترتیبات پر جائیں۔"</annotation></string>
+ <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"جب آپ ایپ استعمال نہ کر رہے ہوں اس صورت میں بھی یہ ایپ ہر وقت آپ کی صحت کی اہم علامتوں کے متعلق سینسر ڈیٹا تک رسائی حاصل کرنا چاہتی ہے۔ یہ تبدیلی کرنے کے لیے "<annotation id="link">"ترتیبات پر جائیں۔"</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے اہم اشاروں کے متعلق سینسر ڈیٹا تک رسائی کی اجازت دیں؟"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"آپ کے ایپ کا استعمال نہ کرنے کے باوجود اس ایپ کو ہر وقت باڈی سینسر ڈیٹا تک رسائی کی اجازت دینے کے لیے "<annotation id="link">"ترتیبات پر جائیں۔"</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"‏ایپ کے استعمال میں ہونے کے دوران ‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎ کو باڈی سینسر ڈیٹا تک رسائی کی اجازت دینا جاری رکھیں؟"</string>
diff --git a/PermissionController/res/values-uz/strings.xml b/PermissionController/res/values-uz/strings.xml
index 356df948e..60a41455b 100644
--- a/PermissionController/res/values-uz/strings.xml
+++ b/PermissionController/res/values-uz/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Batafsil"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Hammasiga ruxsat"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Doim hammasiga ruxsat berish"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Cheklangan ruxsat berish"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Surat va videolarni tanlash"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Yana"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Boshqa tanlanmasin"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Ilovalar"</string>
<string name="app_permissions" msgid="3369917736607944781">"Ilovalar uchun ruxsatlar"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ishlatilmagan ilovalar"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Bu ilova uchun tanlangan suratlarni tahrirlash"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ishlatilmagan ilovalar yoʻq"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ta ishlatilmagan ilova"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Yaqinda tekshirilgan ruxsatlar"</string>
@@ -462,13 +464,13 @@
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Bu ilova ochiq emasligida ham joylashuvingiz haqidagi axborotdan foydalanmoqchi. Bunga "<annotation id="link">" sozlamalar"</annotation>" orqali ruxsat bering."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun joylashuv axborotiga ruxsat oʻzgartirilsinmi?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Bu ilova ochiq emasligida ham joylashuvingiz haqidagi axborotdan foydalanmoqchi. Bunga "<annotation id="link">"sozlamalar"</annotation>" orqali ruxsat bering."</string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; yaqin-atrofdagi qurilmalar joylashuvini aniqlashi va ularga ulanishiga ruxsat berilsinmi?"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ilovasiga yaqin-atrofdagi qurilmalarni topish, ularga ulanish va ularning nisbiy joylashuvini aniqlashi uchun ruxsat berilsinmi?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; yaqin-atrofdagi qurilmalar joylashuvini aniqlashi va ularga ulanishiga ruxsat berilsinmi? "<annotation id="link">"Sozlamalar orqali ruxsat berish mumkin."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> uchun joylashuv ruxsati taxminiydan aniqqa oʻzgartirilsinmi?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun bu qurilmaning taxminiy joylashuvi haqidagi axborotdan foydalanish ruxsati berilsinmi?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Aniq"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Taxminiy"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun taqvimingizga ruxsat berilsinmi?"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun taqvimingizga kirish ruxsati berilsinmi?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun SMS xabarlarni yuborish va ko‘rishga ruxsat berilsinmi?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun qurilmangizdagi suratlar, multimedia va fayllarga kirish ruxsati berilsinmi?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun ushbu qurilmadagi surat, video, musiqa va audiolarga kirish ruxsati berilsinmi?"</string>
@@ -496,7 +498,7 @@
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun organizm holati haqidagi sensor axborotlariga ruxsat berilsinmi?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Bu ilovaga tanadagi sensor maʼlumotlaridan foydalanishga (hatto ilova yopiqligida ham) doimiy ruxsat berish uchun "<annotation id="link">"sozlamalarni oching."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ilovasining tanadagi sensor maʼlumotlaridan foydalanishiga ruxsat berilsinmi?"</string>
- <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ilovasiga sizga bildirishnomalar yuborishi uchun ruxsat berilsinmi?"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sizga bildirishnomalar yuborishi uchun ruxsat berilsinmi?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Boshqariluvchi ruxsatlar"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> joylashuvga kira oladi"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Tashkilotingiz <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga joylashuvingizga kirishga ruxsat bergan"</string>
@@ -516,7 +518,7 @@
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android tomonidan himoyalangan. Maʼlumotlaringiz qurilmada qayta ishlanishi sababli ilovaning ruxsatlardan foydalanish statistikasi maxfiylik boshqaruv panelida chiqmaydi."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Qurilma kamerasi bloklandi"</string>
<string name="blocked_microphone_title" msgid="1631517143648232585">"Qurilma mikrofoni bloklandi"</string>
- <string name="blocked_location_title" msgid="2005608279812892383">"Qurilmada joylashuv axboroti yoqilmagan"</string>
+ <string name="blocked_location_title" msgid="2005608279812892383">"Qurilmada joylashuvni aniqlash oʻchiq"</string>
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Ilovalar va xizmatlar uchun"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Favqulodda xizmat raqamiga telefon qilganingizda mikrofon maʼlumotlari hamon ulashilishi mumkin."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Oʻzgartirish"</string>
diff --git a/PermissionController/res/values-v31/styles.xml b/PermissionController/res/values-v31/styles.xml
index a05fd488b..e2f6378f5 100644
--- a/PermissionController/res/values-v31/styles.xml
+++ b/PermissionController/res/values-v31/styles.xml
@@ -90,22 +90,22 @@
parent="@style/PermissionGrantButtonTop"></style>
<style name="PermissionGrantButtonAllowOneTimeMaterial3"
parent="@style/PermissionGrantButtonMiddle"></style>
+ <style name="PermissionGrantButtonAllowSelectedMaterial3"
+ parent="@style/PermissionGrantButtonTop"></style>
<style name="PermissionGrantButtonAllowAllMaterial3"
parent="@style/PermissionGrantButtonMiddle"></style>
- <style name="PermissionGrantButtonAllowSelectedMaterial3"
- parent="@style/PermissionGrantButtonTop"></style>
- <style name="PermissionGrantButtonDontAllowMoreMaterial3"
- parent="@style/PermissionGrantButtonBottom"></style>
<style name="PermissionGrantButtonDenyMaterial3"
parent="@style/PermissionGrantButtonBottom"></style>
<style name="PermissionGrantButtonNoUpgradeMaterial3"
parent="@style/PermissionGrantButtonBottom"></style>
+ <style name="PermissionGrantButtonDontAllowMoreMaterial3"
+ parent="@style/PermissionGrantButtonBottom"></style>
<!-- END PERMISSION GRANT DIALOG -->
<style name="AppPermissionRadioButton"
parent="@android:style/Widget.DeviceDefault.CompoundButton.RadioButton">
- <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginTop">16dp</item>
<item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
diff --git a/PermissionController/res/values-v33/attrs.xml b/PermissionController/res/values-v33/attrs.xml
index b59716f6d..4a417076d 100644
--- a/PermissionController/res/values-v33/attrs.xml
+++ b/PermissionController/res/values-v33/attrs.xml
@@ -37,7 +37,10 @@
<attr name="scStatusTitleAndSummaryContainerStyle" format="reference" />
<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="colorScShieldAccent" format="color" />
</resources> \ No newline at end of file
diff --git a/PermissionController/res/values-v33/dimens.xml b/PermissionController/res/values-v33/dimens.xml
index ef70d2711..e6925bc54 100644
--- a/PermissionController/res/values-v33/dimens.xml
+++ b/PermissionController/res/values-v33/dimens.xml
@@ -24,20 +24,25 @@
<dimen name="sc_spacing_xlarge">18dp</dimen>
<dimen name="sc_spacing_xxlarge">20dp</dimen>
<dimen name="sc_spacing_xxxlarge">24dp</dimen>
- <dimen name="sc_large_screen_button_padding">56dp</dimen>
<dimen name="sc_card_margin">@dimen/sc_spacing_xxxsmall</dimen>
<dimen name="sc_list_margin">@dimen/sc_spacing_large</dimen>
<dimen name="sc_list_margin_top">@dimen/sc_spacing_small</dimen>
+
+ <!-- Only used for small screen views, but doesn't have _small_screen suffix since it's fixed in
+ the overlayable. -->
<dimen name="sc_action_button_list_margin">@dimen/sc_spacing_large</dimen>
+ <dimen name="sc_action_button_list_margin_large_screen">@dimen/sc_spacing_large</dimen>
<dimen name="sc_top_action_button_margin">@dimen/sc_spacing_xxxlarge</dimen>
+ <dimen name="sc_button_horizontal_padding_small_screen">@dimen/sc_spacing_xxxlarge</dimen>
+ <dimen name="sc_button_horizontal_padding_large_screen">56dp</dimen>
+
<dimen name="sc_entry_padding_end">@dimen/sc_spacing_xxxlarge</dimen>
<dimen name="sc_entry_group_expanded_padding_top">@dimen/sc_spacing_xxxlarge</dimen>
<dimen name="sc_entry_group_expanded_padding_bottom">@dimen/sc_spacing_xsmall</dimen>
<dimen name="sc_entry_group_collapsed_padding_top">@dimen/sc_spacing_large</dimen>
<dimen name="sc_entry_group_collapsed_padding_bottom">@dimen/sc_spacing_large</dimen>
<dimen name="sc_card_margin_bottom">@dimen/sc_spacing_xxxlarge</dimen>
- <dimen name="sc_button_horizontal_padding">@dimen/sc_spacing_xxxlarge</dimen>
<dimen name="sc_icon_button_touch_target_size">48dp</dimen>
<dimen name="sc_button_corner_radius">12dp</dimen>
diff --git a/PermissionController/res/values-v33/layout.xml b/PermissionController/res/values-v33/layout.xml
new file mode 100644
index 000000000..a963dae0d
--- /dev/null
+++ b/PermissionController/res/values-v33/layout.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item name="action_button_list_fixed" type="layout">
+ @layout/action_button_list_small_screen
+ </item>
+ <item name="action_button_list_responsive" type="layout">
+ @layout/action_button_list_small_screen
+ </item>
+</resources> \ No newline at end of file
diff --git a/PermissionController/res/values-v33/styles.xml b/PermissionController/res/values-v33/styles.xml
index a0938665f..94344615b 100644
--- a/PermissionController/res/values-v33/styles.xml
+++ b/PermissionController/res/values-v33/styles.xml
@@ -324,10 +324,10 @@
<style name="SafetyCenterActionButton"
parent="@style/Widget.MaterialComponents.Button.UnelevatedButton">
- <item name="android:theme">@style/Theme.MaterialComponents</item>
+ <item name="android:theme">?attr/scActionButtonTheme</item>
<item name="android:minHeight">56dp</item>
- <item name="android:paddingStart">@dimen/sc_button_horizontal_padding</item>
- <item name="android:paddingEnd">@dimen/sc_button_horizontal_padding</item>
+ <item name="android:paddingStart">@dimen/sc_button_horizontal_padding_small_screen</item>
+ <item name="android:paddingEnd">@dimen/sc_button_horizontal_padding_small_screen</item>
<item name="android:paddingTop">@dimen/sc_spacing_xlarge</item>
<item name="android:paddingBottom">@dimen/sc_spacing_xlarge</item>
<item name="android:insetTop">0dp</item>
@@ -340,13 +340,15 @@
<item name="cornerRadius">@dimen/sc_button_corner_radius</item>
<item name="rippleColor">?android:colorControlHighlight</item>
</style>
+ <style name="SafetyCenterActionButton.Fixed" />
+ <style name="SafetyCenterActionButton.Responsive" />
- <style name="SafetyCenterActionButton.Secondary"
+ <style name="SecondarySafetyCenterActionButton"
parent="@style/Widget.MaterialComponents.Button.OutlinedButton">
- <item name="android:theme">@style/Theme.MaterialComponents</item>
+ <item name="android:theme">?attr/scActionButtonTheme</item>
<item name="android:minHeight">56dp</item>
- <item name="android:paddingStart">@dimen/sc_button_horizontal_padding</item>
- <item name="android:paddingEnd">@dimen/sc_button_horizontal_padding</item>
+ <item name="android:paddingStart">@dimen/sc_button_horizontal_padding_small_screen</item>
+ <item name="android:paddingEnd">@dimen/sc_button_horizontal_padding_small_screen</item>
<item name="android:paddingTop">@dimen/sc_spacing_xlarge</item>
<item name="android:paddingBottom">@dimen/sc_spacing_xlarge</item>
<item name="android:insetTop">0dp</item>
@@ -361,6 +363,8 @@
<item name="cornerRadius">@dimen/sc_button_corner_radius</item>
<item name="rippleColor">?android:colorControlHighlight</item>
</style>
+ <style name="SecondarySafetyCenterActionButton.Fixed" />
+ <style name="SecondarySafetyCenterActionButton.Responsive" />
<!-- START SAFETY STATUS CARD -->
<style name="SafetyCenterCard.Status">
@@ -562,6 +566,12 @@
<item name="android:background">@drawable/safety_center_more_issues_card_background</item>
</style>
+ <style name="SafetyCenterMoreIssuesIcon"
+ parent="android:Widget.DeviceDefault">
+ <item name="android:layout_height">20dp</item>
+ <item name="android:layout_width">20dp</item>
+ </style>
+
<style name="SafetyCenterMoreIssuesTitle"
parent="SafetyCenterBaseTextContainer">
<item name="android:textAppearance">@style/TextAppearance.Material3.LabelLarge</item>
@@ -572,43 +582,22 @@
<item name="android:layout_marginEnd">@dimen/sc_spacing_xxlarge</item>
<item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
- <item name="layout_constraintTop_toTopOf">parent</item>
- <item name="layout_constraintBottom_toBottomOf">parent</item>
- <item name="layout_constraintStart_toEndOf">@id/status_icon</item>
- <item name="layout_constraintEnd_toStartOf">@android:id/widget_frame</item>
<item name="layout_constraintHorizontal_bias">0</item>
<item name="layout_goneMarginStart">0dp</item>
</style>
- <style name="SafetyCenterMoreIssuesIcon"
- parent="android:Widget.DeviceDefault">
- <item name="android:layout_height">20dp</item>
- <item name="android:layout_width">20dp</item>
- <item name="android:gravity">center</item>
- <item name="layout_constraintTop_toTopOf">parent</item>
- <item name="layout_constraintBottom_toBottomOf">parent</item>
- <item name="layout_constraintStart_toStartOf">parent</item>
- </style>
-
<style name="SafetyCenterMoreIssuesCounter"
parent="android:Widget.DeviceDefault">
- <item name="android:layout_height">24dp</item>
- <item name="android:layout_width">wrap_content</item>
- <item name="android:orientation">horizontal</item>
- <item name="android:paddingStart">@dimen/sc_spacing_xsmall</item>
- <item name="android:paddingEnd">@dimen/sc_spacing_xsmall</item>
+ <item name="android:layout_height">0dp</item>
+ <item name="android:layout_width">0dp</item>
<item name="android:background">@drawable/safety_center_card_widget_background</item>
- <item name="android:gravity">center_vertical</item>
- <item name="layout_constraintTop_toTopOf">parent</item>
- <item name="layout_constraintBottom_toBottomOf">parent</item>
- <item name="layout_constraintEnd_toEndOf">parent</item>
</style>
<style name="SafetyCenterMoreIssuesWidgetTitle"
parent="SafetyCenterBaseTextContainer">
<item name="android:textAppearance">@style/TextAppearance.SafetyCenter.Body</item>
- <item name="android:lineHeight">@dimen/sc_line_height_medium</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:paddingStart">@dimen/sc_spacing_xsmall</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_marginEnd">@dimen/sc_spacing_xxxsmall</item>
@@ -619,8 +608,8 @@
parent="android:Widget.DeviceDefault">
<item name="android:layout_height">16dp</item>
<item name="android:layout_width">16dp</item>
- <item name="android:gravity">center</item>
<item name="android:scaleType">fitCenter</item>
+ <item name="android:layout_marginEnd">@dimen/sc_spacing_xsmall</item>
</style>
<style name="SafetyCenterEntry"
@@ -789,12 +778,6 @@
<item name="android:layout_height">24dp</item>
</style>
- <style name="SafetyCenterNoLabelPreferenceCategory"
- parent="android:Widget.DeviceDefault">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">16dp</item>
- </style>
-
<style name="SafetyCenterGroupEntry"
parent="android:Widget.DeviceDefault">
<item name="android:layout_width">match_parent</item>
diff --git a/PermissionController/res/values-v33/themes.xml b/PermissionController/res/values-v33/themes.xml
index 338cbaa01..82a8ef5b6 100644
--- a/PermissionController/res/values-v33/themes.xml
+++ b/PermissionController/res/values-v33/themes.xml
@@ -47,14 +47,18 @@
<item name="scStatusButtonStyle">@style/SafetyCenterStatusButton.Fixed</item>
<!-- Buttons -->
- <item name="scActionButtonStyle">@style/SafetyCenterActionButton</item>
- <item name="scSecondaryActionButtonStyle">@style/SafetyCenterActionButton.Secondary</item>
+ <item name="scActionButtonListLayout">@layout/action_button_list_fixed</item>
+ <item name="scActionButtonTheme">
+ <!-- Dark-only theme for QS buttons -->
+ @style/Theme.MaterialComponents
+ </item>
+ <item name="scActionButtonStyle">@style/SafetyCenterActionButton.Fixed</item>
+ <item name="scSecondaryActionButtonStyle">
+ @style/SecondarySafetyCenterActionButton.Fixed
+ </item>
<item name="textColorScActionButton">@color/sc_primary_action_button_text</item>
- <!-- While the rest of the text colors automatically appear correct in quick settings,
- the outline action button text color needs to be set to primary inverse in light mode
- and primary in night mode to appear correctly in QS's faux permanent night mode. -->
- <item name="textColorScSecondaryActionButton">?android:attr/textColorPrimaryInverse</item>
+ <item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
<item name="colorScOutlineButtonInfoBase">@color/sc_outline_button_info_base_dark</item>
<item name="colorScOutlineButtonRecommendBase">
@@ -93,8 +97,12 @@
<item name="scStatusButtonStyle">@style/SafetyCenterStatusButton.Responsive</item>
<!-- Buttons -->
- <item name="scActionButtonStyle">@style/SafetyCenterActionButton</item>
- <item name="scSecondaryActionButtonStyle">@style/SafetyCenterActionButton.Secondary</item>
+ <item name="scActionButtonListLayout">@layout/action_button_list_responsive</item>
+ <item name="scActionButtonTheme">@style/Theme.MaterialComponents.DayNight</item>
+ <item name="scActionButtonStyle">@style/SafetyCenterActionButton.Responsive</item>
+ <item name="scSecondaryActionButtonStyle">
+ @style/SecondarySafetyCenterActionButton.Responsive
+ </item>
<item name="textColorScActionButton">@color/sc_primary_action_button_text</item>
<item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
diff --git a/PermissionController/res/values-v34/strings.xml b/PermissionController/res/values-v34/strings.xml
index 30181aa02..26a9b4bcc 100644
--- a/PermissionController/res/values-v34/strings.xml
+++ b/PermissionController/res/values-v34/strings.xml
@@ -28,7 +28,7 @@
<!-- Title for the link to location settings [CHAR LIMIT=30] -->
<string name="location_settings">Location access</string>
- <!-- Describes what is affected by the mic toggle [CHAR LIMIT=NONE] -->
+ <!-- Describes what is affected by the mic toggle. Unlike the similar tc/9163104307990677157, there should NOT be a full stop at the end of this sentence. [CHAR LIMIT=NONE] -->
<string name="mic_toggle_description">For apps and services. If this setting is off, microphone data may still be shared when you call an emergency number</string>
<!-- Subtitle for the link to location settings [CHAR LIMIT=NONE] -->
<string name="location_settings_subtitle">For apps and services</string>
diff --git a/PermissionController/res/values-vi-v33/strings.xml b/PermissionController/res/values-vi-v33/strings.xml
index d5fdffc1e..d0c8853c5 100644
--- a/PermissionController/res/values-vi-v33/strings.xml
+++ b/PermissionController/res/values-vi-v33/strings.xml
@@ -20,7 +20,7 @@
<string name="role_sms_request_description" msgid="1506966389698625395">"Ứng dụng này sẽ được phép gửi Thông báo cũng như được cấp quyền truy cập vào Máy ảnh, Danh bạ, Tệp, Micrô, Điện thoại và tin nhắn SMS của bạn"</string>
<string name="permission_description_summary_storage" msgid="1917071243213043858">"Ứng dụng được cấp quyền này có thể truy cập tất cả các tệp trên thiết bị này"</string>
<string name="work_policy_title" msgid="832967780713677409">"Thông tin về chính sách công việc của bạn"</string>
- <string name="work_policy_summary" msgid="3886113358084963931">"Chế độ cài đặt do quản trị viên CNTT quản lý"</string>
+ <string name="work_policy_summary" msgid="3886113358084963931">"Các chế độ cài đặt do quản trị viên CNTT quản lý"</string>
<string name="safety_center_entry_group_expand_action" msgid="5358289574941779652">"Mở rộng và hiện danh sách"</string>
<string name="safety_center_entry_group_collapse_action" msgid="1525710152244405656">"Thu gọn danh sách và ẩn chế độ cài đặt"</string>
<string name="safety_center_entry_group_content_description" msgid="7048420958214443333">"Danh sách. <xliff:g id="ENTRY_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_SUMMARY">%2$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-vi/strings.xml b/PermissionController/res/values-vi/strings.xml
index 3fa50ca58..0cbb48b43 100644
--- a/PermissionController/res/values-vi/strings.xml
+++ b/PermissionController/res/values-vi/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Thông tin khác"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Cho phép tất cả"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Luôn cho phép tất cả"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Cho phép quyền truy cập bị hạn chế"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Chọn ảnh và video"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Chọn thêm"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Không chọn thêm"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Ứng dụng"</string>
<string name="app_permissions" msgid="3369917736607944781">"Quyền ứng dụng"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ứng dụng không dùng đến"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Chỉnh sửa các ảnh đã chọn cho ứng dụng này"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Không có ứng dụng không dùng đến"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"0 ứng dụng không dùng đến"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Quyết định cấp quyền gần đây"</string>
@@ -203,7 +205,7 @@
<string name="unused_apps_category_title" msgid="2988455616845243901">"Chế độ cài đặt cho ứng dụng không dùng đến"</string>
<string name="auto_revoke_label" msgid="5068393642936571656">"Thu hồi quyền nếu bạn không dùng ứng dụng"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Thu hồi quyền và giải phóng dung lượng"</string>
- <string name="unused_apps_label_v2" msgid="7058776770056517980">"Tạm dừng hoạt động trong ứng dụng nếu không dùng"</string>
+ <string name="unused_apps_label_v2" msgid="7058776770056517980">"Tạm dừng hoạt động của ứng dụng nếu không dùng"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Loại bỏ quyền, xoá tệp tạm thời và dừng thông báo"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Để bảo vệ dữ liệu của bạn, các quyền cấp cho ứng dụng này sẽ bị thu hồi nếu bạn không dùng ứng dụng trong vài tháng."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Để bảo vệ dữ liệu của bạn, nếu bạn không dùng ứng dụng này trong vài tháng thì các quyền sau đây sẽ bị thu hồi: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
@@ -482,7 +484,7 @@
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Ứng dụng này có thể muốn ghi âm mọi lúc, ngay cả khi bạn không dùng ứng dụng. "<annotation id="link">"Cho phép trong phần cài đặt."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Thay đổi quyền sử dụng micrô của &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Ứng dụng này muốn ghi âm mọi lúc, ngay cả khi bạn không dùng ứng dụng. "<annotation id="link">"Cho phép trong phần cài đặt."</annotation></string>
- <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Bạn cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào hoạt động thể chất?"</string>
+ <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào dữ liệu hoạt động thể chất?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; chụp ảnh và quay video?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Ứng dụng này chỉ có thể chụp ảnh và quay video khi bạn đang dùng ứng dụng"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; chụp ảnh và quay video?"</string>
@@ -535,7 +537,7 @@
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Kiểm tra trạng thái"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"Các chế độ kiểm soát quyền riêng tư"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"Tùy chọn cài đặt khác"</string>
- <string name="camera_toggle_label_qs" msgid="3880261453066157285">"Quyền truy cập máy ảnh"</string>
+ <string name="camera_toggle_label_qs" msgid="3880261453066157285">"Quyền truy cập camera"</string>
<string name="microphone_toggle_label_qs" msgid="8132912469813396552">"Quyền truy cập micrô"</string>
<string name="permissions_removed_qs" msgid="8957319130625294572">"Đã thu hồi quyền sử dụng"</string>
<string name="camera_usage_qs" msgid="4394233566086665994">"Xem mức sử dụng của máy ảnh gần đây"</string>
@@ -577,7 +579,7 @@
<string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"Đã thay đổi quyền truy cập"</string>
<string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Xem thông tin về hoạt động sử dụng thông tin vị trí gần đây"</string>
<string name="privacy_controls_title" msgid="7605929972256835199">"Chế độ kiểm soát quyền riêng tư"</string>
- <string name="camera_toggle_title" msgid="1251201397431837666">"Quyền truy cập máy ảnh"</string>
+ <string name="camera_toggle_title" msgid="1251201397431837666">"Quyền truy cập camera"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"Quyền truy cập micrô"</string>
<string name="perm_toggle_description" msgid="7801326363741451379">"Áp dụng cho các ứng dụng và dịch vụ"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"Áp dụng cho các ứng dụng và dịch vụ. Nếu bạn tắt chế độ cài đặt, dữ liệu thu được qua micrô vẫn có thể được chia sẻ khi bạn gọi đến số khẩn cấp."</string>
diff --git a/PermissionController/res/values-w764dp-v33/layout.xml b/PermissionController/res/values-w764dp-v33/layout.xml
new file mode 100644
index 000000000..9035c8868
--- /dev/null
+++ b/PermissionController/res/values-w764dp-v33/layout.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item name="action_button_list_responsive" type="layout">
+ @layout/action_button_list_large_screen
+ </item>
+</resources> \ No newline at end of file
diff --git a/PermissionController/res/values-w764dp-v33/styles.xml b/PermissionController/res/values-w764dp-v33/styles.xml
index a836e4aa5..78f1bb42e 100644
--- a/PermissionController/res/values-w764dp-v33/styles.xml
+++ b/PermissionController/res/values-w764dp-v33/styles.xml
@@ -16,6 +16,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="SafetyCenterActionButton.Responsive">
+ <item name="android:paddingStart">@dimen/sc_button_horizontal_padding_large_screen</item>
+ <item name="android:paddingEnd">@dimen/sc_button_horizontal_padding_large_screen</item>
+ </style>
+
+ <style name="SecondarySafetyCenterActionButton.Responsive">
+ <item name="android:paddingStart">@dimen/sc_button_horizontal_padding_large_screen</item>
+ <item name="android:paddingEnd">@dimen/sc_button_horizontal_padding_large_screen</item>
+ </style>
+
<!-- START SAFETY STATUS CARD -->
<style name="SafetyCenterStatusTitleAndSummaryContainer.Responsive">
<item name="layout_constraintEnd_toStartOf">@id/sc_status_buttons_start_barrier</item>
@@ -30,8 +40,8 @@
<item name="layout_constraintStart_toEndOf">@id/status_title_and_summary</item>
<item name="layout_constraintEnd_toEndOf">parent</item>
<item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item>
- <item name="android:paddingStart">@dimen/sc_large_screen_button_padding</item>
- <item name="android:paddingEnd">@dimen/sc_large_screen_button_padding</item>
+ <item name="android:paddingStart">@dimen/sc_button_horizontal_padding_large_screen</item>
+ <item name="android:paddingEnd">@dimen/sc_button_horizontal_padding_large_screen</item>
<!-- Clear the base style constraints so they don't conflict with this style's constraints
on the same sides. -->
<item name="layout_constraintTop_toBottomOf">@null</item>
diff --git a/PermissionController/res/values-w764dp-v34/dimens.xml b/PermissionController/res/values-w764dp-v34/dimens.xml
deleted file mode 100644
index cb336fc3e..000000000
--- a/PermissionController/res/values-w764dp-v34/dimens.xml
+++ /dev/null
@@ -1,19 +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.
- -->
-
-<resources>
- <dimen name="sc_action_button_list_margin">@dimen/sc_spacing_large</dimen>
-</resources>
diff --git a/PermissionController/res/values-zh-rCN-v34/strings.xml b/PermissionController/res/values-zh-rCN-v34/strings.xml
index 0ba05798b..7bebdf9bc 100644
--- a/PermissionController/res/values-zh-rCN-v34/strings.xml
+++ b/PermissionController/res/values-zh-rCN-v34/strings.xml
@@ -19,7 +19,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="security_privacy_brand_name" msgid="7303621734258440812">"安全和隐私"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"控件"</string>
- <string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
+ <string name="health_connect_title" msgid="2132233890867430855">"健康数据共享"</string>
<string name="health_connect_summary" msgid="815473513776882296">"管理应用对健康数据的访问权限"</string>
<string name="location_settings" msgid="8863940440881290182">"位置信息访问权限"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"针对应用和服务。关闭此设置后,系统仍可能在您拨打紧急电话号码时分享麦克风数据"</string>
diff --git a/PermissionController/res/values-zh-rCN/strings.xml b/PermissionController/res/values-zh-rCN/strings.xml
index bde1c8f89..876425068 100644
--- a/PermissionController/res/values-zh-rCN/strings.xml
+++ b/PermissionController/res/values-zh-rCN/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"更多信息"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"全部允许"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"始终全部允许"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"允许有限访问"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"选择照片和视频"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"选择更多"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"不选择其他数据"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"应用"</string>
<string name="app_permissions" msgid="3369917736607944781">"应用权限"</string>
<string name="unused_apps" msgid="2058057455175955094">"闲置应用"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"重选此应用可访问的照片"</string>
<string name="no_unused_apps" msgid="12809387670415295">"无闲置应用"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"没有未使用的应用"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"最近的权限决定"</string>
@@ -219,7 +221,7 @@
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"已移除<xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g>以及<xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g>权限"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"已移除<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>权限及另外 <xliff:g id="NUMBER">%2$s</xliff:g> 项权限"</string>
<string name="unused_apps_page_title" msgid="6986983535677572559">"闲置应用"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"如果您连续几个月未使用某个应用,系统将对该应用采取以下措施:\n\n• 移除权限以保护您的数据\n• 停止通知功能以节省电量\n• 移除临时文件以释放空间\n\n如需重新授予权限以继续接收通知,请打开该应用。"</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"如果您连续几个月未使用某个应用,系统将对该应用采取以下措施:\n\n• 移除权限以保护您的数据\n• 停止通知功能以节省电量\n• 移除临时文件以释放空间\n\n如需重新授予权限和接收通知,请打开该应用。"</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"如果您连续 1 个月未使用某个应用,系统将对该应用采取以下措施:\n\n• 撤消权限以保护您的数据\n• 移除临时文件以释放空间\n\n如需重新授予权限,请打开该应用。"</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{距上次打开已超过 # 个月}other{距上次打开已超过 # 个月}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"应用上次打开时间是 <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -232,7 +234,7 @@
<string name="permission_description_summary_call_log" msgid="7321437186317577624">"具有此权限的应用可以读取和写入手机通话记录"</string>
<string name="permission_description_summary_camera" msgid="108004375101882069">"具有此权限的应用可以拍摄照片和录制视频"</string>
<string name="permission_description_summary_contacts" msgid="2337798886460408996">"具有此权限的应用可以访问您的通讯录"</string>
- <string name="permission_description_summary_location" msgid="2817531799933480694">"具有此权限的应用可以使用此设备的位置信息"</string>
+ <string name="permission_description_summary_location" msgid="2817531799933480694">"具有此权限的应用可以访问此设备的位置信息"</string>
<string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"具有这项权限的应用可以查找、连接附近设备,以及确定附近设备的相对位置"</string>
<string name="permission_description_summary_microphone" msgid="630834800308329907">"具有此权限的应用可以录制音频"</string>
<string name="permission_description_summary_phone" msgid="4515277217435233619">"具有此权限的应用可以拨打电话及管理通话"</string>
@@ -468,12 +470,12 @@
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”获取此设备的大致位置信息吗?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"确切位置"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"大致位置"</string>
- <string name="permgrouprequest_calendar" msgid="1493150855673603806">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问您的日历吗?"</string>
- <string name="permgrouprequest_sms" msgid="5672063688745420991">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;发送和查看短信吗?"</string>
+ <string name="permgrouprequest_calendar" msgid="1493150855673603806">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”访问您的日历吗?"</string>
+ <string name="permgrouprequest_sms" msgid="5672063688745420991">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”发送和查看短信吗?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"要允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”&lt;b&gt;&lt;/b&gt;访问您设备上的照片、媒体内容和文件吗?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”访问此设备上的&lt;b&gt;照片、视频、音乐和音频&lt;/b&gt;吗?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”访问此设备上的&lt;b&gt;照片、视频、音乐、音频和其他文件&lt;/b&gt;吗?"</string>
- <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问此设备上的音乐和音频吗?"</string>
+ <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”访问此设备上的音乐和音频吗?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”访问此设备上的照片和视频吗?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问此设备上的更多照片和视频?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”录音吗?"</string>
@@ -482,7 +484,7 @@
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"此应用可能想要随时录音,即使在您未使用它的时候。"<annotation id="link">"您可以在“设置”中授权"</annotation>"。"</string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"要更改&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;的麦克风使用权限吗?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"此应用想要随时录音,即使在您未使用它的时候。"<annotation id="link">"您可以在“设置”中授权"</annotation>"。"</string>
- <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;获取您的身体活动数据吗?"</string>
+ <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”获取您的身体活动数据吗?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”拍摄照片和录制视频吗?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"此应用将只能在您使用它时拍摄照片和录制视频"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”拍摄照片和录制视频吗?"</string>
@@ -493,8 +495,8 @@
<string name="permgrouprequest_phone" msgid="1829234136997316752">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”拨打电话和管理通话吗?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问与您的生命体征相关的传感器数据吗?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"此应用希望随时都能访问与您的生命体征相关的传感器数据(即使在您未使用此应用时)。如要进行这种权限更改,请"<annotation id="link">"前往设置页面"</annotation>"。"</string>
- <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问与您的生命体征相关的传感器数据吗?"</string>
- <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"如要允许此应用始终可以访问身体传感器数据(即使在您未使用此应用时),请"<annotation id="link">"前往设置"</annotation>"。"</string>
+ <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”访问与您的生命体征相关的传感器数据吗?"</string>
+ <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"如要允许此应用随时可以访问身体传感器数据(即使在您未使用此应用时),请"<annotation id="link">"前往设置"</annotation>"。"</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"始终允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”在使用中时访问身体传感器数据?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”向您发送通知吗?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"受控权限"</string>
@@ -607,10 +609,10 @@
<string name="app_permission_rationale_message" msgid="8511466916077100713">"数据安全"</string>
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"可能会分享位置数据"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"此应用已声明它可能会与第三方分享您的位置数据"</string>
- <string name="data_sharing_updates_title" msgid="7996933386875213859">"位置数据分享方面的更新"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"查看改变了位置数据分享方式的应用"</string>
+ <string name="data_sharing_updates_title" msgid="7996933386875213859">"位置数据共享方面的更新"</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"查看改变了位置数据共享方式的应用"</string>
<string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"这些应用改变了它们在分享您位置数据上的做法。它们之前可能未分享过位置数据,也可能现在是为了广告或营销目的而分享此类数据。"</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"这些应用的开发者已将其数据分享做法相关信息提供给应用商店。此类信息可能会随时间更新。\n\n数据分享做法可能会因应用版本、使用情况、地区和年龄而异。"</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"这些应用的开发者已将其数据分享做法的相关信息提供给应用商店,并且可能会随时间更新此类信息。\n\n数据分享做法可能会因应用版本、使用情况、地区和年龄而异。"</string>
<string name="learn_about_data_sharing" msgid="4200480587079488045">"了解数据分享"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"现在会将您的位置数据分享给第三方"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"现在会将您的位置数据分享给第三方,用于广告或营销目的"</string>
diff --git a/PermissionController/res/values-zh-rHK-v34/strings.xml b/PermissionController/res/values-zh-rHK-v34/strings.xml
index 65ad05bdb..3fb87001c 100644
--- a/PermissionController/res/values-zh-rHK-v34/strings.xml
+++ b/PermissionController/res/values-zh-rHK-v34/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="security_privacy_brand_name" msgid="7303621734258440812">"保安和私隱"</string>
+ <string name="security_privacy_brand_name" msgid="7303621734258440812">"安全性和私隱"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"控制項"</string>
<string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
<string name="health_connect_summary" msgid="815473513776882296">"管理應用程式的健康資料存取權"</string>
diff --git a/PermissionController/res/values-zh-rHK/strings.xml b/PermissionController/res/values-zh-rHK/strings.xml
index 3d92d8caf..830dd8eb0 100644
--- a/PermissionController/res/values-zh-rHK/strings.xml
+++ b/PermissionController/res/values-zh-rHK/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"更多資料"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"全部允許"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"一律全部允許"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"允許有限存取"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"選取相片和影片"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"選取更多項目"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"不選取更多"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"應用程式"</string>
<string name="app_permissions" msgid="3369917736607944781">"應用程式權限"</string>
<string name="unused_apps" msgid="2058057455175955094">"不使用的應用程式"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"設定此應用程式可存取哪些相片"</string>
<string name="no_unused_apps" msgid="12809387670415295">"沒有不使用的應用程式"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"沒有未使用的應用程式"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"最近的權限決定"</string>
@@ -200,7 +202,7 @@
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"查看「<xliff:g id="APP">%1$s</xliff:g>」的所有權限"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"查看擁有此權限的所有應用程式"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"顯示「Google 助理」麥克風使用情況"</string>
- <string name="unused_apps_category_title" msgid="2988455616845243901">"未使用的應用程式設定"</string>
+ <string name="unused_apps_category_title" msgid="2988455616845243901">"不使用的應用程式設定"</string>
<string name="auto_revoke_label" msgid="5068393642936571656">"如不使用應用程式,即移除權限"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"移除權限並騰出空間"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"暫停未使用應用程式的活動"</string>
@@ -474,7 +476,7 @@
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"要允許 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 存取此裝置上的&lt;b&gt;相片、影片、音樂和音訊&lt;/b&gt;嗎?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"要允許 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 存取此裝置上的&lt;b&gt;相片、影片、音樂、音訊和其他檔案&lt;/b&gt;嗎?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"要允許 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 存取此裝置上的音樂和音訊嗎?"</string>
- <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"要允許 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 存取此裝置上的相片和影片嗎?"</string>
+ <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取此裝置上的相片和影片嗎?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取此裝置上更多的相片和影片?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;錄音嗎?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"此應用程式將只能在你使用期間錄音"</string>
@@ -496,7 +498,7 @@
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取與你生命體徵相關的感應器資料嗎?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"如要讓此應用程式隨時存取人體感應器資料 (即使在你沒有使用此應用程式時),請"<annotation id="link">"前往設定"</annotation>"。"</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"要讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」應用程式&lt;b&gt;&lt;/b&gt;在使用時存取人體感應器資料嗎?"</string>
- <string name="permgrouprequest_notifications" msgid="6396739062335106181">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;傳送通知給你嗎?"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;傳送通知給你嗎?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"由管理員控制的權限"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」有位置存取權"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"你的機構允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取位置資訊"</string>
@@ -520,7 +522,7 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"應用程式和服務"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"撥打緊急電話號碼時,系統仍可能會分享麥克風資料。"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"變更"</string>
- <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"保安和私隱"</string>
+ <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"安全性和私隱"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"掃瞄裝置"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"關閉"</string>
<string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"要關閉此警示嗎?"</string>
@@ -531,7 +533,7 @@
<string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"安全性和私隱狀態。<xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>。<xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
<string name="security_settings" msgid="3808106921175271317">"安全性設定"</string>
<string name="sensor_permissions_qs" msgid="1022267900031317472">"權限"</string>
- <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"保安和私隱"</string>
+ <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"安全性和私隱"</string>
<string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"檢查狀態"</string>
<string name="privacy_controls_qs" msgid="5780144882040591169">"你的私隱權設定"</string>
<string name="security_settings_button_label_qs" msgid="8280343822465962330">"更多設定"</string>
diff --git a/PermissionController/res/values-zh-rTW-car/strings.xml b/PermissionController/res/values-zh-rTW-car/strings.xml
index 24062f2d5..2ae9d1c01 100644
--- a/PermissionController/res/values-zh-rTW-car/strings.xml
+++ b/PermissionController/res/values-zh-rTW-car/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="unused_apps_page_summary" msgid="7505839764289846511">"如果你數個月未使用某應用程式,系統將對該應用程式採取以下措施:\n\n• 移除權限以保護你的資料\n• 移除暫存檔以釋出空間"</string>
+ <string name="unused_apps_page_summary" msgid="7505839764289846511">"如果數個月沒有使用應用程式,系統會採取以下措施:\n\n• 移除權限以保護資料\n• 移除暫存檔以釋出空間"</string>
</resources>
diff --git a/PermissionController/res/values-zh-rTW-v34/strings.xml b/PermissionController/res/values-zh-rTW-v34/strings.xml
index e473ca41b..7f4955b39 100644
--- a/PermissionController/res/values-zh-rTW-v34/strings.xml
+++ b/PermissionController/res/values-zh-rTW-v34/strings.xml
@@ -19,9 +19,9 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="security_privacy_brand_name" msgid="7303621734258440812">"安全性與隱私權"</string>
<string name="privacy_subpage_controls_header" msgid="4152396976713749322">"控制選項"</string>
- <string name="health_connect_title" msgid="2132233890867430855">"Health Connect"</string>
+ <string name="health_connect_title" msgid="2132233890867430855">"健康資料同步"</string>
<string name="health_connect_summary" msgid="815473513776882296">"管理應用程式的健康資料存取權"</string>
<string name="location_settings" msgid="8863940440881290182">"位置資訊存取權"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"適用於應用程式和服務。即使關閉這項設定,系統仍可能會在你撥打緊急電話號碼時,分享麥克風資料"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"應用程式和服務"</string>
+ <string name="location_settings_subtitle" msgid="6846532794702613851">"適用於應用程式和服務"</string>
</resources>
diff --git a/PermissionController/res/values-zh-rTW/strings.xml b/PermissionController/res/values-zh-rTW/strings.xml
index 422b072a8..b727d20d4 100644
--- a/PermissionController/res/values-zh-rTW/strings.xml
+++ b/PermissionController/res/values-zh-rTW/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"更多資訊"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"全部允許"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"一律全部允許"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"允許有限存取"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"選取相片和影片"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"選取更多項目"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"不要選取其他資料"</string>
@@ -52,7 +53,7 @@
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"一律允許"</string>
<string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"使用應用程式時"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"改用精確位置"</string>
- <string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"繼續使用概略位置"</string>
+ <string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"繼續使用大概位置"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"僅允許這一次"</string>
<string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"一律允許"</string>
<string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"允許管理所有檔案"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"應用程式"</string>
<string name="app_permissions" msgid="3369917736607944781">"應用程式權限"</string>
<string name="unused_apps" msgid="2058057455175955094">"未使用的應用程式"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"設定這個應用程式可存取哪些相片"</string>
<string name="no_unused_apps" msgid="12809387670415295">"沒有未使用的應用程式"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"沒有未使用的應用程式"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"最近的權限決定"</string>
@@ -70,7 +72,7 @@
<string name="denied_permission_decision" msgid="5308961501779563781">"你已拒絕「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取<xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
<string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{今天}=1{1 天前}other{# 天前}}"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"停用應用程式"</string>
- <string name="app_disable_dlg_text" msgid="3126943217146120240">"如果你停用這個應用程式,Android 和其他應用程式可能無法正常運作。請注意,這是預先安裝在裝置上的應用程式,因此無法刪除。不過,只要停用這個應用程式,即可將應用程式關閉並在裝置上隱藏。"</string>
+ <string name="app_disable_dlg_text" msgid="3126943217146120240">"如果你停用這個應用程式,Android 和其他應用程式可能無法正常運作。請注意,這是預先安裝在裝置上的應用程式,因此無法刪除。不過,只要停用這個應用程式,即可關閉該程式並在裝置上隱藏。"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"權限管理員"</string>
<string name="never_ask_again" msgid="4728762438198560329">"不要再詢問"</string>
<string name="no_permissions" msgid="3881676756371148563">"沒有權限"</string>
@@ -83,7 +85,7 @@
<string name="default_permission_description" msgid="4624464917726285203">"執行不明的動作"</string>
<string name="app_permissions_group_summary" msgid="8788419008958284002">"已授權給 <xliff:g id="COUNT_0">%1$d</xliff:g> 個應用程式 (共 <xliff:g id="COUNT_1">%2$d</xliff:g> 個)"</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> 個)"</string>
- <string name="menu_show_system" msgid="4254021607027872504">"顯示系統設定"</string>
+ <string name="menu_show_system" msgid="4254021607027872504">"顯示系統程序"</string>
<string name="menu_hide_system" msgid="3855390843744028465">"隱藏系統程序"</string>
<string name="menu_show_7_days_data" msgid="8979611198508523706">"顯示過去 7 天內的使用情況"</string>
<string name="menu_show_24_hours_data" msgid="8228054833323380780">"顯示過去 24 小時內的使用情況"</string>
@@ -192,11 +194,11 @@
<string name="app_permission_button_ask" msgid="3342950658789427">"每次都詢問"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"不允許"</string>
<string name="precise_image_description" msgid="6349638632303619872">"精確位置"</string>
- <string name="approximate_image_description" msgid="938803699637069884">"概略位置"</string>
+ <string name="approximate_image_description" msgid="938803699637069884">"大概位置"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"使用精確位置"</string>
- <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"精確位置關閉時,應用程式會存取你的概略位置資訊"</string>
+ <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"精確位置關閉時,應用程式會存取你的大概位置資訊"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g>權限"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"是否允許這個應用程式取得「<xliff:g id="PERM">%1$s</xliff:g>」的存取權限"</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"是否允許這個應用程式存取「<xliff:g id="PERM">%1$s</xliff:g>」資訊"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"查看「<xliff:g id="APP">%1$s</xliff:g>」的所有權限"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"查看具備此權限的所有應用程式"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"顯示 Google 助理的麥克風使用狀況"</string>
@@ -219,7 +221,7 @@
<string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"已移除<xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g>和<xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g>存取權"</string>
<string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"已移除<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>存取權和另外 <xliff:g id="NUMBER">%2$s</xliff:g> 項權限"</string>
<string name="unused_apps_page_title" msgid="6986983535677572559">"未使用的應用程式"</string>
- <string name="unused_apps_page_summary" msgid="1867593913217272155">"如果你數個月未使用某個應用程式,系統將對該應用程式採取以下措施:\n\n• 移除權限以保護你的資料\n• 停止通知以節省電力\n• 移除暫存檔以釋出空間\n\n如要重新授予權限和再次接收通知,請開啟應用程式。"</string>
+ <string name="unused_apps_page_summary" msgid="1867593913217272155">"如果數個月沒有使用應用程式,系統會採取以下措施:\n\n• 移除權限以保護資料\n• 停止通知以節省電量\n• 移除暫存檔以釋出空間\n\n如要重新授予權限和再次接收通知,請開啟應用程式。"</string>
<string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"如果你一個月未使用某應用程式,系統將對該應用程式採取以下措施:\n\n• 移除權限以保護你的資料\n• 移除暫存檔以釋出空間\n\n如要重新授權,只要開啟應用程式即可。"</string>
<string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{距離上次開啟已超過 # 個月}other{距離上次開啟已超過 # 個月}}"</string>
<string name="last_opened_summary" msgid="5248984030024968808">"應用程式上次開啟時間:<xliff:g id="DATE">%s</xliff:g>"</string>
@@ -347,21 +349,21 @@
<string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"有 <xliff:g id="NUM_SERVICES">%s</xliff:g> 個無障礙應用程式可完整存取你的裝置"</string>
<string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"「<xliff:g id="SERVICE_NAME">%s</xliff:g>」不但可查看你的畫面、動作和輸入內容,還能執行動作及控制顯示畫面。"</string>
<string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"這些應用程式不但可查看你的畫面、動作和輸入內容,還能執行動作及控制顯示畫面。"</string>
- <string name="role_assistant_label" msgid="4727586018198208128">"預設的數位助理應用程式"</string>
+ <string name="role_assistant_label" msgid="4727586018198208128">"預設數位助理應用程式"</string>
<string name="role_assistant_short_label" msgid="3369003713187703399">"數位助理應用程式"</string>
- <string name="role_assistant_description" msgid="6622458130459922952">"小幫手應用程式可根據你目前瀏覽的畫面資訊,為你提供協助。部分應用程式同時支援啟動器和語音輸入服務,能夠為你提供更完善的協助。"</string>
- <string name="role_browser_label" msgid="2877796144554070207">"預設的瀏覽器應用程式"</string>
+ <string name="role_assistant_description" msgid="6622458130459922952">"小幫手應用程式可根據當下的螢幕內容提供協助。某些應用程式同時支援啟動器和語音輸入服務,提供更完善的服務。"</string>
+ <string name="role_browser_label" msgid="2877796144554070207">"預設瀏覽器應用程式"</string>
<string name="role_browser_short_label" msgid="6745009127123292296">"瀏覽器應用程式"</string>
- <string name="role_browser_description" msgid="3465253637499842671">"這類應用程式可讓你上網及顯示你點選的連結"</string>
+ <string name="role_browser_description" msgid="3465253637499842671">"你可以透過這類應用程式連上網際網路,也可以輕觸顯示的連結"</string>
<string name="role_browser_request_title" msgid="2895200507835937192">"要將 <xliff:g id="APP_NAME">%1$s</xliff:g> 設為預設的瀏覽器應用程式嗎?"</string>
<string name="role_browser_request_description" msgid="5888803407905985941">"無需任何權限"</string>
- <string name="role_dialer_label" msgid="1100224146343237968">"預設的電話應用程式"</string>
+ <string name="role_dialer_label" msgid="1100224146343237968">"預設電話應用程式"</string>
<string name="role_dialer_short_label" msgid="7186888549465352489">"電話應用程式"</string>
<string name="role_dialer_description" msgid="8768708633696539612">"這類應用程式可讓你透過裝置撥打及接聽電話"</string>
<string name="role_dialer_request_title" msgid="5959618560705912058">"要將「<xliff:g id="APP_NAME">%1$s</xliff:g>」設為預設的電話應用程式嗎?"</string>
<string name="role_dialer_request_description" msgid="6288839625724909320">"這個應用程式將可存取你的相機、聯絡人、麥克風、電話及簡訊"</string>
<string name="role_dialer_search_keywords" msgid="3324448983559188087">"撥號程式"</string>
- <string name="role_sms_label" msgid="8456999857547686640">"預設的簡訊應用程式"</string>
+ <string name="role_sms_label" msgid="8456999857547686640">"預設簡訊應用程式"</string>
<string name="role_sms_short_label" msgid="4371444488034692243">"簡訊應用程式"</string>
<string name="role_sms_description" msgid="3424020199148153513">"這類應用程式可讓你透過自己的電話號碼收發簡訊、相片、影片和其他內容"</string>
<string name="role_sms_request_title" msgid="7953552109601185602">"要將「<xliff:g id="APP_NAME">%1$s</xliff:g>」設為預設的簡訊應用程式嗎?"</string>
@@ -373,7 +375,7 @@
<string name="role_emergency_request_title" msgid="8469579020654348567">"要將「<xliff:g id="APP_NAME">%1$s</xliff:g>」設為預設的緊急應用程式嗎?"</string>
<string name="role_emergency_request_description" msgid="131645948770262850">"無需任何權限"</string>
<string name="role_emergency_search_keywords" msgid="1920007722599213358">"發生緊急情況時"</string>
- <string name="role_home_label" msgid="3871847846649769412">"預設的主畫面應用程式"</string>
+ <string name="role_home_label" msgid="3871847846649769412">"預設主畫面應用程式"</string>
<string name="role_home_short_label" msgid="8544733747952272337">"主畫面應用程式"</string>
<string name="role_home_description" msgid="7997371519626556675">"這類應用程式通常稱為啟動器,能夠取代 Android 裝置的主畫面,並可讓你存取裝置的內容與功能"</string>
<string name="role_home_request_title" msgid="738136983453341081">"要將「<xliff:g id="APP_NAME">%1$s</xliff:g>」設為預設的主畫面應用程式嗎?"</string>
@@ -464,10 +466,10 @@
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"這個應用程式想要隨時存取你的位置資訊 (即使在你未使用此應用程式時)。"<annotation id="link">"如要授予權限,請前往「設定」"</annotation>"。"</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;尋找、連結及判斷附近裝置的相對位置嗎?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;尋找、連結及判斷附近裝置的相對位置嗎?"<annotation id="link">"請前往「設定」授予權限。"</annotation></string>
- <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"要將「<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>」可以存取的定位資訊從「概略位置」改為「精確位置」嗎?"</string>
- <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置的概略位置資訊嗎?"</string>
+ <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"要將「<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>」可以存取的定位資訊從「大概位置」改為「精確位置」嗎?"</string>
+ <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置的大概位置資訊嗎?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"精確"</string>
- <string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"概略"</string>
+ <string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"大概"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取你的日曆嗎?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」傳送及查看簡訊嗎?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取裝置中的相片、媒體和檔案嗎?"</string>
@@ -517,7 +519,7 @@
<string name="blocked_camera_title" msgid="1128510551791284384">"裝置相機已封鎖"</string>
<string name="blocked_microphone_title" msgid="1631517143648232585">"裝置麥克風已封鎖"</string>
<string name="blocked_location_title" msgid="2005608279812892383">"裝置位置資訊已關閉"</string>
- <string name="blocked_sensor_summary" msgid="4443707628305027375">"應用程式和服務"</string>
+ <string name="blocked_sensor_summary" msgid="4443707628305027375">"適用於應用程式和服務"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"當你撥打緊急電話號碼時,系統仍會提供麥克風的資料。"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"變更"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"安全性與隱私權"</string>
@@ -579,9 +581,9 @@
<string name="privacy_controls_title" msgid="7605929972256835199">"隱私權控制項"</string>
<string name="camera_toggle_title" msgid="1251201397431837666">"相機存取權"</string>
<string name="mic_toggle_title" msgid="2649991093496110162">"麥克風存取權"</string>
- <string name="perm_toggle_description" msgid="7801326363741451379">"應用程式和服務"</string>
+ <string name="perm_toggle_description" msgid="7801326363741451379">"適用於應用程式和服務"</string>
<string name="mic_toggle_description" msgid="9163104307990677157">"適用於應用程式和服務。即使關閉這項設定,系統仍可能會在你撥打緊急電話號碼時,分享麥克風資料。"</string>
- <string name="location_settings_subtitle" msgid="2328360561197430695">"查看對位置擁有存取權的應用程式與服務"</string>
+ <string name="location_settings_subtitle" msgid="2328360561197430695">"查看可存取位置資訊的應用程式與服務"</string>
<string name="show_clip_access_notification_title" msgid="5168467637351109096">"顯示剪貼簿存取通知"</string>
<string name="show_clip_access_notification_summary" msgid="3532020182782112687">"系統會在應用程式存取你複製的文字、圖片或其他內容時顯示通知訊息"</string>
<string name="show_password_title" msgid="2877269286984684659">"顯示密碼"</string>
@@ -607,11 +609,11 @@
<string name="app_permission_rationale_message" msgid="8511466916077100713">"資料安全"</string>
<string name="app_location_permission_rationale_title" msgid="925420340572401350">"可能會分享位置資料"</string>
<string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"這個應用程式表示可能會將位置資料分享給第三方"</string>
- <string name="data_sharing_updates_title" msgid="7996933386875213859">"位置資料分享更新"</string>
- <string name="data_sharing_updates_summary" msgid="764113985772233889">"查看哪些應用程式變更了分享位置資料的做法"</string>
- <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"以下應用程式變更了分享位置資料的做法。這些應用程式先前可能未曾分享位置資料,也可能是現在為了廣告或行銷目的而分享這些資料。"</string>
- <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"這些應用程式的開發人員已將資料分享做法相關資訊提供給應用程式商店,並可能會隨著時間更新這些資訊。\n\n資料分享做法可能會因你的應用程式版本、使用方式、所在地區和年齡而異。"</string>
- <string name="learn_about_data_sharing" msgid="4200480587079488045">"瞭解資料分享"</string>
+ <string name="data_sharing_updates_title" msgid="7996933386875213859">"位置資料共用方式更新"</string>
+ <string name="data_sharing_updates_summary" msgid="764113985772233889">"查看曾變更位置資料共用方式的應用程式"</string>
+ <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"以下應用程式變更了共用位置資料的做法。這些應用程式先前可能未曾共用位置資料,也可能是現在為了廣告或行銷目的而共用這些資料。"</string>
+ <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"這些應用程式的開發人員已將資料共用做法相關資訊提供給應用程式商店,並可能會隨著時間更新這些資訊。\n\n資料共用做法可能會因你的應用程式版本、使用方式、所在地區和年齡而異。"</string>
+ <string name="learn_about_data_sharing" msgid="4200480587079488045">"瞭解資料共用"</string>
<string name="shares_location_with_third_parties" msgid="2278051743742057767">"你的位置資料目前已分享給第三方"</string>
<string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"你的位置資料目前已分享給第三方服務作為廣告或行銷用途"</string>
<string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{在 1 天內曾更新}=1{在 1 天內曾更新}other{在 # 天內曾更新}}"</string>
diff --git a/PermissionController/res/values-zu/strings.xml b/PermissionController/res/values-zu/strings.xml
index d1abf785d..0ad2b637b 100644
--- a/PermissionController/res/values-zu/strings.xml
+++ b/PermissionController/res/values-zu/strings.xml
@@ -34,6 +34,7 @@
<string name="grant_dialog_button_more_info" msgid="213350268561945193">"Olunye ulwazi"</string>
<string name="grant_dialog_button_allow_all" msgid="5939066403732409516">"Vumela konke"</string>
<string name="grant_dialog_button_always_allow_all" msgid="1719900027660252167">"Njalo vumela konke"</string>
+ <string name="grant_dialog_button_allow_limited_access" msgid="5713551784422137594">"Vumela ukufinyelela okulinganiselwe"</string>
<string name="grant_dialog_button_allow_selected_photos" msgid="5497042471576153842">"Khetha izithombe namavidiyo"</string>
<string name="grant_dialog_button_allow_more_selected_photos" msgid="5145657877588697709">"Khetha okuningi"</string>
<string name="grant_dialog_button_dont_select_more" msgid="6643552729129461268">"Ungakhethi okuningi"</string>
@@ -50,7 +51,7 @@
<string name="permission_revoked_none" msgid="9213345075484381180">"Lutho olukhutshaziwe"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"Vumela"</string>
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"Vumela sonke isikhathi"</string>
- <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Ngenkathi usebenzisa uhlelo lokusebenza"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"Ngenkathi i-app"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"Shintshela kwindawo ngqo"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"Gcina indawo elinganiselwayo"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"Ngalesi sikhathi kuphela"</string>
@@ -60,6 +61,7 @@
<string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Izinhlelo zokusebenza"</string>
<string name="app_permissions" msgid="3369917736607944781">"Izimvume zohlelo lokusebenza"</string>
<string name="unused_apps" msgid="2058057455175955094">"Izinhlelo zokusebenza ezingasetshenzisiwe"</string>
+ <string name="edit_photos_description" msgid="5540108003480078892">"Hlela izithobe ezikhethiwe zale App"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Awekho ama-app angasetshenzisiwe"</string>
<string name="zero_unused_apps" msgid="9024448554157499748">"Ama-app angasetshenzisiwe angu-0"</string>
<string name="review_permission_decisions" msgid="309559429150613632">"Izinqumo zemvume yakamuva"</string>
@@ -90,7 +92,7 @@
<string name="manage_permission" msgid="2895385393037061964">"Phatha izimvume"</string>
<string name="no_apps" msgid="2412612731628386816">"Azikho izinhlelo zokusebenza"</string>
<string name="location_settings" msgid="3624412509133422562">"Amasethingi Endawo"</string>
- <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> ingumhlinzeki wamasevisi wendawo kule divayisi. Ukufinyelela kwendawo kungashintshwa kusuka kuzilungiselelo zendawo."</string>
+ <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> ingumhlinzeki wamasevisi wendawo kule divayisi. Ukufinyelela kwendawo kungashintshwa kusuka kumasethingi endawo."</string>
<string name="system_warning" msgid="1173400963234358816">"Uma unqabela le mvume, izici eziyisisekelo zedivayisi yakho zingahle zingasasebenzi njengoba zihlosiwe."</string>
<string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Le app iklanyelwe uhlobo lwakudala le-Android. Uma unqabela le app ukufinyelela izithombe namavidiyo, ukufinyelela kumculo nokunye okulalelwayo nakho kuzonqatshelwa."</string>
<string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Le app iklanyelwe uhlobo lwakudala le-Android. Uma wenqabela le app ukufinyelela emculweni nokunye okulalelwayo, ukufinyelela izithombe namavidiyo nakho kuzonqatshelwa."</string>
@@ -196,7 +198,7 @@
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Sebenzisa indawo eqondile"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Uma indawo ngqo ivaliwe, ama-app angakwazi ukufinyelela indawo yakho elinganiselwayo"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> imvume"</string>
- <string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g> ukufinyelela kwale app"</string>
+ <string name="app_permission_header" msgid="2951363137032603806">"ukufinyelela kwale app ku<xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Bona zonke izimvume ze-<xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Bona zonke izinhlelo zokusebenza ngale mvume"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Bonisa ukusetshenziswa kwe-microphone kamsizi"</string>
@@ -483,7 +485,7 @@
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Shintsha ukufinyelela kwemakrofoni kwe-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Lolu hlelo lokusebenza lufuna ukurekhoda imisindo ngaso sonke isikhathi, ngisho nalapho ungasebenzisi uhlelo lokusebenza. "<annotation id="link">"Vumela kumasethingi."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele kumsebenzi wakho womzimba?"</string>
- <string name="permgrouprequest_camera" msgid="5123097035410002594">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthatha izithombe iphinde irekhode ividiyo?"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ithathe izithombe futhi irekhode ividiyo?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Lolu hlelo lokusebenza luzokwazi ukuthatha izithombe futhi lirekhode ividiyo kuphela kuyilapho usebenzisa uhlelo lokusebenza"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthatha izithombe iphinde irekhode ividiyo?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Lolu hlelo lokusebenza lungafuna ukuthatha izithombe futhi lirekhode ividiyo ngaso sonke isikhathi, ngisho nalapho ungasebenzi uhlelo lokusebenza. "<annotation id="link">"Vumela kumasethingi."</annotation></string>
diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml
index 850b5df40..8f81e446b 100644
--- a/PermissionController/res/values/overlayable.xml
+++ b/PermissionController/res/values/overlayable.xml
@@ -37,12 +37,24 @@
<item type="style" name="PermissionGrantTitleMessage" />
<item type="style" name="PermissionGrantDetailMessage" />
+ <item type="style" name="PermissionGrantPermissionRationaleContent" />
+ <item type="style" name="PermissionGrantPermissionRationaleIcon" />
+ <item type="style" name="PermissionGrantPermissionRationaleMessage" />
+ <item type="style" name="PermissionGrantPermissionRationaleMoreInfoIcon" />
+
+ <item type="color" name="permission_rationale_overview_background" />
+
+ <item type="style" name="PermissionLocationAccuracyRadioGroupMaterial3" />
+ <item type="style" name="PermissionLocationAccuracyFineImageViewMaterial3" />
+ <item type="style" name="PermissionLocationAccuracyCoarseImageViewMaterial3" />
+
+ <item type="style" name="PermissionLocationAccuracyRadioFine" />
+ <item type="style" name="PermissionLocationAccuracyRadioCoarse" />
+
<item type="style" name="PermissionGrantButtonList" />
<item type="style" name="PermissionGrantButtonBarSpace" />
<item type="style" name="PermissionGrantButton" />
- <!-- IDs for V31 only -->
-
<item type="style" name="PermissionGrantButtonTop" />
<item type="style" name="PermissionGrantButtonMiddle" />
<item type="style" name="PermissionGrantButtonBottom" />
@@ -52,23 +64,27 @@
<item type="style" name="PermissionGrantButtonAllowMaterial3" />
<item type="style" name="PermissionGrantButtonAllowForegroundMaterial3" />
<item type="style" name="PermissionGrantButtonAllowOneTimeMaterial3" />
+ <item type="style" name="PermissionGrantButtonAllowSelectedMaterial3" />
+ <item type="style" name="PermissionGrantButtonAllowAllMaterial3" />
<item type="style" name="PermissionGrantButtonDenyMaterial3" />
<item type="style" name="PermissionGrantButtonNoUpgradeMaterial3" />
+ <item type="style" name="PermissionGrantButtonDontAllowMoreMaterial3" />
+
+ <!-- Used in V30 only -->
- <!-- END IDs for V31 only -->
+ <item type="style" name="PermissionLocationAccuracyFineImageView" />
+ <item type="style" name="PermissionLocationAccuracyCoarseImageView" />
<item type="style" name="PermissionGrantButtonAllow" />
<item type="style" name="PermissionGrantButtonAllowForeground" />
<item type="style" name="PermissionGrantButtonAllowOneTime" />
+ <item type="style" name="PermissionGrantButtonAllowSelected" />
+ <item type="style" name="PermissionGrantButtonAllowAll" />
<item type="style" name="PermissionGrantButtonDeny" />
<item type="style" name="PermissionGrantButtonNoUpgrade" />
+ <item type="style" name="PermissionGrantButtonDontAllowMore" />
- <item type="style" name="PermissionGrantPermissionRationaleContent" />
- <item type="style" name="PermissionGrantPermissionRationaleIcon" />
- <item type="style" name="PermissionGrantPermissionRationaleMessage" />
- <item type="style" name="PermissionGrantPermissionRationaleMoreInfoIcon" />
-
- <item type="color" name="permission_rationale_overview_background" />
+ <!-- END Used in V30 only -->
<!-- END PERMISSION GRANT DIALOG -->
@@ -526,4 +542,4 @@
</policy>
</overlayable>
-</resources> \ No newline at end of file
+</resources>
diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml
index 08d1b6b2d..f08d971cb 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -73,6 +73,10 @@
<!-- Title for the dialog button to allow access to all of a resource always. [CHAR LIMIT=60] -->
<string name="grant_dialog_button_always_allow_all">Always allow all</string>
+ <!-- Title for the dialog button to allow limited access. [CHAR LIMIT=60] -->
+ <string name="grant_dialog_button_allow_limited_access">Allow limited access</string>
+ <!-- TODO: b/310657469 add app_permission version of this string -->
+
<!-- Title for the dialog button to allow access to select photos to be shared. [CHAR LIMIT=60] -->
<string name="grant_dialog_button_allow_selected_photos">Select photos and videos</string>
@@ -164,6 +168,9 @@
<!-- Title for the page of manage unused apps [CHAR LIMIT=30] -->
<string name="unused_apps">Unused apps</string>
+ <!-- Description for the button that lets users change which photos an app has access to [CHAR LIMIT=none] -->
+ <string name="edit_photos_description">Edit selected photos for this app</string>
+
<!-- Label when there are no unused apps [CHAR LIMIT=30] -->
<string name="no_unused_apps">No unused apps</string>
diff --git a/PermissionController/res/values/styles.xml b/PermissionController/res/values/styles.xml
index 11b9ebec1..4f12ef7c7 100644
--- a/PermissionController/res/values/styles.xml
+++ b/PermissionController/res/values/styles.xml
@@ -195,6 +195,8 @@
<item name="android:paddingEnd">16dp</item>
<item name="android:orientation">horizontal</item>
<item name="android:background">@drawable/grant_dialog_permission_rationale_background</item>
+ <item name="android:minWidth">48dp</item>
+ <item name="android:minHeight">48dp</item>
</style>
<style name="PermissionGrantPermissionRationaleIcon">
@@ -233,16 +235,16 @@
parent="@style/PermissionGrantButton"></style>
<style name="PermissionGrantButtonAllowOneTime"
parent="@style/PermissionGrantButton"></style>
- <style name="PermissionGrantButtonAllowAll"
- parent="@style/PermissionGrantButton"></style>
<style name="PermissionGrantButtonAllowSelected"
parent="@style/PermissionGrantButton"></style>
- <style name="PermissionGrantButtonDontAllowMore"
+ <style name="PermissionGrantButtonAllowAll"
parent="@style/PermissionGrantButton"></style>
<style name="PermissionGrantButtonDeny"
parent="@style/PermissionGrantButton"></style>
<style name="PermissionGrantButtonNoUpgrade"
parent="@style/PermissionGrantButton"></style>
+ <style name="PermissionGrantButtonDontAllowMore"
+ parent="@style/PermissionGrantButton"></style>
<style name="PermissionGrantButtonAllowMaterial3"
parent="@style/PermissionGrantButton"></style>
@@ -256,10 +258,6 @@
parent="@style/PermissionGrantButton"></style>
<style name="PermissionGrantButtonAllowAllMaterial3"
parent="@style/PermissionGrantButton"></style>
- <style name="PermissionGrantButtonAllowSelectedPhotosMaterial3"
- parent="@style/PermissionGrantButton"></style>
- <style name="PermissionGrantButtonDontAllowMorePhotosMaterial3"
- parent="@style/PermissionGrantButton"></style>
<!-- END PERMISSION GRANT DIALOG -->
@@ -1040,6 +1038,7 @@
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginStart">16dp</item>
+ <item name="android:background">@null</item>
</style>
<!-- END REQUEST ROLE DIALOG ITEM -->
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index 2f8f5a291..51e0a29e5 100644
--- a/PermissionController/res/xml/roles.xml
+++ b/PermissionController/res/xml/roles.xml
@@ -140,6 +140,8 @@
minSdkVersion="33" />
<permission name="android.permission.EXECUTE_APP_ACTION"
minSdkVersion="34" />
+ <permission name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
+ minSdkVersion="35" optionalMinSdkVersion="34" />
</permissions>
<app-op-permissions>
<app-op-permission name="android.permission.SYSTEM_ALERT_WINDOW" />
diff --git a/PermissionController/res/xml/safety_center_dashboard.xml b/PermissionController/res/xml/safety_center_dashboard.xml
index 216ef318e..e3951ca83 100644
--- a/PermissionController/res/xml/safety_center_dashboard.xml
+++ b/PermissionController/res/xml/safety_center_dashboard.xml
@@ -29,6 +29,11 @@
android:layout="@layout/preference_category_no_label"
app:selectable="false" />
+ <!-- TODO: b/291574390 - Move this to the issue drawer or status card view instead of having a
+ separate preference just for this margin/padding -->
+ <com.android.permissioncontroller.safetycenter.ui.EntriesTopPaddingPreference
+ android:key="entries_top_padding" />
+
<com.android.permissioncontroller.safetycenter.ui.ComparablePreferenceCategory
android:key="entries_group"
android:title="@string/safety_center_entries_category_title"
@@ -36,7 +41,7 @@
<com.android.permissioncontroller.safetycenter.ui.ComparablePreferenceCategory
android:key="static_entries_group"
- android:layout="@layout/spaced_preference_category_no_label"
+ android:layout="@layout/preference_category_no_label"
app:selectable="false" />
<com.android.permissioncontroller.safetycenter.ui.SpacerPreference
diff --git a/PermissionController/role-controller/java/com/android/role/controller/compat/AppOpsManagerCompat.java b/PermissionController/role-controller/java/com/android/role/controller/compat/AppOpsManagerCompat.java
new file mode 100644
index 000000000..8e5a768df
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/compat/AppOpsManagerCompat.java
@@ -0,0 +1,48 @@
+/*
+ * 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.role.controller.compat;
+
+import android.app.AppOpsManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import java.util.Objects;
+
+/** Helper for accessing features in {@link AppOpsManager}. */
+public class AppOpsManagerCompat {
+
+ private AppOpsManagerCompat() {}
+
+ /**
+ * @see AppOpsManager#permissionToOp().
+ */
+ @Nullable
+ public static String permissionToOp(@NonNull String permission) {
+ if (!SdkLevel.isAtLeastV()) {
+ // On Android U and below, PACKAGE_USAGE_STATUS is missing from the mapping
+ // in the framework.
+ if (Objects.equals(permission, android.Manifest.permission.PACKAGE_USAGE_STATS)) {
+ return AppOpsManager.OPSTR_GET_USAGE_STATS;
+ }
+ }
+ return AppOpsManager.permissionToOp(permission);
+ }
+} \ No newline at end of file
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/AppOpPermissions.java b/PermissionController/role-controller/java/com/android/role/controller/model/AppOpPermissions.java
index f3f9b321e..29939a1a5 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/AppOpPermissions.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/AppOpPermissions.java
@@ -24,6 +24,7 @@ import android.os.Build;
import androidx.annotation.NonNull;
+import com.android.role.controller.compat.AppOpsManagerCompat;
import com.android.role.controller.util.ArrayUtils;
import com.android.role.controller.util.PackageUtils;
@@ -55,7 +56,7 @@ public class AppOpPermissions {
if (!ArrayUtils.contains(packageInfo.requestedPermissions, appOpPermission)) {
return false;
}
- String appOp = AppOpsManager.permissionToOp(appOpPermission);
+ String appOp = AppOpsManagerCompat.permissionToOp(appOpPermission);
if (!overrideNonDefaultMode) {
Integer currentMode = Permissions.getAppOpMode(packageName, appOp, context);
if (currentMode != null && currentMode != Permissions.getDefaultAppOpMode(appOp)) {
@@ -96,6 +97,7 @@ public class AppOpPermissions {
case AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS:
case AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW:
case AppOpsManager.OPSTR_WRITE_SETTINGS:
+ case AppOpsManager.OPSTR_GET_USAGE_STATS:
case AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES:
case AppOpsManager.OPSTR_START_FOREGROUND:
// This isn't an API but we are deprecating it soon anyway.
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java b/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java
index 0c4a14574..df44023c5 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java
@@ -16,6 +16,9 @@
package com.android.role.controller.model;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
import android.os.Build;
import androidx.annotation.NonNull;
@@ -40,9 +43,15 @@ public class Permission {
*/
private final int mMinSdkVersion;
- public Permission(@NonNull String name, int minSdkVersion) {
+ /**
+ * The minimum SDK version for this permission to be optionally granted (when it is grantable).
+ */
+ private final int mOptionalMinSdkVersion;
+
+ public Permission(@NonNull String name, int minSdkVersion, int optionalMinSdkVersion) {
mName = name;
mMinSdkVersion = minSdkVersion;
+ mOptionalMinSdkVersion = optionalMinSdkVersion;
}
@NonNull
@@ -54,18 +63,52 @@ public class Permission {
return mMinSdkVersion;
}
+ public int getOptionalMinSdkVersion() {
+ return mOptionalMinSdkVersion;
+ }
+
/**
* Check whether this permission is available.
*
* @return whether this permission is available
*/
- public boolean isAvailable() {
- // Workaround to match the value 34+ for U+ in roles.xml before SDK finalization.
- if (mMinSdkVersion >= 34) {
- return SdkLevel.isAtLeastU();
- } else {
- return Build.VERSION.SDK_INT >= mMinSdkVersion;
+ public boolean isAvailable(@NonNull Context context) {
+ if (Build.VERSION.SDK_INT >= mMinSdkVersion
+ // Workaround to match the value 34 for U in roles.xml before SDK finalization.
+ || (mMinSdkVersion == 34 && SdkLevel.isAtLeastU())) {
+ return true;
+ }
+ if (Build.VERSION.SDK_INT >= mOptionalMinSdkVersion) {
+ PackageManager packageManager = context.getPackageManager();
+ PermissionInfo permissionInfo;
+ try {
+ permissionInfo = packageManager.getPermissionInfo(mName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ return permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS
+ || (permissionInfo.getProtectionFlags() & PermissionInfo.PROTECTION_FLAG_ROLE)
+ == PermissionInfo.PROTECTION_FLAG_ROLE
+ || (permissionInfo.getProtectionFlags() & PermissionInfo.PROTECTION_FLAG_APPOP)
+ == PermissionInfo.PROTECTION_FLAG_APPOP;
+ }
+ return false;
+ }
+
+ /**
+ * Return a new permission with the specified SDK versions, or this permission if it already has
+ * the same SDK versions.
+ *
+ * @param minSdkVersion the minimum SDK version
+ * @param optionalMinSdkVersion the optional minimum SDK version
+ * @return a permission with the specified SDK versions
+ */
+ @NonNull
+ public Permission withSdkVersions(int minSdkVersion, int optionalMinSdkVersion) {
+ if (mMinSdkVersion == minSdkVersion && mOptionalMinSdkVersion == optionalMinSdkVersion) {
+ return this;
}
+ return new Permission(mName, minSdkVersion, optionalMinSdkVersion);
}
@Override
@@ -73,6 +116,7 @@ public class Permission {
return "Permission{"
+ "mName='" + mName + '\''
+ ", mMinSdkVersion=" + mMinSdkVersion
+ + ", mOptionalMinSdkVersion=" + mOptionalMinSdkVersion
+ '}';
}
@@ -86,11 +130,12 @@ public class Permission {
}
Permission that = (Permission) object;
return mMinSdkVersion == that.mMinSdkVersion
+ && mOptionalMinSdkVersion == that.mOptionalMinSdkVersion
&& mName.equals(that.mName);
}
@Override
public int hashCode() {
- return Objects.hash(mName, mMinSdkVersion);
+ return Objects.hash(mName, mMinSdkVersion, mOptionalMinSdkVersion);
}
}
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 83e3b1932..ca8d01ab0 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
@@ -61,16 +61,18 @@ public class Permissions {
* Filter a list of permissions based on their SDK versions.
*
* @param permissions the list of permissions
+ * @param context the {@code Context} to retrieve system services
*
* @return the filtered list of permission names.
*/
@NonNull
- public static List<String> filterBySdkVersion(@NonNull List<Permission> permissions) {
+ public static List<String> filterBySdkVersion(@NonNull List<Permission> permissions,
+ @NonNull Context context) {
List<String> permissionNames = new ArrayList<>();
int permissionsSize = permissions.size();
for (int i = 0; i < permissionsSize; i++) {
Permission permission = permissions.get(i);
- if (!permission.isAvailable()) {
+ if (!permission.isAvailable(context)) {
continue;
}
permissionNames.add(permission.getName());
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 aa6cba169..521d792d9 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
@@ -392,13 +392,10 @@ public class Role {
* @return whether this role is available based on SDK version
*/
boolean isAvailableBySdkVersion() {
- // Workaround to match the value 34+ for U+ in roles.xml before SDK finalization.
- if (mMinSdkVersion >= 34) {
- return SdkLevel.isAtLeastU();
- } else {
- return Build.VERSION.SDK_INT >= mMinSdkVersion
- && Build.VERSION.SDK_INT <= mMaxSdkVersion;
- }
+ return (Build.VERSION.SDK_INT >= mMinSdkVersion
+ // Workaround to match the value 34 for U in roles.xml before SDK finalization.
+ || (mMinSdkVersion == 34 && SdkLevel.isAtLeastU()))
+ && Build.VERSION.SDK_INT <= mMaxSdkVersion;
}
/**
@@ -462,7 +459,7 @@ public class Role {
} else {
List<String> packageNames = new ArrayList<>();
for (String defaultHolder : defaultHolders.split(DEFAULT_HOLDER_SEPARATOR)) {
- String packageName = getQualifiedDefaultHolderPackageName(defaultHolders, context);
+ String packageName = getQualifiedDefaultHolderPackageName(defaultHolder, context);
if (packageName != null) {
packageNames.add(packageName);
}
@@ -783,11 +780,12 @@ public class Role {
public void grant(@NonNull String packageName, boolean dontKillApp,
boolean overrideUser, @NonNull Context context) {
boolean permissionOrAppOpChanged = Permissions.grant(packageName,
- Permissions.filterBySdkVersion(mPermissions),
+ Permissions.filterBySdkVersion(mPermissions, context),
SdkLevel.isAtLeastS() ? !mSystemOnly : true, overrideUser, true, false, false,
context);
- List<String> appOpPermissionsToGrant = Permissions.filterBySdkVersion(mAppOpPermissions);
+ List<String> appOpPermissionsToGrant =
+ Permissions.filterBySdkVersion(mAppOpPermissions, context);
int appOpPermissionsSize = appOpPermissionsToGrant.size();
for (int i = 0; i < appOpPermissionsSize; i++) {
String appOpPermission = appOpPermissionsToGrant.get(i);
@@ -830,24 +828,26 @@ public class Role {
List<String> otherRoleNames = roleManager.getHeldRolesFromController(packageName);
otherRoleNames.remove(mName);
- List<String> permissionsToRevoke = Permissions.filterBySdkVersion(mPermissions);
+ List<String> permissionsToRevoke = Permissions.filterBySdkVersion(mPermissions, context);
ArrayMap<String, Role> roles = Roles.get(context);
int otherRoleNamesSize = otherRoleNames.size();
for (int i = 0; i < otherRoleNamesSize; i++) {
String roleName = otherRoleNames.get(i);
Role role = roles.get(roleName);
- permissionsToRevoke.removeAll(Permissions.filterBySdkVersion(role.mPermissions));
+ permissionsToRevoke.removeAll(
+ Permissions.filterBySdkVersion(role.mPermissions, context));
}
boolean permissionOrAppOpChanged = Permissions.revoke(packageName, permissionsToRevoke,
true, false, overrideSystemFixedPermissions, context);
- List<String> appOpPermissionsToRevoke = Permissions.filterBySdkVersion(mAppOpPermissions);
+ List<String> appOpPermissionsToRevoke =
+ Permissions.filterBySdkVersion(mAppOpPermissions, context);
for (int i = 0; i < otherRoleNamesSize; i++) {
String roleName = otherRoleNames.get(i);
Role role = roles.get(roleName);
appOpPermissionsToRevoke.removeAll(
- Permissions.filterBySdkVersion(role.mAppOpPermissions));
+ Permissions.filterBySdkVersion(role.mAppOpPermissions, context));
}
int appOpPermissionsSize = appOpPermissionsToRevoke.size();
for (int i = 0; i < appOpPermissionsSize; i++) {
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 23299419e..31c05d217 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
@@ -102,6 +102,7 @@ public class RoleParser {
private static final String ATTRIBUTE_VISIBLE = "visible";
private static final String ATTRIBUTE_FLAGS = "flags";
private static final String ATTRIBUTE_MIN_TARGET_SDK_VERSION = "minTargetSdkVersion";
+ private static final String ATTRIBUTE_OPTIONAL_MIN_SDK_VERSION = "optionalMinSdkVersion";
private static final String ATTRIBUTE_PERMISSION = "permission";
private static final String ATTRIBUTE_PROHIBITED = "prohibited";
private static final String ATTRIBUTE_VALUE = "value";
@@ -252,6 +253,11 @@ public class RoleParser {
return null;
}
+ int minSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MIN_SDK_VERSION,
+ Build.VERSION_CODES.BASE);
+ int optionalMinSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_OPTIONAL_MIN_SDK_VERSION,
+ minSdkVersion);
+
List<Permission> permissions = new ArrayList<>();
int type;
@@ -269,6 +275,11 @@ public class RoleParser {
if (permission == null) {
continue;
}
+ int mergedMinSdkVersion = Math.max(permission.getMinSdkVersion(), minSdkVersion);
+ int mergedOptionalMinSdkVersion = Math.max(permission.getOptionalMinSdkVersion(),
+ optionalMinSdkVersion);
+ permission = permission.withSdkVersions(mergedMinSdkVersion,
+ mergedOptionalMinSdkVersion);
validateNoDuplicateElement(permission, permissions, "permission");
permissions.add(permission);
} else {
@@ -290,7 +301,9 @@ public class RoleParser {
}
int minSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MIN_SDK_VERSION,
Build.VERSION_CODES.BASE);
- return new Permission(name, minSdkVersion);
+ int optionalMinSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_OPTIONAL_MIN_SDK_VERSION,
+ minSdkVersion);
+ return new Permission(name, minSdkVersion, optionalMinSdkVersion);
}
@Nullable
@@ -738,13 +751,29 @@ public class RoleParser {
if (permissionSetName == null) {
continue;
}
- if (!permissionSets.containsKey(permissionSetName)) {
+ PermissionSet permissionSet = permissionSets.get(permissionSetName);
+ if (permissionSet == null) {
throwOrLogMessage("Unknown permission set:" + permissionSetName);
continue;
}
- PermissionSet permissionSet = permissionSets.get(permissionSetName);
- // We do allow intersection between permission sets.
- permissions.addAll(permissionSet.getPermissions());
+ int minSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MIN_SDK_VERSION,
+ Build.VERSION_CODES.BASE);
+ int optionalMinSdkVersion = getAttributeIntValue(parser,
+ ATTRIBUTE_OPTIONAL_MIN_SDK_VERSION, minSdkVersion);
+ List<Permission> permissionsInSet = permissionSet.getPermissions();
+ int permissionsInSetSize = permissionsInSet.size();
+ for (int permissionsInSetIndex = 0;
+ permissionsInSetIndex < permissionsInSetSize; permissionsInSetIndex++) {
+ Permission permission = permissionsInSet.get(permissionsInSetIndex);
+ int mergedMinSdkVersion =
+ Math.max(permission.getMinSdkVersion(), minSdkVersion);
+ int mergedOptionalMinSdkVersion = Math.max(
+ permission.getOptionalMinSdkVersion(), optionalMinSdkVersion);
+ permission = permission.withSdkVersions(mergedMinSdkVersion,
+ mergedOptionalMinSdkVersion);
+ // We do allow intersection between permission sets.
+ permissions.add(permission);
+ }
break;
}
case TAG_PERMISSION: {
@@ -1110,7 +1139,7 @@ public class RoleParser {
}
private void validatePermission(@NonNull Permission permission) {
- if (!permission.isAvailable()) {
+ if (!permission.isAvailable(mContext)) {
return;
}
validatePermission(permission.getName(), true);
@@ -1146,7 +1175,7 @@ public class RoleParser {
}
private void validateAppOpPermission(@NonNull Permission appOpPermission) {
- if (!appOpPermission.isAvailable()) {
+ if (!appOpPermission.isAvailable(mContext)) {
return;
}
validateAppOpPermission(appOpPermission.getName());
diff --git a/PermissionController/src/com/android/permissioncontroller/DumpableLog.kt b/PermissionController/src/com/android/permissioncontroller/DumpableLog.kt
index bbce5bf5c..56682d018 100644
--- a/PermissionController/src/com/android/permissioncontroller/DumpableLog.kt
+++ b/PermissionController/src/com/android/permissioncontroller/DumpableLog.kt
@@ -20,9 +20,7 @@ import android.util.Log
import com.android.permissioncontroller.Constants.LOGS_TO_DUMP_FILE
import java.io.File
-/**
- * Like {@link Log} but stores the logs in a file which can later be dumped via {@link #dump}
- */
+/** Like {@link Log} but stores the logs in a file which can later be dumped via {@link #dump} */
object DumpableLog {
private const val MAX_FILE_SIZE = 64 * 1024
@@ -33,41 +31,31 @@ object DumpableLog {
file.createNewFile()
}
- /**
- * Equivalent to {@link Log.v}
- */
+ /** Equivalent to {@link Log.v} */
fun v(tag: String, message: String, exception: Throwable? = null) {
Log.v(tag, message, exception)
addLogToDump("v", tag, message, exception)
}
- /**
- * Equivalent to {@link Log.d}
- */
+ /** Equivalent to {@link Log.d} */
fun d(tag: String, message: String, exception: Throwable? = null) {
Log.d(tag, message, exception)
addLogToDump("d", tag, message, exception)
}
- /**
- * Equivalent to {@link Log.i}
- */
+ /** Equivalent to {@link Log.i} */
fun i(tag: String, message: String, exception: Throwable? = null) {
Log.i(tag, message, exception)
addLogToDump("i", tag, message, exception)
}
- /**
- * Equivalent to {@link Log.w}
- */
+ /** Equivalent to {@link Log.w} */
fun w(tag: String, message: String, exception: Throwable? = null) {
Log.w(tag, message, exception)
addLogToDump("w", tag, message, exception)
}
- /**
- * Equivalent to {@link Log.e}
- */
+ /** Equivalent to {@link Log.e} */
fun e(tag: String, message: String, exception: Throwable? = null) {
Log.e(tag, message, exception)
addLogToDump("e", tag, message, exception)
@@ -83,17 +71,17 @@ object DumpableLog {
dump.subList(dump.size / 2, dump.size).forEach { file.appendText(it + "\n") }
}
- file.appendText("${System.currentTimeMillis()} $tag:$level $message " +
- "${exception?.let { it.message + Log.getStackTraceString(it) } ?: ""}\n")
+ file.appendText(
+ "${System.currentTimeMillis()} $tag:$level $message " +
+ "${exception?.let { it.message + Log.getStackTraceString(it) } ?: ""}\n"
+ )
}
}
- /**
- * @return the previously logged entries
- */
+ /** @return the previously logged entries */
suspend fun get(): List<String> {
synchronized(lock) {
return file.readLines()
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt
index b62f8d721..7a2d5d570 100644
--- a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt
+++ b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt
@@ -55,9 +55,7 @@ import java.util.Random
*/
class DrivingDecisionReminderService : Service() {
- /**
- * Information needed to show a reminder about a permission decisions.
- */
+ /** Information needed to show a reminder about a permission decisions. */
data class PermissionReminder(
val packageName: String,
val permissionGroup: String,
@@ -109,22 +107,31 @@ class DrivingDecisionReminderService : Service() {
packageName: String,
permGroupName: String
) {
- Car.createCar(
- context,
- /* handler= */ null,
- Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT) { car: Car, ready: Boolean ->
+ Car.createCar(context, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT) {
+ car: Car,
+ ready: Boolean ->
// just give up if we can't connect to the car
if (ready) {
val restrictionsManager = car.getCarManager(
- Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
- if (restrictionsManager.currentCarUxRestrictions
- .isRequiresDistractionOptimization) {
- context.startService(
- createIntent(
- context,
- packageName,
- permGroupName,
- Process.myUserHandle()))
+ Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager?
+ if (restrictionsManager != null) {
+ val currentCarUxRestrictions = restrictionsManager.currentCarUxRestrictions
+ if (currentCarUxRestrictions != null) {
+ if (currentCarUxRestrictions.isRequiresDistractionOptimization) {
+ context.startService(
+ createIntent(
+ context,
+ packageName,
+ permGroupName,
+ Process.myUserHandle()))
+ }
+ } else {
+ DumpableLog.e(LOG_TAG,
+ "Reminder service not created because CarUxRestrictions is null")
+ }
+ } else {
+ DumpableLog.e(LOG_TAG,
+ "Reminder service not created because CarUxRestrictionsManager is null")
}
}
car.disconnect()
@@ -156,28 +163,32 @@ class DrivingDecisionReminderService : Service() {
}
private fun scheduleNotificationForUnrestrictedState() {
- Car.createCar(this, null,
- Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT
- ) { createdCar: Car?, ready: Boolean ->
+ Car.createCar(this, null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT) {
+ createdCar: Car?,
+ ready: Boolean ->
car = createdCar
if (ready) {
onCarReady()
} else {
- DumpableLog.w(LOG_TAG,
- "Car service disconnected, no notification will be scheduled")
+ DumpableLog.w(
+ LOG_TAG,
+ "Car service disconnected, no notification will be scheduled"
+ )
stopSelf()
}
}
}
private fun onCarReady() {
- carUxRestrictionsManager = car?.getCarManager(
- Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
+ carUxRestrictionsManager =
+ car?.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
DumpableLog.d(LOG_TAG, "Registering UX restriction listener")
carUxRestrictionsManager?.registerListener { restrictions ->
if (!restrictions.isRequiresDistractionOptimization) {
- DumpableLog.d(LOG_TAG,
- "UX restrictions no longer required - showing reminder notification")
+ DumpableLog.d(
+ LOG_TAG,
+ "UX restrictions no longer required - showing reminder notification"
+ )
showRecentGrantDecisionsPostDriveNotification()
stopSelf()
}
@@ -185,10 +196,12 @@ class DrivingDecisionReminderService : Service() {
}
private fun parseStartIntent(intent: Intent?): PermissionReminder? {
- if (intent == null ||
- !intent.hasExtra(EXTRA_PACKAGE_NAME) ||
- !intent.hasExtra(EXTRA_PERMISSION_GROUP) ||
- !intent.hasExtra(EXTRA_USER)) {
+ if (
+ intent == null ||
+ !intent.hasExtra(EXTRA_PACKAGE_NAME) ||
+ !intent.hasExtra(EXTRA_PERMISSION_GROUP) ||
+ !intent.hasExtra(EXTRA_USER)
+ ) {
DumpableLog.e(LOG_TAG, "Missing extras from intent $intent")
return null
}
@@ -202,21 +215,25 @@ class DrivingDecisionReminderService : Service() {
fun showRecentGrantDecisionsPostDriveNotification() {
val notificationManager = getSystemService(NotificationManager::class.java)!!
- val permissionReminderChannel = NotificationChannel(
- Constants.PERMISSION_REMINDER_CHANNEL_ID, getString(R.string.permission_reminders),
- NotificationManager.IMPORTANCE_HIGH)
+ val permissionReminderChannel =
+ NotificationChannel(
+ Constants.PERMISSION_REMINDER_CHANNEL_ID,
+ getString(R.string.permission_reminders),
+ NotificationManager.IMPORTANCE_HIGH
+ )
notificationManager.createNotificationChannel(permissionReminderChannel)
- notificationManager.notify(DrivingDecisionReminderService::class.java.simpleName,
+ notificationManager.notify(
+ DrivingDecisionReminderService::class.java.simpleName,
Constants.PERMISSION_DECISION_REMINDER_NOTIFICATION_ID,
- createNotification(createNotificationTitle(), createNotificationContent()))
+ createNotification(createNotificationTitle(), createNotificationContent())
+ )
logNotificationPresented()
}
private fun createNotificationTitle(): String {
- return applicationContext
- .getString(R.string.post_drive_permission_decision_reminder_title)
+ return applicationContext.getString(R.string.post_drive_permission_decision_reminder_title)
}
@VisibleForTesting
@@ -224,76 +241,106 @@ class DrivingDecisionReminderService : Service() {
val packageLabels: MutableList<String> = mutableListOf()
val permissionGroupNames: MutableList<String> = mutableListOf()
for (permissionReminder in permissionReminders) {
- val packageLabel = getLabelForPackage(permissionReminder.packageName,
- permissionReminder.user)
- val permissionGroupLabel = getPermGroupLabel(applicationContext,
- permissionReminder.permissionGroup).toString()
+ val packageLabel =
+ getLabelForPackage(permissionReminder.packageName, permissionReminder.user)
+ val permissionGroupLabel =
+ getPermGroupLabel(applicationContext, permissionReminder.permissionGroup).toString()
packageLabels.add(packageLabel)
permissionGroupNames.add(permissionGroupLabel)
}
val packageLabelsDistinct = packageLabels.distinct()
val permissionGroupNamesDistinct = permissionGroupNames.distinct()
return if (packageLabelsDistinct.size > 1) {
- StringUtils.getIcuPluralsString(applicationContext,
+ StringUtils.getIcuPluralsString(
+ applicationContext,
R.string.post_drive_permission_decision_reminder_summary_multi_apps,
- (packageLabels.size - 1), packageLabelsDistinct[0])
+ (packageLabels.size - 1),
+ packageLabelsDistinct[0]
+ )
} else if (permissionGroupNamesDistinct.size == 2) {
getString(
R.string.post_drive_permission_decision_reminder_summary_1_app_2_permissions,
- packageLabelsDistinct[0], permissionGroupNamesDistinct[0],
- permissionGroupNamesDistinct[1])
+ packageLabelsDistinct[0],
+ permissionGroupNamesDistinct[0],
+ permissionGroupNamesDistinct[1]
+ )
} else if (permissionGroupNamesDistinct.size > 2) {
getString(
R.string.post_drive_permission_decision_reminder_summary_1_app_multi_permission,
- permissionGroupNamesDistinct.size, packageLabelsDistinct[0])
+ permissionGroupNamesDistinct.size,
+ packageLabelsDistinct[0]
+ )
} else {
getString(
R.string.post_drive_permission_decision_reminder_summary_1_app_1_permission,
- packageLabelsDistinct[0], permissionGroupNamesDistinct[0])
+ packageLabelsDistinct[0],
+ permissionGroupNamesDistinct[0]
+ )
}
}
@VisibleForTesting
fun getLabelForPackage(packageName: String, user: UserHandle): String {
- return BidiFormatter.getInstance().unicodeWrap(
- getPackageLabel(application, packageName, user))
+ return BidiFormatter.getInstance()
+ .unicodeWrap(getPackageLabel(application, packageName, user))
}
private fun createNotification(title: String, body: String): Notification {
- val clickIntent = Intent(PermissionManager.ACTION_REVIEW_PERMISSION_DECISIONS).apply {
- putExtra(Constants.EXTRA_SESSION_ID, sessionId)
- putExtra(AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE,
- AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE_NOTIFICATION)
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
- }
- val pendingIntent = PendingIntent.getActivity(this, 0, clickIntent,
- PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT or
- PendingIntent.FLAG_IMMUTABLE)
+ val clickIntent =
+ Intent(PermissionManager.ACTION_REVIEW_PERMISSION_DECISIONS).apply {
+ putExtra(Constants.EXTRA_SESSION_ID, sessionId)
+ putExtra(
+ AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE,
+ AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE_NOTIFICATION
+ )
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ val pendingIntent =
+ PendingIntent.getActivity(
+ this,
+ 0,
+ clickIntent,
+ PendingIntent.FLAG_ONE_SHOT or
+ PendingIntent.FLAG_UPDATE_CURRENT or
+ PendingIntent.FLAG_IMMUTABLE
+ )
- val settingsDrawable = KotlinUtils.getBadgedPackageIcon(
- application,
- getSettingsPackageName(applicationContext.packageManager),
- permissionReminders.first().user)
- val settingsIcon = if (settingsDrawable != null) {
- KotlinUtils.convertToBitmap(settingsDrawable)
- } else {
- null
- }
+ val settingsDrawable =
+ KotlinUtils.getBadgedPackageIcon(
+ application,
+ getSettingsPackageName(applicationContext.packageManager),
+ permissionReminders.first().user
+ )
+ val settingsIcon =
+ if (settingsDrawable != null) {
+ KotlinUtils.convertToBitmap(settingsDrawable)
+ } else {
+ null
+ }
- val b = Notification.Builder(this, Constants.PERMISSION_REMINDER_CHANNEL_ID)
- .setContentTitle(title)
- .setContentText(body)
- .setSmallIcon(R.drawable.ic_settings_24dp)
- .setLargeIcon(settingsIcon)
- .setColor(getColor(android.R.color.system_notification_accent_color))
- .setAutoCancel(true)
- .setContentIntent(pendingIntent)
- .addExtras(Bundle().apply {
- putBoolean("com.android.car.notification.EXTRA_USE_LAUNCHER_ICON", false)
- })
- // Auto doesn't show icons for actions
- .addAction(Notification.Action.Builder(/* icon= */ null,
- getString(R.string.go_to_settings), pendingIntent).build())
+ val b =
+ Notification.Builder(this, Constants.PERMISSION_REMINDER_CHANNEL_ID)
+ .setContentTitle(title)
+ .setContentText(body)
+ .setSmallIcon(R.drawable.ic_settings_24dp)
+ .setLargeIcon(settingsIcon)
+ .setColor(getColor(android.R.color.system_notification_accent_color))
+ .setAutoCancel(true)
+ .setContentIntent(pendingIntent)
+ .addExtras(
+ Bundle().apply {
+ putBoolean("com.android.car.notification.EXTRA_USE_LAUNCHER_ICON", false)
+ }
+ )
+ // Auto doesn't show icons for actions
+ .addAction(
+ Notification.Action.Builder(
+ /* icon= */ null,
+ getString(R.string.go_to_settings),
+ pendingIntent
+ )
+ .build()
+ )
Utils.getSettingsLabelForNotifications(applicationContext.packageManager)?.let { label ->
val extras = Bundle()
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, label.toString())
@@ -311,6 +358,8 @@ class DrivingDecisionReminderService : Service() {
private fun logNotificationPresented() {
PermissionControllerStatsLog.write(
PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED,
- sessionId, PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_PRESENTED)
+ sessionId,
+ PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_PRESENTED
+ )
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
index 1f6b5272a..6aa5d0f12 100644
--- a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
+++ b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
@@ -99,6 +99,7 @@ import com.android.permissioncontroller.permission.data.get
import com.android.permissioncontroller.permission.data.getUnusedPackages
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
import com.android.permissioncontroller.permission.service.revokeAppPermissions
+import com.android.permissioncontroller.permission.utils.IPC
import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.android.permissioncontroller.permission.utils.StringUtils
import com.android.permissioncontroller.permission.utils.Utils
@@ -113,26 +114,31 @@ import kotlinx.coroutines.launch
private const val LOG_TAG = "HibernationPolicy"
const val DEBUG_OVERRIDE_THRESHOLDS = false
-// TODO eugenesusla: temporarily enabled for extra logs during dogfooding
-const val DEBUG_HIBERNATION_POLICY = true || DEBUG_OVERRIDE_THRESHOLDS
+const val DEBUG_HIBERNATION_POLICY = false
private var SKIP_NEXT_RUN = false
private val DEFAULT_UNUSED_THRESHOLD_MS = TimeUnit.DAYS.toMillis(90)
-fun getUnusedThresholdMs() = when {
- DEBUG_OVERRIDE_THRESHOLDS -> TimeUnit.SECONDS.toMillis(1)
- else -> DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
- Utils.PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS,
- DEFAULT_UNUSED_THRESHOLD_MS)
-}
+fun getUnusedThresholdMs() =
+ when {
+ DEBUG_OVERRIDE_THRESHOLDS -> TimeUnit.SECONDS.toMillis(1)
+ else ->
+ DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_PERMISSIONS,
+ Utils.PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS,
+ DEFAULT_UNUSED_THRESHOLD_MS
+ )
+ }
private val DEFAULT_CHECK_FREQUENCY_MS = TimeUnit.DAYS.toMillis(15)
-private fun getCheckFrequencyMs() = DeviceConfig.getLong(
- DeviceConfig.NAMESPACE_PERMISSIONS,
+private fun getCheckFrequencyMs() =
+ DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_PERMISSIONS,
Utils.PROPERTY_HIBERNATION_CHECK_FREQUENCY_MILLIS,
- DEFAULT_CHECK_FREQUENCY_MS)
+ DEFAULT_CHECK_FREQUENCY_MS
+ )
// Intentionally kept value of the key same as before because we want to continue reading value of
// this shared preference stored by previous versions of PermissionController
@@ -149,8 +155,11 @@ val ONE_DAY_MS = TimeUnit.DAYS.toMillis(1)
fun isHibernationEnabled(): Boolean {
return SdkLevel.isAtLeastS() &&
- DeviceConfig.getBoolean(NAMESPACE_APP_HIBERNATION, Utils.PROPERTY_APP_HIBERNATION_ENABLED,
- true /* defaultValue */)
+ DeviceConfig.getBoolean(
+ NAMESPACE_APP_HIBERNATION,
+ Utils.PROPERTY_APP_HIBERNATION_ENABLED,
+ true /* defaultValue */
+ )
}
/**
@@ -158,30 +167,33 @@ fun isHibernationEnabled(): Boolean {
* [isHibernationEnabled] is false.
*/
fun hibernationTargetsPreSApps(): Boolean {
- return DeviceConfig.getBoolean(NAMESPACE_APP_HIBERNATION,
+ return DeviceConfig.getBoolean(
+ NAMESPACE_APP_HIBERNATION,
Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS,
- false /* defaultValue */)
+ false /* defaultValue */
+ )
}
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
fun isSystemExemptFromHibernationEnabled(): Boolean {
- return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(NAMESPACE_APP_HIBERNATION,
+ return SdkLevel.isAtLeastU() &&
+ DeviceConfig.getBoolean(
+ NAMESPACE_APP_HIBERNATION,
Utils.PROPERTY_SYSTEM_EXEMPT_HIBERNATION_ENABLED,
- true /* defaultValue */)
+ true /* defaultValue */
+ )
}
-/**
- * Remove the unused apps notification.
- */
+/** Remove the unused apps notification. */
fun cancelUnusedAppsNotification(context: Context) {
- context.getSystemService(NotificationManager::class.java)!!.cancel(
- HibernationJobService::class.java.simpleName,
- Constants.UNUSED_APPS_NOTIFICATION_ID)
+ context
+ .getSystemService(NotificationManager::class.java)!!
+ .cancel(HibernationJobService::class.java.simpleName, Constants.UNUSED_APPS_NOTIFICATION_ID)
}
/**
- * Checks if we need to show the safety center card and sends the appropriate source data. If
- * the user has not reviewed the latest auto-revoked apps, we show the card. Otherwise, we ensure
+ * Checks if we need to show the safety center card and sends the appropriate source data. If the
+ * user has not reviewed the latest auto-revoked apps, we show the card. Otherwise, we ensure
* nothing is shown.
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@@ -193,36 +205,40 @@ fun rescanAndPushDataToSafetyCenter(
val safetyCenterManager: SafetyCenterManager =
context.getSystemService(SafetyCenterManager::class.java)!!
if (getUnusedAppsReviewNeeded(context)) {
- val seeUnusedAppsAction = Action.Builder(
- Constants.UNUSED_APPS_SAFETY_CENTER_SEE_UNUSED_APPS_ID,
- context.getString(R.string.unused_apps_safety_center_action_title),
- makeUnusedAppsIntent(context, sessionId))
- .build()
-
- val issue = SafetySourceIssue.Builder(
- Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID,
- context.getString(R.string.unused_apps_safety_center_card_title),
- context.getString(R.string.unused_apps_safety_center_card_content),
- SafetySourceData.SEVERITY_LEVEL_INFORMATION,
- Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID)
- .addAction(seeUnusedAppsAction)
- .setOnDismissPendingIntent(makeDismissIntent(context, sessionId))
- .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
- .build()
-
- val safetySourceData = SafetySourceData.Builder()
- .addIssue(issue)
- .build()
+ val seeUnusedAppsAction =
+ Action.Builder(
+ Constants.UNUSED_APPS_SAFETY_CENTER_SEE_UNUSED_APPS_ID,
+ context.getString(R.string.unused_apps_safety_center_action_title),
+ makeUnusedAppsIntent(context, sessionId)
+ )
+ .build()
+
+ val issue =
+ SafetySourceIssue.Builder(
+ Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID,
+ context.getString(R.string.unused_apps_safety_center_card_title),
+ context.getString(R.string.unused_apps_safety_center_card_content),
+ SafetySourceData.SEVERITY_LEVEL_INFORMATION,
+ Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID
+ )
+ .addAction(seeUnusedAppsAction)
+ .setOnDismissPendingIntent(makeDismissIntent(context, sessionId))
+ .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
+ .build()
+
+ val safetySourceData = SafetySourceData.Builder().addIssue(issue).build()
safetyCenterManager.setSafetySourceData(
Constants.UNUSED_APPS_SAFETY_CENTER_SOURCE_ID,
safetySourceData,
- safetyEvent)
+ safetyEvent
+ )
} else {
safetyCenterManager.setSafetySourceData(
Constants.UNUSED_APPS_SAFETY_CENTER_SOURCE_ID,
/* safetySourceData= */ null,
- safetyEvent)
+ safetyEvent
+ )
}
}
@@ -231,8 +247,10 @@ fun rescanAndPushDataToSafetyCenter(
*/
fun setUnusedAppsReviewNeeded(context: Context, needsReview: Boolean) {
val sharedPreferences = context.sharedPreferences
- if (sharedPreferences.contains(PREF_KEY_UNUSED_APPS_REVIEW) &&
- sharedPreferences.getBoolean(PREF_KEY_UNUSED_APPS_REVIEW, false) == needsReview) {
+ if (
+ sharedPreferences.contains(PREF_KEY_UNUSED_APPS_REVIEW) &&
+ sharedPreferences.getBoolean(PREF_KEY_UNUSED_APPS_REVIEW, false) == needsReview
+ ) {
return
}
sharedPreferences.edit().putBoolean(PREF_KEY_UNUSED_APPS_REVIEW, needsReview).apply()
@@ -245,10 +263,10 @@ private fun getUnusedAppsReviewNeeded(context: Context): Boolean {
/**
* Receiver of the following broadcasts:
* <ul>
- * <li> {@link Intent.ACTION_BOOT_COMPLETED}
- * <li> {@link #ACTION_SET_UP_HIBERNATION}
- * <li> {@link Intent.ACTION_TIME_CHANGED}
- * <li> {@link Intent.ACTION_TIMEZONE_CHANGED}
+ * <li> {@link Intent.ACTION_BOOT_COMPLETED}
+ * <li> {@link #ACTION_SET_UP_HIBERNATION}
+ * <li> {@link Intent.ACTION_TIME_CHANGED}
+ * <li> {@link Intent.ACTION_TIMEZONE_CHANGED}
* </ul>
*/
class HibernationBroadcastReceiver : BroadcastReceiver() {
@@ -257,9 +275,12 @@ class HibernationBroadcastReceiver : BroadcastReceiver() {
val action = intent.action
if (action == Intent.ACTION_BOOT_COMPLETED || action == ACTION_SET_UP_HIBERNATION) {
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG, "scheduleHibernationJob " +
- "with frequency ${getCheckFrequencyMs()}ms " +
- "and threshold ${getUnusedThresholdMs()}ms")
+ DumpableLog.i(
+ LOG_TAG,
+ "scheduleHibernationJob " +
+ "with frequency ${getCheckFrequencyMs()}ms " +
+ "and threshold ${getUnusedThresholdMs()}ms"
+ )
}
initStartTimeOfUnusedAppTracking(context.sharedPreferences)
@@ -268,32 +289,40 @@ class HibernationBroadcastReceiver : BroadcastReceiver() {
// primary user
if (isProfile(context)) {
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG,
- "user ${Process.myUserHandle().identifier} is a profile." +
- " Not running hibernation job.")
+ DumpableLog.i(
+ LOG_TAG,
+ "user ${Process.myUserHandle().identifier} is a profile." +
+ " Not running hibernation job."
+ )
}
return
} else if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG,
- "user ${Process.myUserHandle().identifier} is a profile" +
- "owner. Running hibernation job.")
+ DumpableLog.i(
+ LOG_TAG,
+ "user ${Process.myUserHandle().identifier} is a profile" +
+ "owner. Running hibernation job."
+ )
}
if (isNewJobScheduleRequired(context)) {
// periodic jobs normally run immediately, which is unnecessarily premature
SKIP_NEXT_RUN = true
- val jobInfo = JobInfo.Builder(
- Constants.HIBERNATION_JOB_ID,
- ComponentName(context, HibernationJobService::class.java))
- .setPeriodic(getCheckFrequencyMs())
- // persist this job across boots
- .setPersisted(true)
- .build()
- val status =
- context.getSystemService(JobScheduler::class.java)!!.schedule(jobInfo)
+ val jobInfo =
+ JobInfo.Builder(
+ Constants.HIBERNATION_JOB_ID,
+ ComponentName(context, HibernationJobService::class.java)
+ )
+ .setPeriodic(getCheckFrequencyMs())
+ // persist this job across boots
+ .setPersisted(true)
+ .build()
+ val status = context.getSystemService(JobScheduler::class.java)!!.schedule(jobInfo)
if (status != JobScheduler.RESULT_SUCCESS) {
- DumpableLog.e(LOG_TAG, "Could not schedule " +
- "${HibernationJobService::class.java.simpleName}: $status")
+ DumpableLog.e(
+ LOG_TAG,
+ "Could not schedule " +
+ "${HibernationJobService::class.java.simpleName}: $status"
+ )
}
}
}
@@ -318,8 +347,10 @@ class HibernationBroadcastReceiver : BroadcastReceiver() {
private fun isNewJobScheduleRequired(context: Context): Boolean {
// check if the job is already scheduled or needs a change
var scheduleNewJob = false
- val existingJob: JobInfo? = context.getSystemService(JobScheduler::class.java)!!
- .getPendingJob(Constants.HIBERNATION_JOB_ID)
+ val existingJob: JobInfo? =
+ context
+ .getSystemService(JobScheduler::class.java)!!
+ .getPendingJob(Constants.HIBERNATION_JOB_ID)
if (existingJob == null) {
if (DEBUG_HIBERNATION_POLICY) {
DumpableLog.i(LOG_TAG, "No existing job, scheduling a new one")
@@ -350,19 +381,24 @@ private suspend fun getAppsToHibernate(
val startTimeOfUnusedAppTracking = getStartTimeOfUnusedAppTracking(context.sharedPreferences)
val allPackagesByUser = AllPackageInfosLiveData.getInitializedValue(forceUpdate = true)
- val allPackagesByUserByUid = allPackagesByUser.mapValues { (_, pkgs) ->
- pkgs.groupBy { pkg -> pkg.uid }
- }
+ val allPackagesByUserByUid =
+ allPackagesByUser.mapValues { (_, pkgs) -> pkgs.groupBy { pkg -> pkg.uid } }
val unusedApps = allPackagesByUser.toMutableMap()
- val userStats = UsageStatsLiveData[getUnusedThresholdMs(),
- if (DEBUG_OVERRIDE_THRESHOLDS) INTERVAL_DAILY else INTERVAL_MONTHLY].getInitializedValue()
+ val userStats =
+ UsageStatsLiveData[
+ getUnusedThresholdMs(),
+ if (DEBUG_OVERRIDE_THRESHOLDS) INTERVAL_DAILY else INTERVAL_MONTHLY]
+ .getInitializedValue()
if (DEBUG_HIBERNATION_POLICY) {
for ((user, stats) in userStats) {
- DumpableLog.i(LOG_TAG, "Usage stats for user ${user.identifier}: " +
- stats.map { stat ->
- stat.packageName to Date(stat.lastTimePackageUsed())
- }.toMap())
+ DumpableLog.i(
+ LOG_TAG,
+ "Usage stats for user ${user.identifier}: " +
+ stats
+ .map { stat -> stat.packageName to Date(stat.lastTimePackageUsed()) }
+ .toMap()
+ )
}
}
for (user in unusedApps.keys.toList()) {
@@ -377,42 +413,52 @@ private suspend fun getAppsToHibernate(
for ((user, stats) in userStats) {
var unusedUserApps = unusedApps[user] ?: continue
- unusedUserApps = unusedUserApps.filter { packageInfo ->
- val pkgName = packageInfo.packageName
-
- val uidPackages = allPackagesByUserByUid[user]!![packageInfo.uid]
- ?.map { info -> info.packageName } ?: emptyList()
- if (pkgName !in uidPackages) {
- Log.wtf(LOG_TAG, "Package $pkgName not among packages for " +
- "its uid ${packageInfo.uid}: $uidPackages")
- }
- var lastTimePkgUsed: Long = stats.lastTimePackageUsed(uidPackages)
+ unusedUserApps =
+ unusedUserApps.filter { packageInfo ->
+ val pkgName = packageInfo.packageName
- // Limit by install time
- lastTimePkgUsed = Math.max(lastTimePkgUsed, packageInfo.firstInstallTime)
-
- // Limit by first boot time
- lastTimePkgUsed = Math.max(lastTimePkgUsed, startTimeOfUnusedAppTracking)
-
- // Handle cross-profile apps
- if (context.isPackageCrossProfile(pkgName)) {
- for ((otherUser, otherStats) in userStats) {
- if (otherUser == user) {
- continue
+ val uidPackages =
+ allPackagesByUserByUid[user]!![packageInfo.uid]?.map { info ->
+ info.packageName
+ }
+ ?: emptyList()
+ if (pkgName !in uidPackages) {
+ Log.wtf(
+ LOG_TAG,
+ "Package $pkgName not among packages for " +
+ "its uid ${packageInfo.uid}: $uidPackages"
+ )
+ }
+ var lastTimePkgUsed: Long = stats.lastTimePackageUsed(uidPackages)
+
+ // Limit by install time
+ lastTimePkgUsed = Math.max(lastTimePkgUsed, packageInfo.firstInstallTime)
+
+ // Limit by first boot time
+ lastTimePkgUsed = Math.max(lastTimePkgUsed, startTimeOfUnusedAppTracking)
+
+ // Handle cross-profile apps
+ if (context.isPackageCrossProfile(pkgName)) {
+ for ((otherUser, otherStats) in userStats) {
+ if (otherUser == user) {
+ continue
+ }
+ lastTimePkgUsed =
+ maxOf(lastTimePkgUsed, otherStats.lastTimePackageUsed(pkgName))
}
- lastTimePkgUsed =
- maxOf(lastTimePkgUsed, otherStats.lastTimePackageUsed(pkgName))
}
- }
- // Threshold check - whether app is unused
- now - lastTimePkgUsed > getUnusedThresholdMs()
- }
+ // Threshold check - whether app is unused
+ now - lastTimePkgUsed > getUnusedThresholdMs()
+ }
unusedApps[user] = unusedUserApps
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG, "Unused apps for user ${user.identifier}: " +
- "${unusedUserApps.map { it.packageName }}")
+ DumpableLog.i(
+ LOG_TAG,
+ "Unused apps for user ${user.identifier}: " +
+ "${unusedUserApps.map { it.packageName }}"
+ )
}
}
@@ -434,25 +480,29 @@ private suspend fun getAppsToHibernate(
}
val packageName = pkg.packageName
- val packageImportance = context
- .getSystemService(ActivityManager::class.java)!!
- .getPackageImportance(packageName)
+ val packageImportance =
+ context
+ .getSystemService(ActivityManager::class.java)!!
+ .getPackageImportance(packageName)
if (packageImportance <= IMPORTANCE_CANT_SAVE_STATE) {
// Process is running in a state where it should not be killed
- DumpableLog.i(LOG_TAG,
+ DumpableLog.i(
+ LOG_TAG,
"Skipping hibernation - $packageName running with importance " +
- "$packageImportance")
+ "$packageImportance"
+ )
return@forEachInParallel
}
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG, "unused app $packageName - last used on " +
- userStats[user]?.lastTimePackageUsed(packageName)?.let(::Date))
+ DumpableLog.i(
+ LOG_TAG,
+ "unused app $packageName - last used on " +
+ userStats[user]?.lastTimePackageUsed(packageName)?.let(::Date)
+ )
}
- synchronized(userAppsToHibernate) {
- userAppsToHibernate.add(pkg)
- }
+ synchronized(userAppsToHibernate) { userAppsToHibernate.add(pkg) }
}
appsToHibernate.put(user, userAppsToHibernate)
}
@@ -460,9 +510,9 @@ private suspend fun getAppsToHibernate(
}
/**
- * Gets the last time we consider the package used based off its usage stats. On pre-S devices
- * this looks at last time visible which tracks explicit usage. In S, we add component usage
- * which tracks various forms of implicit usage (e.g. service bindings).
+ * Gets the last time we consider the package used based off its usage stats. On pre-S devices this
+ * looks at last time visible which tracks explicit usage. In S, we add component usage which tracks
+ * various forms of implicit usage (e.g. service bindings).
*/
fun UsageStats.lastTimePackageUsed(): Long {
var lastTimePkgUsed = this.lastTimeVisible
@@ -486,9 +536,7 @@ private fun List<UsageStats>.lastTimePackageUsed(pkgName: String): Long {
return lastTimePackageUsed(listOf(pkgName))
}
-/**
- * Checks if the given package is exempt from hibernation in a way that's not user-overridable
- */
+/** Checks if the given package is exempt from hibernation in a way that's not user-overridable */
suspend fun isPackageHibernationExemptBySystem(
pkg: LightPackageInfo,
user: UserHandle,
@@ -499,23 +547,22 @@ suspend fun isPackageHibernationExemptBySystem(
}
return true
}
- if (!ExemptServicesLiveData[user]
- .getInitializedValue()[pkg.packageName]
- .isNullOrEmpty()) {
+ if (!ExemptServicesLiveData[user].getInitializedValue()[pkg.packageName].isNullOrEmpty()) {
return true
}
if (Utils.isUserDisabledOrWorkProfile(user)) {
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG,
- "Exempted ${pkg.packageName} - $user is disabled or a work profile")
+ DumpableLog.i(
+ LOG_TAG,
+ "Exempted ${pkg.packageName} - $user is disabled or a work profile"
+ )
}
return true
}
- if (pkg.uid == Process.SYSTEM_UID){
+ if (pkg.uid == Process.SYSTEM_UID) {
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG,
- "Exempted ${pkg.packageName} - Package shares system uid")
+ DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - Package shares system uid")
}
return true
}
@@ -523,8 +570,8 @@ suspend fun isPackageHibernationExemptBySystem(
val context = PermissionControllerApplication.get()
if (context.getSystemService(DevicePolicyManager::class.java)!!.isDeviceManaged) {
// TODO(b/237065504): Use proper system API to check if the device is financed in U.
- val isFinancedDevice = Settings.Global.getInt(
- context.contentResolver, "device_owner_type", 0) == 1
+ val isFinancedDevice =
+ Settings.Global.getInt(context.contentResolver, "device_owner_type", 0) == 1
if (!isFinancedDevice) {
if (DEBUG_HIBERNATION_POLICY) {
DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - device is managed")
@@ -533,12 +580,16 @@ suspend fun isPackageHibernationExemptBySystem(
}
}
- val carrierPrivilegedStatus = CarrierPrivilegedStatusLiveData[pkg.packageName]
- .getInitializedValue()
- if (carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_HAS_ACCESS &&
- carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
- DumpableLog.w(LOG_TAG, "Error carrier privileged status for ${pkg.packageName}: " +
- carrierPrivilegedStatus)
+ val carrierPrivilegedStatus =
+ CarrierPrivilegedStatusLiveData[pkg.packageName].getInitializedValue()
+ if (
+ carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_HAS_ACCESS &&
+ carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_NO_ACCESS
+ ) {
+ DumpableLog.w(
+ LOG_TAG,
+ "Error carrier privileged status for ${pkg.packageName}: " + carrierPrivilegedStatus
+ )
}
if (carrierPrivilegedStatus == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
if (DEBUG_HIBERNATION_POLICY) {
@@ -547,19 +598,24 @@ suspend fun isPackageHibernationExemptBySystem(
return true
}
- if (PermissionControllerApplication.get()
+ if (
+ PermissionControllerApplication.get()
.packageManager
- .checkPermission(
- Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
- pkg.packageName) == PERMISSION_GRANTED) {
+ .checkPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pkg.packageName) ==
+ PERMISSION_GRANTED
+ ) {
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} " +
- "- holder of READ_PRIVILEGED_PHONE_STATE")
+ DumpableLog.i(
+ LOG_TAG,
+ "Exempted ${pkg.packageName} " + "- holder of READ_PRIVILEGED_PHONE_STATE"
+ )
}
return true
}
- val emergencyRoleHolders = context.getSystemService(android.app.role.RoleManager::class.java)!!
+ val emergencyRoleHolders =
+ context
+ .getSystemService(android.app.role.RoleManager::class.java)!!
.getRoleHolders(RoleManager.ROLE_EMERGENCY)
if (emergencyRoleHolders.contains(pkg.packageName)) {
if (DEBUG_HIBERNATION_POLICY) {
@@ -570,19 +626,19 @@ suspend fun isPackageHibernationExemptBySystem(
if (SdkLevel.isAtLeastS()) {
val hasInstallOrUpdatePermissions =
+ context.checkPermission(Manifest.permission.INSTALL_PACKAGES, -1 /* pid */, pkg.uid) ==
+ PERMISSION_GRANTED ||
context.checkPermission(
- Manifest.permission.INSTALL_PACKAGES, -1 /* pid */, pkg.uid) ==
- PERMISSION_GRANTED ||
- context.checkPermission(
- Manifest.permission.INSTALL_PACKAGE_UPDATES, -1 /* pid */, pkg.uid) ==
- PERMISSION_GRANTED
+ Manifest.permission.INSTALL_PACKAGE_UPDATES,
+ -1 /* pid */,
+ pkg.uid
+ ) == PERMISSION_GRANTED
val hasUpdatePackagesWithoutUserActionPermission =
- context.checkPermission(
- UPDATE_PACKAGES_WITHOUT_USER_ACTION, -1 /* pid */, pkg.uid) ==
- PERMISSION_GRANTED
+ context.checkPermission(UPDATE_PACKAGES_WITHOUT_USER_ACTION, -1 /* pid */, pkg.uid) ==
+ PERMISSION_GRANTED
val isInstallerOfRecord =
- InstallerPackagesLiveData[user].getInitializedValue().contains(pkg.packageName) &&
- hasUpdatePackagesWithoutUserActionPermission
+ InstallerPackagesLiveData[user].getInitializedValue().contains(pkg.packageName) &&
+ hasUpdatePackagesWithoutUserActionPermission
// Grant if app w/ privileged install/update permissions or app is an installer app that
// updates packages without user action.
if (hasInstallOrUpdatePermissions || isInstallerOfRecord) {
@@ -592,7 +648,9 @@ suspend fun isPackageHibernationExemptBySystem(
return true
}
- val roleHolders = context.getSystemService(android.app.role.RoleManager::class.java)!!
+ val roleHolders =
+ context
+ .getSystemService(android.app.role.RoleManager::class.java)!!
.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)
if (roleHolders.contains(pkg.packageName)) {
if (DEBUG_HIBERNATION_POLICY) {
@@ -603,8 +661,10 @@ suspend fun isPackageHibernationExemptBySystem(
}
if (SdkLevel.isAtLeastT()) {
- val roleHolders = context.getSystemService(android.app.role.RoleManager::class.java)!!
- .getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT)
+ val roleHolders =
+ context
+ .getSystemService(android.app.role.RoleManager::class.java)!!
+ .getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT)
if (roleHolders.contains(pkg.packageName)) {
if (DEBUG_HIBERNATION_POLICY) {
DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - device policy manager app")
@@ -613,9 +673,12 @@ suspend fun isPackageHibernationExemptBySystem(
}
}
- if (isSystemExemptFromHibernationEnabled() && AppOpLiveData[pkg.packageName,
- AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION,
- pkg.uid].getInitializedValue() == AppOpsManager.MODE_ALLOWED) {
+ if (
+ isSystemExemptFromHibernationEnabled() &&
+ AppOpLiveData[
+ pkg.packageName, AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION, pkg.uid]
+ .getInitializedValue() == AppOpsManager.MODE_ALLOWED
+ ) {
if (DEBUG_HIBERNATION_POLICY) {
DumpableLog.i(
LOG_TAG,
@@ -640,8 +703,8 @@ suspend fun isPackageHibernationExemptByUser(
val packageUid = pkg.uid
val allowlistAppOpMode =
- AppOpLiveData[packageName,
- AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid]
+ AppOpLiveData[
+ packageName, AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid]
.getInitializedValue()
if (allowlistAppOpMode == AppOpsManager.MODE_DEFAULT) {
// Initial state - allowlist not explicitly overridden by either user or installer
@@ -657,11 +720,11 @@ suspend fun isPackageHibernationExemptByUser(
// Q- packages exempt by default, except R- on Auto since Auto-Revoke was skipped in R
val maxTargetSdkVersionForExemptApps =
- if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- android.os.Build.VERSION_CODES.R
- } else {
- android.os.Build.VERSION_CODES.Q
- }
+ if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ android.os.Build.VERSION_CODES.R
+ } else {
+ android.os.Build.VERSION_CODES.Q
+ }
return pkg.targetSdkVersion <= maxTargetSdkVersionForExemptApps
}
@@ -670,18 +733,18 @@ suspend fun isPackageHibernationExemptByUser(
}
private fun Context.isPackageCrossProfile(pkg: String): Boolean {
- return packageManager.checkPermission(
- Manifest.permission.INTERACT_ACROSS_PROFILES, pkg) == PERMISSION_GRANTED ||
- packageManager.checkPermission(
- Manifest.permission.INTERACT_ACROSS_USERS, pkg) == PERMISSION_GRANTED ||
- packageManager.checkPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL, pkg) == PERMISSION_GRANTED
+ return packageManager.checkPermission(Manifest.permission.INTERACT_ACROSS_PROFILES, pkg) ==
+ PERMISSION_GRANTED ||
+ packageManager.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS, pkg) ==
+ PERMISSION_GRANTED ||
+ packageManager.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, pkg) ==
+ PERMISSION_GRANTED
}
val Context.sharedPreferences: SharedPreferences
get() {
- return PreferenceManager.getDefaultSharedPreferences(this)
-}
+ return PreferenceManager.getDefaultSharedPreferences(this)
+ }
internal class SystemTime {
var actualSystemTime: Long = SNAPSHOT_UNINITIALIZED
@@ -691,15 +754,15 @@ internal class SystemTime {
private fun getSystemTime(sharedPreferences: SharedPreferences): SystemTime {
val systemTime = SystemTime()
- val systemTimeSnapshot = sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT,
- SNAPSHOT_UNINITIALIZED)
+ val systemTimeSnapshot =
+ sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
if (systemTimeSnapshot == SNAPSHOT_UNINITIALIZED) {
DumpableLog.e(LOG_TAG, "PREF_KEY_BOOT_TIME_SNAPSHOT is not initialized")
return systemTime
}
- val realtimeSnapshot = sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT,
- SNAPSHOT_UNINITIALIZED)
+ val realtimeSnapshot =
+ sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
if (realtimeSnapshot == SNAPSHOT_UNINITIALIZED) {
DumpableLog.e(LOG_TAG, "PREF_KEY_ELAPSED_REALTIME_SNAPSHOT is not initialized")
return systemTime
@@ -712,14 +775,19 @@ private fun getSystemTime(sharedPreferences: SharedPreferences): SystemTime {
}
fun getStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences): Long {
- val startTimeOfUnusedAppTracking = sharedPreferences.getLong(
- PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, SNAPSHOT_UNINITIALIZED)
+ val startTimeOfUnusedAppTracking =
+ sharedPreferences.getLong(
+ PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
+ SNAPSHOT_UNINITIALIZED
+ )
// If the preference is not initialized then use the current system time.
if (startTimeOfUnusedAppTracking == SNAPSHOT_UNINITIALIZED) {
val actualSystemTime = System.currentTimeMillis()
- sharedPreferences.edit()
- .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, actualSystemTime).apply()
+ sharedPreferences
+ .edit()
+ .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, actualSystemTime)
+ .apply()
return actualSystemTime
}
@@ -728,20 +796,28 @@ fun getStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences): Long
if (diffSystemTime > ONE_DAY_MS) {
adjustStartTimeOfUnusedAppTracking(sharedPreferences)
}
- return sharedPreferences.getLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
- SNAPSHOT_UNINITIALIZED)
+ return sharedPreferences.getLong(
+ PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
+ SNAPSHOT_UNINITIALIZED
+ )
}
private fun initStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences) {
val systemTimeSnapshot = System.currentTimeMillis()
- if (sharedPreferences
- .getLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, SNAPSHOT_UNINITIALIZED)
- == SNAPSHOT_UNINITIALIZED) {
- sharedPreferences.edit()
- .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, systemTimeSnapshot).apply()
+ if (
+ sharedPreferences.getLong(
+ PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
+ SNAPSHOT_UNINITIALIZED
+ ) == SNAPSHOT_UNINITIALIZED
+ ) {
+ sharedPreferences
+ .edit()
+ .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, systemTimeSnapshot)
+ .apply()
}
val realtimeSnapshot = SystemClock.elapsedRealtime()
- sharedPreferences.edit()
+ sharedPreferences
+ .edit()
.putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTimeSnapshot)
.putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, realtimeSnapshot)
.apply()
@@ -750,49 +826,52 @@ private fun initStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreference
private fun adjustStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences) {
val systemTime = getSystemTime(sharedPreferences)
val startTimeOfUnusedAppTracking =
- sharedPreferences.getLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
- SNAPSHOT_UNINITIALIZED)
+ sharedPreferences.getLong(
+ PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
+ SNAPSHOT_UNINITIALIZED
+ )
if (startTimeOfUnusedAppTracking == SNAPSHOT_UNINITIALIZED) {
DumpableLog.e(LOG_TAG, "PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING is not initialized")
return
}
val adjustedStartTimeOfUnusedAppTracking =
startTimeOfUnusedAppTracking + systemTime.diffSystemTime
- sharedPreferences.edit()
+ sharedPreferences
+ .edit()
.putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, adjustedStartTimeOfUnusedAppTracking)
.putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTime.actualSystemTime)
.putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, systemTime.actualRealtime)
.apply()
}
-/**
- * Make intent to go to unused apps page.
- */
+/** Make intent to go to unused apps page. */
private fun makeUnusedAppsIntent(context: Context, sessionId: Long): PendingIntent {
- val clickIntent = Intent(Intent.ACTION_MANAGE_UNUSED_APPS).apply {
- putExtra(Constants.EXTRA_SESSION_ID, sessionId)
- flags = FLAG_ACTIVITY_NEW_TASK
- }
- val pendingIntent = PendingIntent.getActivity(context, 0, clickIntent,
- FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
+ val clickIntent =
+ Intent(Intent.ACTION_MANAGE_UNUSED_APPS).apply {
+ putExtra(Constants.EXTRA_SESSION_ID, sessionId)
+ flags = FLAG_ACTIVITY_NEW_TASK
+ }
+ val pendingIntent =
+ PendingIntent.getActivity(context, 0, clickIntent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
return pendingIntent
}
-/**
- * Make intent for when safety center card is dismissed.
- */
+/** Make intent for when safety center card is dismissed. */
private fun makeDismissIntent(context: Context, sessionId: Long): PendingIntent {
- val dismissIntent = Intent(context, DismissHandler::class.java).apply {
- putExtra(Constants.EXTRA_SESSION_ID, sessionId)
- flags = FLAG_RECEIVER_FOREGROUND
- }
- return PendingIntent.getBroadcast(context, /* requestCode= */ 0, dismissIntent,
- FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
+ val dismissIntent =
+ Intent(context, DismissHandler::class.java).apply {
+ putExtra(Constants.EXTRA_SESSION_ID, sessionId)
+ flags = FLAG_RECEIVER_FOREGROUND
+ }
+ return PendingIntent.getBroadcast(
+ context,
+ /* requestCode= */ 0,
+ dismissIntent,
+ FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
}
-/**
- * Broadcast receiver class for when safety center card is dismissed.
- */
+/** Broadcast receiver class for when safety center card is dismissed. */
class DismissHandler : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
setUnusedAppsReviewNeeded(context!!, false)
@@ -800,8 +879,8 @@ class DismissHandler : BroadcastReceiver() {
}
/**
- * A job to check for apps unused in the last [getUnusedThresholdMs]ms every
- * [getCheckFrequencyMs]ms and hibernate the app / revoke their runtime permissions.
+ * A job to check for apps unused in the last [getUnusedThresholdMs]ms every [getCheckFrequencyMs]ms
+ * and hibernate the app / revoke their runtime permissions.
*/
class HibernationJobService : JobService() {
var job: Job? = null
@@ -822,58 +901,75 @@ class HibernationJobService : JobService() {
}
jobStartTime = System.currentTimeMillis()
- job = GlobalScope.launch(Main) {
- try {
- var sessionId = Constants.INVALID_SESSION_ID
- while (sessionId == Constants.INVALID_SESSION_ID) {
- sessionId = Random().nextLong()
- }
+ job =
+ GlobalScope.launch(Main) {
+ try {
+ var sessionId = Constants.INVALID_SESSION_ID
+ while (sessionId == Constants.INVALID_SESSION_ID) {
+ sessionId = Random().nextLong()
+ }
- val appsToHibernate = getAppsToHibernate(this@HibernationJobService)
- var hibernatedApps: Set<Pair<String, UserHandle>> = emptySet()
- if (isHibernationEnabled()) {
- val hibernationController =
- HibernationController(this@HibernationJobService, getUnusedThresholdMs(),
- hibernationTargetsPreSApps())
- hibernatedApps = hibernationController.hibernateApps(appsToHibernate)
- }
- val revokedApps = revokeAppPermissions(
- appsToHibernate, this@HibernationJobService, sessionId)
- val unusedApps: Set<Pair<String, UserHandle>> = hibernatedApps + revokedApps
- if (unusedApps.isNotEmpty()) {
- showUnusedAppsNotification(unusedApps.size, sessionId)
- if (SdkLevel.isAtLeastT() &&
- revokedApps.isNotEmpty() &&
- getSystemService(SafetyCenterManager::class.java)!!.isSafetyCenterEnabled) {
- setUnusedAppsReviewNeeded(this@HibernationJobService, true)
- rescanAndPushDataToSafetyCenter(
- this@HibernationJobService,
- sessionId,
- SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED)
- .build())
+ val appsToHibernate = getAppsToHibernate(this@HibernationJobService)
+ var hibernatedApps: Set<Pair<String, UserHandle>> = emptySet()
+ if (isHibernationEnabled()) {
+ val hibernationController =
+ HibernationController(
+ this@HibernationJobService,
+ getUnusedThresholdMs(),
+ hibernationTargetsPreSApps()
+ )
+ hibernatedApps = hibernationController.hibernateApps(appsToHibernate)
+ }
+ val revokedApps =
+ revokeAppPermissions(appsToHibernate, this@HibernationJobService, sessionId)
+ val unusedApps: Set<Pair<String, UserHandle>> = hibernatedApps + revokedApps
+ if (unusedApps.isNotEmpty()) {
+ showUnusedAppsNotification(unusedApps.size, sessionId)
+ if (
+ SdkLevel.isAtLeastT() &&
+ revokedApps.isNotEmpty() &&
+ getSystemService(SafetyCenterManager::class.java)!!
+ .isSafetyCenterEnabled
+ ) {
+ setUnusedAppsReviewNeeded(this@HibernationJobService, true)
+ rescanAndPushDataToSafetyCenter(
+ this@HibernationJobService,
+ sessionId,
+ SafetyEvent.Builder(
+ SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED
+ )
+ .build()
+ )
+ }
}
+ } catch (e: Exception) {
+ DumpableLog.e(LOG_TAG, "Failed to auto-revoke permissions", e)
}
- } catch (e: Exception) {
- DumpableLog.e(LOG_TAG, "Failed to auto-revoke permissions", e)
+ jobFinished(params, false)
}
- jobFinished(params, false)
- }
return true
}
- private suspend fun showUnusedAppsNotification(numUnused: Int, sessionId: Long) {
+ private fun showUnusedAppsNotification(numUnused: Int, sessionId: Long) {
val notificationManager = getSystemService(NotificationManager::class.java)!!
- val permissionReminderChannel = NotificationChannel(
- Constants.PERMISSION_REMINDER_CHANNEL_ID, getString(R.string.permission_reminders),
- NotificationManager.IMPORTANCE_LOW)
+ val permissionReminderChannel =
+ NotificationChannel(
+ Constants.PERMISSION_REMINDER_CHANNEL_ID,
+ getString(R.string.permission_reminders),
+ NotificationManager.IMPORTANCE_LOW
+ )
notificationManager.createNotificationChannel(permissionReminderChannel)
var notifTitle: String
var notifContent: String
if (isHibernationEnabled()) {
- notifTitle = StringUtils.getIcuPluralsString(this,
- R.string.unused_apps_notification_title, numUnused)
+ notifTitle =
+ StringUtils.getIcuPluralsString(
+ this,
+ R.string.unused_apps_notification_title,
+ numUnused
+ )
notifContent = getString(R.string.unused_apps_notification_content)
} else {
notifTitle = getString(R.string.auto_revoke_permission_notification_title)
@@ -881,16 +977,19 @@ class HibernationJobService : JobService() {
}
// Notification won't appear on TV, because notifications are considered distruptive on TV
- val b = Notification.Builder(this, Constants.PERMISSION_REMINDER_CHANNEL_ID)
- .setContentTitle(notifTitle)
- .setContentText(notifContent)
- .setStyle(Notification.BigTextStyle().bigText(notifContent))
- .setColor(getColor(android.R.color.system_notification_accent_color))
- .setAutoCancel(true)
- .setContentIntent(makeUnusedAppsIntent(this, sessionId))
+ val b =
+ Notification.Builder(this, Constants.PERMISSION_REMINDER_CHANNEL_ID)
+ .setContentTitle(notifTitle)
+ .setContentText(notifContent)
+ .setStyle(Notification.BigTextStyle().bigText(notifContent))
+ .setColor(getColor(android.R.color.system_notification_accent_color))
+ .setAutoCancel(true)
+ .setContentIntent(makeUnusedAppsIntent(this, sessionId))
val extras = Bundle()
- if (SdkLevel.isAtLeastT() &&
- getSystemService(SafetyCenterManager::class.java)!!.isSafetyCenterEnabled) {
+ if (
+ SdkLevel.isAtLeastT() &&
+ getSystemService(SafetyCenterManager::class.java)!!.isSafetyCenterEnabled
+ ) {
val notificationResources = KotlinUtils.getSafetyCenterNotificationResources(this)
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, notificationResources.appLabel)
@@ -902,15 +1001,19 @@ class HibernationJobService : JobService() {
Utils.getSettingsLabelForNotifications(applicationContext.packageManager)?.let {
settingsLabel ->
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, settingsLabel.toString())
- b.setSmallIcon(R.drawable.ic_settings_24dp)
- .addExtras(extras)
+ b.setSmallIcon(R.drawable.ic_settings_24dp).addExtras(extras)
}
}
- notificationManager.notify(HibernationJobService::class.java.simpleName,
- Constants.UNUSED_APPS_NOTIFICATION_ID, b.build())
- // Preload the unused packages
- getUnusedPackages().getInitializedValue()
+ notificationManager.notify(
+ HibernationJobService::class.java.simpleName,
+ Constants.UNUSED_APPS_NOTIFICATION_ID,
+ b.build()
+ )
+ GlobalScope.launch(IPC) {
+ // Preload the unused packages
+ getUnusedPackages().getInitializedValue(staleOk = true)
+ }
}
override fun onStopJob(params: JobParameters?): Boolean {
@@ -926,47 +1029,39 @@ class HibernationJobService : JobService() {
*/
class ExemptServicesLiveData(private val user: UserHandle) :
SmartUpdateMediatorLiveData<Map<String, List<String>>>() {
- private val serviceLiveDatas: List<SmartUpdateMediatorLiveData<Set<String>>> = listOf(
- ServiceLiveData[InputMethod.SERVICE_INTERFACE,
- Manifest.permission.BIND_INPUT_METHOD,
- user],
+ private val serviceLiveDatas: List<SmartUpdateMediatorLiveData<Set<String>>> =
+ listOf(
ServiceLiveData[
- NotificationListenerService.SERVICE_INTERFACE,
- Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE,
- user],
+ InputMethod.SERVICE_INTERFACE, Manifest.permission.BIND_INPUT_METHOD, user],
ServiceLiveData[
- AccessibilityService.SERVICE_INTERFACE,
- Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
- user],
+ NotificationListenerService.SERVICE_INTERFACE,
+ Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE,
+ user],
ServiceLiveData[
- WallpaperService.SERVICE_INTERFACE,
- Manifest.permission.BIND_WALLPAPER,
- user],
+ AccessibilityService.SERVICE_INTERFACE,
+ Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
+ user],
ServiceLiveData[
- VoiceInteractionService.SERVICE_INTERFACE,
- Manifest.permission.BIND_VOICE_INTERACTION,
- user],
+ WallpaperService.SERVICE_INTERFACE, Manifest.permission.BIND_WALLPAPER, user],
ServiceLiveData[
- PrintService.SERVICE_INTERFACE,
- Manifest.permission.BIND_PRINT_SERVICE,
- user],
+ VoiceInteractionService.SERVICE_INTERFACE,
+ Manifest.permission.BIND_VOICE_INTERACTION,
+ user],
ServiceLiveData[
- DreamService.SERVICE_INTERFACE,
- Manifest.permission.BIND_DREAM_SERVICE,
- user],
+ PrintService.SERVICE_INTERFACE, Manifest.permission.BIND_PRINT_SERVICE, user],
ServiceLiveData[
- AutofillService.SERVICE_INTERFACE,
- Manifest.permission.BIND_AUTOFILL_SERVICE,
- user],
+ DreamService.SERVICE_INTERFACE, Manifest.permission.BIND_DREAM_SERVICE, user],
ServiceLiveData[
- DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE,
- Manifest.permission.BIND_DEVICE_ADMIN,
- user],
+ AutofillService.SERVICE_INTERFACE, Manifest.permission.BIND_AUTOFILL_SERVICE, user],
+ ServiceLiveData[
+ DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE,
+ Manifest.permission.BIND_DEVICE_ADMIN,
+ user],
BroadcastReceiverLiveData[
- DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
- Manifest.permission.BIND_DEVICE_ADMIN,
- user]
- )
+ DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
+ Manifest.permission.BIND_DEVICE_ADMIN,
+ user]
+ )
init {
serviceLiveDatas.forEach { addSource(it) { update() } }
@@ -978,8 +1073,9 @@ class ExemptServicesLiveData(private val user: UserHandle) :
serviceLiveDatas.forEach { serviceLD ->
serviceLD.value!!.forEach { packageName ->
- pksToServices.getOrPut(packageName, { mutableListOf() })
- .add((serviceLD as? HasIntentAction)?.intentAction ?: "???")
+ pksToServices
+ .getOrPut(packageName, { mutableListOf() })
+ .add((serviceLD as? HasIntentAction)?.intentAction ?: "???")
}
}
@@ -999,23 +1095,26 @@ class ExemptServicesLiveData(private val user: UserHandle) :
}
}
-/**
- * Live data for whether the hibernation feature is enabled or not.
- */
-object HibernationEnabledLiveData :
- MutableLiveData<Boolean>() {
+/** Live data for whether the hibernation feature is enabled or not. */
+object HibernationEnabledLiveData : MutableLiveData<Boolean>() {
init {
- postValue(SdkLevel.isAtLeastS() &&
- DeviceConfig.getBoolean(NAMESPACE_APP_HIBERNATION,
- Utils.PROPERTY_APP_HIBERNATION_ENABLED, true /* defaultValue */))
+ postValue(
+ SdkLevel.isAtLeastS() &&
+ DeviceConfig.getBoolean(
+ NAMESPACE_APP_HIBERNATION,
+ Utils.PROPERTY_APP_HIBERNATION_ENABLED,
+ true /* defaultValue */
+ )
+ )
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_APP_HIBERNATION,
PermissionControllerApplication.get().mainExecutor,
{ properties ->
for (key in properties.keyset) {
if (key == Utils.PROPERTY_APP_HIBERNATION_ENABLED) {
- value = SdkLevel.isAtLeastS() &&
- properties.getBoolean(key, true /* defaultValue */)
+ value =
+ SdkLevel.isAtLeastS() &&
+ properties.getBoolean(key, true /* defaultValue */)
break
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/v31/HibernationController.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/v31/HibernationController.kt
index f4dfe233a..e81c7a207 100644
--- a/PermissionController/src/com/android/permissioncontroller/hibernation/v31/HibernationController.kt
+++ b/PermissionController/src/com/android/permissioncontroller/hibernation/v31/HibernationController.kt
@@ -25,11 +25,10 @@ import android.os.Build
import android.os.UserHandle
import androidx.annotation.RequiresApi
import com.android.permissioncontroller.DumpableLog
+import com.android.permissioncontroller.permission.data.HibernatedPackagesLiveData
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
-/**
- * Hibernation controller that handles modifying hibernation state.
- */
+/** Hibernation controller that handles modifying hibernation state. */
@RequiresApi(Build.VERSION_CODES.S)
class HibernationController(
private val context: Context,
@@ -61,8 +60,7 @@ class HibernationController(
if (hibernationManager.isHibernatingForUser(pkg.packageName)) {
continue
}
- if (!targetsPreS &&
- pkg.targetSdkVersion < Build.VERSION_CODES.S) {
+ if (!targetsPreS && pkg.targetSdkVersion < Build.VERSION_CODES.S) {
// Only apps targeting S or above can be truly hibernated.
continue
}
@@ -80,8 +78,10 @@ class HibernationController(
context.getSystemService(APP_HIBERNATION_SERVICE) as AppHibernationManager
val globallyHibernatedApps = mutableSetOf<String>()
for ((pkgName, _) in hibernatedApps) {
- if (globallyHibernatedApps.contains(pkgName) ||
- hibernationManager.isHibernatingGlobally(pkgName)) {
+ if (
+ globallyHibernatedApps.contains(pkgName) ||
+ hibernationManager.isHibernatingGlobally(pkgName)
+ ) {
continue
}
@@ -94,10 +94,15 @@ class HibernationController(
hibernationManager.setHibernatingGlobally(pkgName, true)
globallyHibernatedApps.add(pkgName)
}
+ if (hibernatedApps.isNotEmpty()) {
+ HibernatedPackagesLiveData.update()
+ }
if (DEBUG_HIBERNATION) {
- DumpableLog.i(LOG_TAG,
+ DumpableLog.i(
+ LOG_TAG,
"Done hibernating apps $hibernatedApps \n " +
- "Globally hibernating apps $globallyHibernatedApps")
+ "Globally hibernating apps $globallyHibernatedApps"
+ )
}
return hibernatedApps
diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/v31/InstallerPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/v31/InstallerPackagesLiveData.kt
index f1a19294a..ebe57ec36 100644
--- a/PermissionController/src/com/android/permissioncontroller/hibernation/v31/InstallerPackagesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/hibernation/v31/InstallerPackagesLiveData.kt
@@ -27,17 +27,13 @@ import com.android.permissioncontroller.permission.data.DataRepositoryForPackage
import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData
import kotlinx.coroutines.Job
-/**
- * Packages that are the installer of record for some package on the device.
- */
+/** Packages that are the installer of record for some package on the device. */
@RequiresApi(Build.VERSION_CODES.S)
-class InstallerPackagesLiveData(private val user: UserHandle)
- : SmartAsyncMediatorLiveData<Set<String>>() {
+class InstallerPackagesLiveData(private val user: UserHandle) :
+ SmartAsyncMediatorLiveData<Set<String>>() {
init {
- addSource(AllPackageInfosLiveData) {
- update()
- }
+ addSource(AllPackageInfosLiveData) { update() }
}
override suspend fun loadDataAndPostValue(job: Job) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING
index b65eb6710..52f0fd1a5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING
@@ -74,5 +74,10 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsHibernationTestCases"
+ }
]
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AllPackageInfosLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AllPackageInfosLiveData.kt
index c2655ecff..b3a28eefe 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/AllPackageInfosLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AllPackageInfosLiveData.kt
@@ -20,9 +20,7 @@ import android.os.UserHandle
import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData.addSource
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
-/**
- * A LiveData which tracks the PackageInfos of all of the packages in the system, for all users.
- */
+/** A LiveData which tracks the PackageInfos of all of the packages in the system, for all users. */
object AllPackageInfosLiveData :
SmartUpdateMediatorLiveData<Map<UserHandle, List<LightPackageInfo>>>() {
@@ -30,9 +28,7 @@ object AllPackageInfosLiveData :
private val userPackageInfos = mutableMapOf<UserHandle, List<LightPackageInfo>>()
init {
- addSource(UsersLiveData) {
- update()
- }
+ addSource(UsersLiveData) { update() }
}
override fun onUpdate() {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt
index 5a0abeaa1..1e44f16bd 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt
@@ -27,12 +27,12 @@ import com.android.permissioncontroller.PermissionControllerApplication
* @param packageName The name of the package
* @param op The name of the appop
* @param uid The uid of the package
- *
* @see AppOpsManager
*/
// TODO eugenesusla: observe appops
// TODO eugenesusla: use for external storage
-class AppOpLiveData private constructor(
+class AppOpLiveData
+private constructor(
private val app: Application,
private val packageName: String,
private val op: String,
@@ -52,13 +52,18 @@ class AppOpLiveData private constructor(
/**
* Repository for AppOpLiveData.
- * <p> Key value is a triple of string package name, string appop, and
- * package uid, value is its corresponding LiveData.
+ *
+ * <p> Key value is a triple of string package name, string appop, and package uid, value is its
+ * corresponding LiveData.
*/
companion object : DataRepository<Triple<String, String, Int>, AppOpLiveData>() {
override fun newValue(key: Triple<String, String, Int>): AppOpLiveData {
- return AppOpLiveData(PermissionControllerApplication.get(),
- key.first, key.second, key.third)
+ return AppOpLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ key.third
+ )
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt
index b9d2d237a..b17098a13 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt
@@ -32,17 +32,17 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightPack
import com.android.permissioncontroller.permission.model.livedatatypes.LightPermGroupInfo
import com.android.permissioncontroller.permission.model.livedatatypes.LightPermInfo
import com.android.permissioncontroller.permission.model.livedatatypes.PermState
-import com.android.permissioncontroller.permission.utils.PermissionMapping.isPlatformPermissionGroup
import com.android.permissioncontroller.permission.utils.LocationUtils
+import com.android.permissioncontroller.permission.utils.PermissionMapping.isPlatformPermissionGroup
import com.android.permissioncontroller.permission.utils.Utils
import kotlinx.coroutines.Job
/**
* A LiveData representing UI properties of an App Permission Group:
* <ul>
- * <li>shouldShow</li>
- * <li>isSystem</li>
- * <li>isGranted</li>
+ * <li>shouldShow</li>
+ * <li>isSystem</li>
+ * <li>isGranted</li>
* </ul>
*
* @param app The current application
@@ -50,7 +50,8 @@ import kotlinx.coroutines.Job
* @param permGroupName The name of the permission group whose permissions are observed
* @param user The user of the package
*/
-class AppPermGroupUiInfoLiveData private constructor(
+class AppPermGroupUiInfoLiveData
+private constructor(
private val app: Application,
private val packageName: String,
private val permGroupName: String,
@@ -62,23 +63,22 @@ class AppPermGroupUiInfoLiveData private constructor(
private val permGroupLiveData = PermGroupLiveData[permGroupName]
private val permissionStateLiveData = PermStateLiveData[packageName, permGroupName, user]
private val isStorage = permGroupName == STORAGE
+ private val isHealth = Utils.isHealthPermissionGroup(permGroupName)
init {
- isSpecialLocation = LocationUtils.isLocationGroupAndProvider(app,
- permGroupName, packageName) ||
- LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName)
+ isSpecialLocation =
+ LocationUtils.isLocationGroupAndProvider(app, permGroupName, packageName) ||
+ LocationUtils.isLocationGroupAndControllerExtraPackage(
+ app,
+ permGroupName,
+ packageName
+ )
- addSource(packageInfoLiveData) {
- update()
- }
+ addSource(packageInfoLiveData) { update() }
- addSource(permGroupLiveData) {
- update()
- }
+ addSource(permGroupLiveData) { update() }
- addSource(permissionStateLiveData) {
- update()
- }
+ addSource(permissionStateLiveData) { update() }
}
override suspend fun loadDataAndPostValue(job: Job) {
@@ -90,27 +90,36 @@ class AppPermGroupUiInfoLiveData private constructor(
val permissionState = permissionStateLiveData.value
if (packageInfo == null || permissionGroup == null || permissionState == null) {
- if (packageInfoLiveData.isInitialized && permGroupLiveData.isInitialized &&
- permissionStateLiveData.isInitialized) {
+ if (
+ packageInfoLiveData.isInitialized &&
+ permGroupLiveData.isInitialized &&
+ permissionStateLiveData.isInitialized
+ ) {
invalidateSingle(Triple(packageName, permGroupName, user))
postValue(null)
}
return
}
- postValue(getAppPermGroupUiInfo(packageInfo, permissionGroup.groupInfo,
- permissionGroup.permissionInfos, permissionState))
+ postValue(
+ getAppPermGroupUiInfo(
+ packageInfo,
+ permissionGroup.groupInfo,
+ permissionGroup.permissionInfos,
+ permissionState
+ )
+ )
}
/**
- * Determines if the UI should show a given package, if that package is a system app, and
- * if it has granted permissions in this LiveData's permission group.
+ * Determines if the UI should show a given package, if that package is a system app, and if it
+ * has granted permissions in this LiveData's permission group.
*
* @param packageInfo The PackageInfo of the package we wish to examine
* @param groupInfo The groupInfo of the permission group we wish to examine
* @param allPermInfos All of the PermissionInfos in the permission group
- * @param permissionState The flags and grant state for all permissions in the permission
- * group that this package requests
+ * @param permissionState The flags and grant state for all permissions in the permission group
+ * that this package requests
*/
private fun getAppPermGroupUiInfo(
packageInfo: LightPackageInfo,
@@ -125,9 +134,11 @@ class AppPermGroupUiInfoLiveData private constructor(
val requestedPermissionInfos =
allPermInfos.filter { permissionState.containsKey(it.key) }.values
- val shouldShow = packageInfo.enabled &&
- isGrantableAndNotLegacyPlatform(packageInfo, groupInfo, requestedPermissionInfos) &&
- (!isStorage || Utils.shouldShowStorage(packageInfo))
+ val shouldShow =
+ packageInfo.enabled &&
+ isGrantableAndNotLegacyPlatform(packageInfo, groupInfo, requestedPermissionInfos) &&
+ (!isStorage || Utils.shouldShowStorage(packageInfo)) &&
+ (!isHealth || Utils.shouldShowHealthPermission(packageInfo, groupInfo.name))
val isSystemApp = !isUserSensitive(permissionState)
@@ -145,19 +156,17 @@ class AppPermGroupUiInfoLiveData private constructor(
*
* @param packageInfo The PackageInfo of the package we are examining
* @param groupInfo The Permission Group Info of the permission group we are examining
- * @param permissionInfos The LightPermInfos corresponding to the permissions in the
- * permission group that this package requests
- *
+ * @param permissionInfos The LightPermInfos corresponding to the permissions in the permission
+ * group that this package requests
* @return True if the app permission group is grantable, and is not a legacy system permission,
- * false otherwise.
+ * false otherwise.
*/
private fun isGrantableAndNotLegacyPlatform(
packageInfo: LightPackageInfo,
groupInfo: LightPermGroupInfo,
permissionInfos: Collection<LightPermInfo>
): Boolean {
- if (groupInfo.packageName == Utils.OS_PKG &&
- !isPlatformPermissionGroup(groupInfo.name)) {
+ if (groupInfo.packageName == Utils.OS_PKG && !isPlatformPermissionGroup(groupInfo.name)) {
return false
}
@@ -165,8 +174,9 @@ class AppPermGroupUiInfoLiveData private constructor(
var hasPreRuntime = false
for (permissionInfo in permissionInfos) {
- if (permissionInfo.protectionFlags and
- PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY == 0) {
+ if (
+ permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY == 0
+ ) {
hasPreRuntime = true
}
@@ -175,8 +185,9 @@ class AppPermGroupUiInfoLiveData private constructor(
}
}
- val isGrantingAllowed = (!packageInfo.isInstantApp || hasInstantPerm) &&
- (packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntime)
+ val isGrantingAllowed =
+ (!packageInfo.isInstantApp || hasInstantPerm) &&
+ (packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntime)
if (!isGrantingAllowed) {
return false
}
@@ -189,10 +200,9 @@ class AppPermGroupUiInfoLiveData private constructor(
* then it is considered a system app, and hidden in the UI by default.
*
* @param permissionState The permission flags and grant state corresponding to the permissions
- * in this group requested by a given app
- *
+ * in this group requested by a given app
* @return Whether or not this package requests a user sensitive permission in the given
- * permission group
+ * permission group
*/
private fun isUserSensitive(permissionState: Map<String, PermState>): Boolean {
if (!isPlatformPermissionGroup(permGroupName)) {
@@ -202,10 +212,12 @@ class AppPermGroupUiInfoLiveData private constructor(
for (permissionName in permissionState.keys) {
val flags = permissionState[permissionName]?.permFlags ?: return true
val granted = permissionState[permissionName]?.granted ?: return true
- if ((granted &&
+ if (
+ (granted &&
flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED != 0) ||
- (!granted &&
- flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0)) {
+ (!granted &&
+ flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0)
+ ) {
return true
}
}
@@ -216,32 +228,30 @@ class AppPermGroupUiInfoLiveData private constructor(
* Determines if the app permission group is user set
*
* @param permissionState The permission flags and grant state corresponding to the permissions
- * in this group requested by a given app
- *
+ * in this group requested by a given app
* @return Whether or not any of the permissions in this group have been set or fixed by the
- * user
+ * 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
return permissionState.any { (it.value.permFlags and flagMask) != 0 }
}
/**
- * Determines if this app permission group is granted, granted in foreground only, or denied.
- * It is granted if it either requests no background permissions, and has at least one requested
- * permission that is granted, or has granted at least one requested background permission.
- * It is granted in foreground only if it has at least one non-background permission granted,
- * and has denied all requested background permissions. It is denied if all requested
- * permissions are denied.
+ * Determines if this app permission group is granted, granted in foreground only, or denied. It
+ * is granted if it either requests no background permissions, and has at least one requested
+ * permission that is granted, or has granted at least one requested background permission. It
+ * is granted in foreground only if it has at least one non-background permission granted, and
+ * has denied all requested background permissions. It is denied if all requested permissions
+ * are denied.
*
* @param permissionState The permission flags and grant state corresponding to the permissions
- * in this group requested by a given app
- * @param allPermInfos All of the permissionInfos in the permission group of this app
- * permission group
- *
+ * in this group requested by a given app
+ * @param allPermInfos All of the permissionInfos in the permission group of this app permission
+ * group
* @return The int code corresponding to the app permission group state, either allowed, allowed
- * in foreground only, or denied.
+ * in foreground only, or denied.
*/
private fun getGrantedIncludingBackground(
permissionState: Map<String, PermState>,
@@ -260,36 +270,46 @@ class AppPermGroupUiInfoLiveData private constructor(
val permInfo = allPermInfos[permName] ?: continue
permInfo.backgroundPermission?.let { backgroundPerm ->
hasPermWithBackground = true
- if (permissionState[backgroundPerm]?.granted == true &&
+ if (
+ permissionState[backgroundPerm]?.granted == true &&
(permissionState[backgroundPerm]!!.permFlags and
- PackageManager.FLAG_PERMISSION_ONE_TIME == 0) &&
- specialLocationState != false) {
+ PackageManager.FLAG_PERMISSION_ONE_TIME == 0) &&
+ specialLocationState != false
+ ) {
return PermGrantState.PERMS_ALLOWED_ALWAYS
}
}
- isUserFixed = isUserFixed ||
+ isUserFixed =
+ isUserFixed ||
permState.permFlags and PackageManager.FLAG_PERMISSION_USER_FIXED != 0
}
// isOneTime indicates whether all granted permissions in permission states are one-time
// permissions
- val isOneTime = permissionState.any {
- it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0 } &&
+ val isOneTime =
+ permissionState.any {
+ it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0
+ } &&
!permissionState.any {
it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME == 0 &&
- it.value.granted }
+ it.value.granted
+ }
val supportsRuntime = pkg.targetSdkVersion >= Build.VERSION_CODES.M
- val anyAllowed = specialLocationState ?: permissionState.any { (_, state) ->
- state.granted || (supportsRuntime &&
- (state.permFlags and PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0)
- }
+ val anyAllowed =
+ specialLocationState
+ ?: permissionState.any { (_, state) ->
+ state.granted ||
+ (supportsRuntime &&
+ (state.permFlags and PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) !=
+ 0)
+ }
val onlySelectedPhotosGranted =
permissionState.containsKey(READ_MEDIA_VISUAL_USER_SELECTED) &&
- permissionState.all { (permName, state) ->
- (permName == READ_MEDIA_VISUAL_USER_SELECTED && state.granted) ||
- (permName != READ_MEDIA_VISUAL_USER_SELECTED && !state.granted)
- }
+ permissionState.all { (permName, state) ->
+ (permName == READ_MEDIA_VISUAL_USER_SELECTED && state.granted) ||
+ (permName != READ_MEDIA_VISUAL_USER_SELECTED && !state.granted)
+ }
if (anyAllowed && (hasPermWithBackground || shouldShowAsForegroundGroup())) {
return if (isOneTime) {
PermGrantState.PERMS_ASK
@@ -323,32 +343,38 @@ class AppPermGroupUiInfoLiveData private constructor(
}
// The permission of the extra location controller package is determined by the
// status of the controller package itself.
- if (LocationUtils.isLocationGroupAndControllerExtraPackage(userContext,
- permGroupName, packageName)) {
+ if (
+ LocationUtils.isLocationGroupAndControllerExtraPackage(
+ userContext,
+ permGroupName,
+ packageName
+ )
+ ) {
return LocationUtils.isExtraLocationControllerPackageEnabled(userContext)
}
return null
}
private fun isFullFilesAccessGranted(pkg: LightPackageInfo): Boolean {
- val packageState = if (!FullStoragePermissionAppsLiveData.isStale) {
- val fullStoragePackages = FullStoragePermissionAppsLiveData.value ?: return false
- fullStoragePackages.find {
- it.packageName == packageName && it.user == user
- } ?: return false
- } else {
- val appOpsManager = Utils.getUserContext(app, UserHandle.getUserHandleForUid(pkg.uid))
- .getSystemService(AppOpsManager::class.java)!!
- FullStoragePermissionAppsLiveData.getFullStorageStateForPackage(
- appOpsManager, pkg) ?: return false
- }
+ val packageState =
+ if (!FullStoragePermissionAppsLiveData.isStale) {
+ val fullStoragePackages = FullStoragePermissionAppsLiveData.value ?: return false
+ fullStoragePackages.find { it.packageName == packageName && it.user == user }
+ ?: return false
+ } else {
+ val appOpsManager =
+ Utils.getUserContext(app, UserHandle.getUserHandleForUid(pkg.uid))
+ .getSystemService(AppOpsManager::class.java)!!
+ FullStoragePermissionAppsLiveData.getFullStorageStateForPackage(appOpsManager, pkg)
+ ?: return false
+ }
return !packageState.isLegacy && packageState.isGranted
}
// TODO moltmann-team: Actually change mic/camera to be a foreground only permission
private fun shouldShowAsForegroundGroup(): Boolean {
return permGroupName.equals(Manifest.permission_group.CAMERA) ||
- permGroupName.equals(Manifest.permission_group.MICROPHONE)
+ permGroupName.equals(Manifest.permission_group.MICROPHONE)
}
override fun onLocationStateChange(enabled: Boolean) {
@@ -373,15 +399,19 @@ class AppPermGroupUiInfoLiveData private constructor(
/**
* Repository for AppPermGroupUiInfoLiveDatas.
- * <p> Key value is a triple of string package name, string permission group name, and UserHandle,
- * value is its corresponding LiveData.
+ *
+ * <p> Key value is a triple of string package name, string permission group name, and
+ * UserHandle, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>,
- AppPermGroupUiInfoLiveData>() {
- override fun newValue(key: Triple<String, String, UserHandle>):
- AppPermGroupUiInfoLiveData {
- return AppPermGroupUiInfoLiveData(PermissionControllerApplication.get(),
- key.first, key.second, key.third)
+ companion object :
+ DataRepositoryForPackage<Triple<String, String, UserHandle>, AppPermGroupUiInfoLiveData>() {
+ override fun newValue(key: Triple<String, String, UserHandle>): AppPermGroupUiInfoLiveData {
+ return AppPermGroupUiInfoLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ key.third
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveData.kt
index b55c736ad..fac901a04 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveData.kt
@@ -22,12 +22,12 @@ import android.content.res.Resources.ID_NULL
import android.os.UserHandle
import android.util.Log
import com.android.permissioncontroller.PermissionControllerApplication
+import java.io.FileNotFoundException
import kotlinx.coroutines.Job
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParser.END_DOCUMENT
import org.xmlpull.v1.XmlPullParser.END_TAG
import org.xmlpull.v1.XmlPullParser.START_TAG
-import java.io.FileNotFoundException
private const val MANIFEST_FILE_NAME = "AndroidManifest.xml"
private const val MANIFEST_TAG = "manifest"
@@ -43,7 +43,8 @@ private const val LABEL_ATTR = "label"
* <p>Obviously the resource is found in the package, hence needs to be loaded via a Resources
* object created for this package.
*/
-class AttributionLabelLiveData private constructor(
+class AttributionLabelLiveData
+private constructor(
private val app: Application,
private val attributionTag: String?,
private val packageName: String,
@@ -61,14 +62,15 @@ class AttributionLabelLiveData private constructor(
return
}
- val pkgContext = try {
- app.createPackageContextAsUser(packageName, 0, user)
- } catch (e: NameNotFoundException) {
- Log.e(LOG_TAG, "Cannot find $packageName for $user")
+ val pkgContext =
+ try {
+ app.createPackageContextAsUser(packageName, 0, user)
+ } catch (e: NameNotFoundException) {
+ Log.e(LOG_TAG, "Cannot find $packageName for $user")
- postValue(null)
- return
- }
+ postValue(null)
+ return
+ }
// TODO (moltmann): Read this from PackageInfo once available
var cookie = 0
@@ -76,11 +78,12 @@ class AttributionLabelLiveData private constructor(
// Some resources have multiple "AndroidManifest.xml" loaded and hence we need
// to find the right one
cookie++
- val parser = try {
- pkgContext.assets.openXmlResourceParser(cookie, MANIFEST_FILE_NAME)
- } catch (e: FileNotFoundException) {
- break
- }
+ val parser =
+ try {
+ pkgContext.assets.openXmlResourceParser(cookie, MANIFEST_FILE_NAME)
+ } catch (e: FileNotFoundException) {
+ break
+ }
try {
do {
@@ -109,8 +112,9 @@ class AttributionLabelLiveData private constructor(
}
if (parser.getAttributeValue(ANDROID_NS, TAG_ATTR) == attributionTag) {
- postValue(parser.getAttributeResourceValue(ANDROID_NS, LABEL_ATTR,
- ID_NULL))
+ postValue(
+ parser.getAttributeResourceValue(ANDROID_NS, LABEL_ATTR, ID_NULL)
+ )
return
} else {
parser.skipTag()
@@ -125,9 +129,7 @@ class AttributionLabelLiveData private constructor(
postValue(null)
}
- /**
- * Skip tag parser is currently pointing to (including all tags nested in it)
- */
+ /** Skip tag parser is currently pointing to (including all tags nested in it) */
private fun XmlPullParser.skipTag() {
var depth = 1
while (depth != 0) {
@@ -158,18 +160,25 @@ class AttributionLabelLiveData private constructor(
/**
* Repository for AttributionLiveData.
+ *
* <p> Key value is a pair of string attribution tag, string package name, user handle, value is
* its corresponding LiveData.
*/
- companion object : DataRepository<Triple<String?, String, UserHandle>,
- AttributionLabelLiveData>() {
+ companion object :
+ DataRepository<Triple<String?, String, UserHandle>, AttributionLabelLiveData>() {
override fun newValue(key: Triple<String?, String, UserHandle>): AttributionLabelLiveData {
- return AttributionLabelLiveData(PermissionControllerApplication.get(),
- key.first, key.second, key.third)
+ return AttributionLabelLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ key.third
+ )
}
- operator fun get(attributionTag: String?, packageName: String, user: UserHandle):
- AttributionLabelLiveData =
- get(Triple(attributionTag, packageName, user))
+ operator fun get(
+ attributionTag: String?,
+ packageName: String,
+ user: UserHandle
+ ): AttributionLabelLiveData = get(Triple(attributionTag, packageName, user))
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AutoRevokedPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AutoRevokedPackagesLiveData.kt
index 70f857afb..2e6fab44d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/AutoRevokedPackagesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AutoRevokedPackagesLiveData.kt
@@ -21,8 +21,8 @@ import android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED
import android.os.Build
import android.os.UserHandle
import android.util.Log
-import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.PermissionMapping
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
@@ -34,15 +34,13 @@ import kotlinx.coroutines.launch
*
* ```(packageName, user) -> [groupName]```
*/
-object AutoRevokedPackagesLiveData
- : SmartAsyncMediatorLiveData<Map<Pair<String, UserHandle>, Set<String>>>() {
+object AutoRevokedPackagesLiveData :
+ SmartAsyncMediatorLiveData<Map<Pair<String, UserHandle>, Set<String>>>() {
private val LOG_TAG = AutoRevokedPackagesLiveData::class.java.simpleName
init {
- addSource(AllPackageInfosLiveData) {
- update()
- }
+ addSource(AllPackageInfosLiveData) { update() }
}
private val permStateLiveDatas =
@@ -66,7 +64,8 @@ object AutoRevokedPackagesLiveData
for ((idx, requestedPerm) in pkg.requestedPermissions.withIndex()) {
val group =
PermissionMapping.getGroupOfPlatformPermission(requestedPerm) ?: continue
- val granted = (pkg.requestedPermissionsFlags[idx] and
+ val granted =
+ (pkg.requestedPermissionsFlags[idx] and
PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0
if (pkg.targetSdkVersion < Build.VERSION_CODES.M || !granted) {
pkgGroups.add(Triple(pkg.packageName, group, user))
@@ -85,7 +84,6 @@ object AutoRevokedPackagesLiveData
private fun observePermStateLiveDatas(packageGroups: Set<Triple<String, String, UserHandle>>) {
GlobalScope.launch(Main.immediate) {
-
val (toAdd, toRemove) =
KotlinUtils.getMapAndListDifferences(packageGroups, permStateLiveDatas)
@@ -118,7 +116,8 @@ object AutoRevokedPackagesLiveData
} else if (permState != null) {
for ((_, state) in permState) {
if (state.permFlags and FLAG_PERMISSION_AUTO_REVOKED != 0) {
- packageAutoRevokedPermsList.getOrPut(packageUser) { mutableSetOf() }
+ packageAutoRevokedPermsList
+ .getOrPut(packageUser) { mutableSetOf() }
.add(packagePermGroup.second)
added = true
break
@@ -142,8 +141,7 @@ object AutoRevokedPackagesLiveData
}
private fun postCopyOfMap() {
- val autoRevokedCopy =
- mutableMapOf<Pair<String, UserHandle>, Set<String>>()
+ val autoRevokedCopy = mutableMapOf<Pair<String, UserHandle>, Set<String>>()
for ((userPackage, permGroups) in packageAutoRevokedPermsList) {
autoRevokedCopy[userPackage] = permGroups.toSet()
}
@@ -155,9 +153,7 @@ object AutoRevokedPackagesLiveData
private val autoRevokedPackagesSetLiveData =
object : SmartUpdateMediatorLiveData<Set<Pair<String, UserHandle>>>() {
init {
- addSource(AutoRevokedPackagesLiveData) {
- update()
- }
+ addSource(AutoRevokedPackagesLiveData) { update() }
}
override fun onUpdate() {
@@ -168,4 +164,4 @@ private val autoRevokedPackagesSetLiveData =
}
}
-val unusedAutoRevokePackagesLiveData = UnusedPackagesLiveData(autoRevokedPackagesSetLiveData) \ No newline at end of file
+val unusedAutoRevokePackagesLiveData = UnusedPackagesLiveData(autoRevokedPackagesSetLiveData)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/BroadcastReceiverLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/BroadcastReceiverLiveData.kt
index 0a296d977..e14a02115 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/BroadcastReceiverLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/BroadcastReceiverLiveData.kt
@@ -41,9 +41,10 @@ class BroadcastReceiverLiveData(
override val intentAction: String,
private val permission: String,
private val user: UserHandle
-) : SmartAsyncMediatorLiveData<Set<String>>(),
- PackageBroadcastReceiver.PackageBroadcastListener,
- HasIntentAction {
+) :
+ SmartAsyncMediatorLiveData<Set<String>>(),
+ PackageBroadcastReceiver.PackageBroadcastListener,
+ HasIntentAction {
private val name = intentAction.substringAfterLast(".")
@@ -51,9 +52,7 @@ class BroadcastReceiverLiveData(
init {
if (intentAction == DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED) {
- addSource(enabledDeviceAdminsLiveDataLiveData) {
- updateAsync()
- }
+ addSource(enabledDeviceAdminsLiveDataLiveData) { updateAsync() }
}
}
@@ -65,15 +64,20 @@ class BroadcastReceiverLiveData(
if (job.isCancelled) {
return
}
- if (intentAction == DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED &&
- !enabledDeviceAdminsLiveDataLiveData.isInitialized) {
+ if (
+ intentAction == DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED &&
+ !enabledDeviceAdminsLiveDataLiveData.isInitialized
+ ) {
return
}
- val packageNames = getUserContext(app, user).packageManager
+ val packageNames =
+ getUserContext(app, user)
+ .packageManager
.queryBroadcastReceivers(
- Intent(intentAction),
- PackageManager.GET_RECEIVERS or PackageManager.GET_META_DATA)
+ Intent(intentAction),
+ PackageManager.GET_RECEIVERS or PackageManager.GET_META_DATA
+ )
.mapNotNull { resolveInfo ->
if (resolveInfo?.activityInfo?.permission != permission) {
return@mapNotNull null
@@ -81,17 +85,22 @@ class BroadcastReceiverLiveData(
val packageName = resolveInfo.activityInfo?.packageName
if (!isReceiverEnabled(packageName)) {
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG,
- "Not exempting $packageName - not an active $name " +
- "for u${user.identifier}")
+ DumpableLog.i(
+ LOG_TAG,
+ "Not exempting $packageName - not an active $name " +
+ "for u${user.identifier}"
+ )
}
return@mapNotNull null
}
packageName
- }.toSet()
+ }
+ .toSet()
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG,
- "Detected ${intentAction.substringAfterLast(".")}s: $packageNames")
+ DumpableLog.i(
+ LOG_TAG,
+ "Detected ${intentAction.substringAfterLast(".")}s: $packageNames"
+ )
}
postValue(packageNames)
@@ -127,13 +136,17 @@ class BroadcastReceiverLiveData(
* <p> Key value is a (string intent action, required permission, user) triple, value is its
* corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>,
- BroadcastReceiverLiveData>() {
+ companion object :
+ DataRepositoryForPackage<Triple<String, String, UserHandle>, BroadcastReceiverLiveData>() {
private const val LOG_TAG = "BroadcastReceiverLiveData"
override fun newValue(key: Triple<String, String, UserHandle>): BroadcastReceiverLiveData {
- return BroadcastReceiverLiveData(PermissionControllerApplication.get(),
- key.first, key.second, key.third)
+ return BroadcastReceiverLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ key.third
+ )
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/CarrierPrivilegedStatusLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/CarrierPrivilegedStatusLiveData.kt
index fae7f223b..d84db741c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/CarrierPrivilegedStatusLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/CarrierPrivilegedStatusLiveData.kt
@@ -26,10 +26,9 @@ import com.android.permissioncontroller.PermissionControllerApplication
* @param app The current application
* @param packageName The name of the package
*/
-class CarrierPrivilegedStatusLiveData private constructor(
- private val app: Application,
- private val packageName: String
-) : SmartUpdateMediatorLiveData<Int>() {
+class CarrierPrivilegedStatusLiveData
+private constructor(private val app: Application, private val packageName: String) :
+ SmartUpdateMediatorLiveData<Int>() {
private val telephonyManager = app.getSystemService(TelephonyManager::class.java)!!
@@ -44,13 +43,13 @@ class CarrierPrivilegedStatusLiveData private constructor(
/**
* Repository for [CarrierPrivilegedStatusLiveData].
+ *
* <p> Key value is a package name, value is its corresponding LiveData of
* [android.telephony.Annotation.CarrierPrivilegeStatus]
*/
- companion object
- : DataRepository<String, CarrierPrivilegedStatusLiveData>() {
+ companion object : DataRepository<String, CarrierPrivilegedStatusLiveData>() {
override fun newValue(key: String): CarrierPrivilegedStatusLiveData {
return CarrierPrivilegedStatusLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/CustomPermGroupNamesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/CustomPermGroupNamesLiveData.kt
index cb44c0a27..757472464 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/CustomPermGroupNamesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/CustomPermGroupNamesLiveData.kt
@@ -24,7 +24,6 @@ import com.android.permissioncontroller.permission.utils.PermissionMapping
/**
* A class which tracks the names of all custom permission groups in the system, including
* non-grouped runtime permissions, the UNDEFINED group, and any group not defined by the system.
- *
*/
object CustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String>>() {
@@ -32,9 +31,7 @@ object CustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String>>(
private val packagesLiveData = AllPackageInfosLiveData
init {
- addSource(packagesLiveData) {
- update()
- }
+ addSource(packagesLiveData) { update() }
}
override fun onUpdate() {
@@ -49,15 +46,19 @@ object CustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String>>(
packageInfo.permissions.let {
for (permission in it) {
// We care only about installed runtime permissions.
- if (permission.protection != PermissionInfo.PROTECTION_DANGEROUS ||
- permission.flags and PermissionInfo.FLAG_INSTALLED == 0) {
+ if (
+ permission.protection != PermissionInfo.PROTECTION_DANGEROUS ||
+ permission.flags and PermissionInfo.FLAG_INSTALLED == 0
+ ) {
continue
}
// If this permission is already in a group, no more work to do
- if (groupNames.contains(permission.group) ||
- platformGroupNames.contains(permission.group) ||
- groupNames.contains(permission.name)) {
+ if (
+ groupNames.contains(permission.group) ||
+ platformGroupNames.contains(permission.group) ||
+ groupNames.contains(permission.name)
+ ) {
continue
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt
index 5cb91f5c5..c6c4ec2d6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt
@@ -39,18 +39,16 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon
private val TIME_THRESHOLD_ALL_NANOS: Long = 0
protected val lock = Any()
- @GuardedBy("lock")
- protected val data = mutableMapOf<K, V>()
+ @GuardedBy("lock") protected val data = mutableMapOf<K, V>()
- /**
- * Whether or not this data repository has been registered as a component callback yet
- */
+ /** Whether or not this data repository has been registered as a component callback yet */
private var registered = false
- /**
- * Whether or not this device is a low-RAM device.
- */
- private var isLowMemoryDevice = PermissionControllerApplication.get().getSystemService(
- ActivityManager::class.java)?.isLowRamDevice ?: false
+ /** Whether or not this device is a low-RAM device. */
+ private var isLowMemoryDevice =
+ PermissionControllerApplication.get()
+ .getSystemService(ActivityManager::class.java)
+ ?.isLowRamDevice
+ ?: false
init {
PermissionControllerApplication.get().registerComponentCallbacks(this)
@@ -60,7 +58,6 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon
* Get a value from this repository, creating it if needed
*
* @param key The key associated with the desired Value
- *
* @return The cached or newly created Value for the given Key
*/
operator fun get(key: K): V {
@@ -73,11 +70,9 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon
* Generate a new value type from the given data
*
* @param key Information about this value object, used to instantiate it
- *
* @return The generated Value
*/
- @MainThread
- protected abstract fun newValue(key: K): V
+ @MainThread protected abstract fun newValue(key: K): V
/**
* Remove LiveData objects with no observer based on the severity of the memory pressure. If
@@ -91,15 +86,18 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon
return
}
- trimInactiveData(threshold = when (level) {
- ComponentCallbacks2.TRIM_MEMORY_BACKGROUND -> TIME_THRESHOLD_LAX_NANOS
- ComponentCallbacks2.TRIM_MEMORY_MODERATE -> TIME_THRESHOLD_TIGHT_NANOS
- ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> TIME_THRESHOLD_ALL_NANOS
- ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE -> TIME_THRESHOLD_LAX_NANOS
- ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> TIME_THRESHOLD_TIGHT_NANOS
- ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> TIME_THRESHOLD_ALL_NANOS
- else -> return
- })
+ trimInactiveData(
+ threshold =
+ when (level) {
+ ComponentCallbacks2.TRIM_MEMORY_BACKGROUND -> TIME_THRESHOLD_LAX_NANOS
+ ComponentCallbacks2.TRIM_MEMORY_MODERATE -> TIME_THRESHOLD_TIGHT_NANOS
+ ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> TIME_THRESHOLD_ALL_NANOS
+ ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE -> TIME_THRESHOLD_LAX_NANOS
+ ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> TIME_THRESHOLD_TIGHT_NANOS
+ ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> TIME_THRESHOLD_ALL_NANOS
+ else -> return
+ }
+ )
}
override fun onLowMemory() {
@@ -111,9 +109,7 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon
}
fun invalidateSingle(key: K) {
- synchronized(lock) {
- data.remove(key)
- }
+ synchronized(lock) { data.remove(key) }
}
private fun trimInactiveData(threshold: Long) {
@@ -127,8 +123,8 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon
}
/**
- * Interface which describes an object which can track how long it has been inactive, and if
- * it has any observers.
+ * Interface which describes an object which can track how long it has been inactive, and if it
+ * has any observers.
*/
interface InactiveTimekeeper {
@@ -156,8 +152,8 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon
* invalidating all values tied to a package. Expects key to be a pair or triple, with the package
* name as the first value of the key.
*/
-abstract class DataRepositoryForPackage<K, V : DataRepository.InactiveTimekeeper>
- : DataRepository<K, V>() {
+abstract class DataRepositoryForPackage<K, V : DataRepository.InactiveTimekeeper> :
+ DataRepository<K, V>() {
/**
* Invalidates every value with the packageName in the key.
@@ -175,9 +171,7 @@ abstract class DataRepositoryForPackage<K, V : DataRepository.InactiveTimekeeper
}
}
-/**
- * A convenience to retrieve data from a repository with a composite key
- */
+/** A convenience to retrieve data from a repository with a composite key */
operator fun <K1, K2, V : DataRepository.InactiveTimekeeper> DataRepository<Pair<K1, K2>, V>.get(
k1: K1,
k2: K2
@@ -185,14 +179,10 @@ operator fun <K1, K2, V : DataRepository.InactiveTimekeeper> DataRepository<Pair
return get(k1 to k2)
}
-/**
- * A convenience to retrieve data from a repository with a composite key
- */
-operator fun <K1, K2, K3, V : DataRepository.InactiveTimekeeper>
- DataRepository<Triple<K1, K2, K3>, V>.get(
- k1: K1,
- k2: K2,
- k3: K3
- ): V {
+/** A convenience to retrieve data from a repository with a composite key */
+operator fun <K1, K2, K3, V : DataRepository.InactiveTimekeeper> DataRepository<
+ Triple<K1, K2, K3>, V
+>
+ .get(k1: K1, k2: K2, k3: K3): V {
return get(Triple(k1, k2, k3))
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/DisabledPrintServicesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/DisabledPrintServicesLiveData.kt
index 3abb20564..4d9c2574b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/DisabledPrintServicesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/DisabledPrintServicesLiveData.kt
@@ -30,28 +30,26 @@ import kotlinx.coroutines.Job
* @param app The current application
* @param user The user the services should be determined for
*/
-class DisabledPrintServicesLiveData(
- private val app: Application,
- private val user: UserHandle
-) : SmartAsyncMediatorLiveData<List<String>>() {
+class DisabledPrintServicesLiveData(private val app: Application, private val user: UserHandle) :
+ SmartAsyncMediatorLiveData<List<String>>() {
override suspend fun loadDataAndPostValue(job: Job) {
if (job.isCancelled) {
return
}
- val packageNames = Settings.Secure.getString(
- Utils.getUserContext(app, user).contentResolver, SETTING)
+ val packageNames =
+ Settings.Secure.getString(Utils.getUserContext(app, user).contentResolver, SETTING)
?.split(":")
?.map { pkgOrComponent ->
if ('/' in pkgOrComponent) {
- ComponentName.unflattenFromString(pkgOrComponent)
- ?.packageName
- ?: pkgOrComponent
+ ComponentName.unflattenFromString(pkgOrComponent)?.packageName
+ ?: pkgOrComponent
} else {
pkgOrComponent
}
- } ?: emptyList()
+ }
+ ?: emptyList()
postValue(packageNames)
}
@@ -61,8 +59,7 @@ class DisabledPrintServicesLiveData(
*
* <p> Key value is a user, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<UserHandle,
- DisabledPrintServicesLiveData>() {
+ companion object : DataRepositoryForPackage<UserHandle, DisabledPrintServicesLiveData>() {
/* Settings.Secure.DISABLED_PRINT_SERVICES */
private const val SETTING = "disabled_print_services"
@@ -70,4 +67,4 @@ class DisabledPrintServicesLiveData(
return DisabledPrintServicesLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledAccessibilityServicesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledAccessibilityServicesLiveData.kt
index 2b1391a98..9717d949c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledAccessibilityServicesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledAccessibilityServicesLiveData.kt
@@ -21,8 +21,8 @@ import android.app.Application
import android.os.UserHandle
import android.view.accessibility.AccessibilityManager
import com.android.permissioncontroller.PermissionControllerApplication
-import com.android.permissioncontroller.permission.utils.componentInfo
import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.permission.utils.componentInfo
import kotlinx.coroutines.Job
/**
@@ -42,7 +42,8 @@ class EnabledAccessibilityServicesLiveData(
return
}
- val packageNames = Utils.getUserContext(app, user)
+ val packageNames =
+ Utils.getUserContext(app, user)
.getSystemService(AccessibilityManager::class.java)!!
.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK)
.map { info: AccessibilityServiceInfo ->
@@ -62,10 +63,10 @@ class EnabledAccessibilityServicesLiveData(
*
* <p> Key value is a user, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<UserHandle,
- EnabledAccessibilityServicesLiveData>() {
+ companion object :
+ DataRepositoryForPackage<UserHandle, EnabledAccessibilityServicesLiveData>() {
override fun newValue(key: UserHandle): EnabledAccessibilityServicesLiveData {
return EnabledAccessibilityServicesLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDeviceAdminsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDeviceAdminsLiveData.kt
index 60dcf59b0..eebcac06f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDeviceAdminsLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDeviceAdminsLiveData.kt
@@ -29,17 +29,16 @@ import kotlinx.coroutines.Job
* @param app The current application
* @param user The user the services should be determined for
*/
-class EnabledDeviceAdminsLiveData(
- private val app: Application,
- private val user: UserHandle
-) : SmartAsyncMediatorLiveData<List<String>>() {
+class EnabledDeviceAdminsLiveData(private val app: Application, private val user: UserHandle) :
+ SmartAsyncMediatorLiveData<List<String>>() {
override suspend fun loadDataAndPostValue(job: Job) {
if (job.isCancelled) {
return
}
- val packageNames = Utils.getUserContext(app, user)
+ val packageNames =
+ Utils.getUserContext(app, user)
.getSystemService(DevicePolicyManager::class.java)!!
.activeAdmins
?.map { component -> component.packageName }
@@ -53,10 +52,9 @@ class EnabledDeviceAdminsLiveData(
*
* <p> Key value is a user, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<UserHandle,
- EnabledDeviceAdminsLiveData>() {
+ companion object : DataRepositoryForPackage<UserHandle, EnabledDeviceAdminsLiveData>() {
override fun newValue(key: UserHandle): EnabledDeviceAdminsLiveData {
return EnabledDeviceAdminsLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDreamServicesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDreamServicesLiveData.kt
index 200384aab..9b8554e80 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDreamServicesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDreamServicesLiveData.kt
@@ -30,28 +30,26 @@ import kotlinx.coroutines.Job
* @param app The current application
* @param user The user the services should be determined for
*/
-class EnabledDreamServicesLiveData(
- private val app: Application,
- private val user: UserHandle
-) : SmartAsyncMediatorLiveData<List<String>>() {
+class EnabledDreamServicesLiveData(private val app: Application, private val user: UserHandle) :
+ SmartAsyncMediatorLiveData<List<String>>() {
override suspend fun loadDataAndPostValue(job: Job) {
if (job.isCancelled) {
return
}
- val packageNames = Settings.Secure.getString(
- Utils.getUserContext(app, user).contentResolver, SETTING)
+ val packageNames =
+ Settings.Secure.getString(Utils.getUserContext(app, user).contentResolver, SETTING)
?.split(",")
?.map { pkgOrComponent ->
if ('/' in pkgOrComponent) {
- ComponentName.unflattenFromString(pkgOrComponent)
- ?.packageName
- ?: pkgOrComponent
+ ComponentName.unflattenFromString(pkgOrComponent)?.packageName
+ ?: pkgOrComponent
} else {
pkgOrComponent
}
- } ?: emptyList()
+ }
+ ?: emptyList()
postValue(packageNames)
}
@@ -61,8 +59,7 @@ class EnabledDreamServicesLiveData(
*
* <p> Key value is a user, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<UserHandle,
- EnabledDreamServicesLiveData>() {
+ companion object : DataRepositoryForPackage<UserHandle, EnabledDreamServicesLiveData>() {
/* Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
private const val SETTING = "enabled_notification_listeners"
@@ -70,4 +67,4 @@ class EnabledDreamServicesLiveData(
return EnabledDreamServicesLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledInputMethodsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledInputMethodsLiveData.kt
index d0d2783ab..e48c432d8 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledInputMethodsLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledInputMethodsLiveData.kt
@@ -30,22 +30,19 @@ import kotlinx.coroutines.Job
* @param app The current application
* @param user The user the services should be determined for
*/
-class EnabledInputMethodsLiveData(
- private val app: Application,
- private val user: UserHandle
-) : SmartAsyncMediatorLiveData<List<String>>() {
+class EnabledInputMethodsLiveData(private val app: Application, private val user: UserHandle) :
+ SmartAsyncMediatorLiveData<List<String>>() {
override suspend fun loadDataAndPostValue(job: Job) {
if (job.isCancelled) {
return
}
- val packageNames = Utils.getUserContext(app, user)
+ val packageNames =
+ Utils.getUserContext(app, user)
.getSystemService(InputMethodManager::class.java)!!
.enabledInputMethodList
- .map { info: InputMethodInfo ->
- info.component.packageName
- }
+ .map { info: InputMethodInfo -> info.component.packageName }
postValue(packageNames)
}
@@ -55,10 +52,9 @@ class EnabledInputMethodsLiveData(
*
* <p> Key value is a user, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<UserHandle,
- EnabledInputMethodsLiveData>() {
+ companion object : DataRepositoryForPackage<UserHandle, EnabledInputMethodsLiveData>() {
override fun newValue(key: UserHandle): EnabledInputMethodsLiveData {
return EnabledInputMethodsLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledNotificationListenersLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledNotificationListenersLiveData.kt
index f5c5d4bf1..7dd1567e4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledNotificationListenersLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledNotificationListenersLiveData.kt
@@ -40,20 +40,22 @@ class EnabledNotificationListenersLiveData(
return
}
- val packageNames = Settings.Secure.getString(
- Utils.getUserContext(app, user).contentResolver,
- /* Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
- "enabled_notification_listeners")
+ val packageNames =
+ Settings.Secure.getString(
+ Utils.getUserContext(app, user).contentResolver,
+ /* Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
+ "enabled_notification_listeners"
+ )
?.split(":")
?.map { pkgOrComponent ->
if ('/' in pkgOrComponent) {
- ComponentName.unflattenFromString(pkgOrComponent)
- ?.packageName
- ?: pkgOrComponent
+ ComponentName.unflattenFromString(pkgOrComponent)?.packageName
+ ?: pkgOrComponent
} else {
pkgOrComponent
}
- } ?: emptyList()
+ }
+ ?: emptyList()
postValue(packageNames)
}
@@ -63,10 +65,10 @@ class EnabledNotificationListenersLiveData(
*
* <p> Key value is a user, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<UserHandle,
- EnabledNotificationListenersLiveData>() {
+ companion object :
+ DataRepositoryForPackage<UserHandle, EnabledNotificationListenersLiveData>() {
override fun newValue(key: UserHandle): EnabledNotificationListenersLiveData {
return EnabledNotificationListenersLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/ForegroundPermNamesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/ForegroundPermNamesLiveData.kt
index 1471ed15f..513f5fc4e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/ForegroundPermNamesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/ForegroundPermNamesLiveData.kt
@@ -39,11 +39,12 @@ object ForegroundPermNamesLiveData : SmartAsyncMediatorLiveData<Map<String, List
val systemGroups = PermissionMapping.getPlatformPermissionGroups()
val permMap = mutableMapOf<String, MutableList<String>>()
for (groupName in systemGroups) {
- val permInfos = try {
- Utils.getInstalledRuntimePermissionInfosForGroup(app.packageManager, groupName)
- } catch (e: PackageManager.NameNotFoundException) {
- continue
- }
+ val permInfos =
+ try {
+ Utils.getInstalledRuntimePermissionInfosForGroup(app.packageManager, groupName)
+ } catch (e: PackageManager.NameNotFoundException) {
+ continue
+ }
for (permInfo in permInfos) {
val backgroundPerm: String? = permInfo.backgroundPermission
if (backgroundPerm != null) {
@@ -54,4 +55,4 @@ object ForegroundPermNamesLiveData : SmartAsyncMediatorLiveData<Map<String, List
}
postValue(permMap)
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt
index 0b27dbff0..4a2d3b68a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt
@@ -34,14 +34,13 @@ import kotlinx.coroutines.Job
/**
* A liveData which tracks all packages in the system which have full file permissions, as
* represented by the OPSTR_LEGACY_STORAGE app op, not just media-only storage permissions.
- *
*/
object FullStoragePermissionAppsLiveData :
SmartAsyncMediatorLiveData<List<FullStoragePermissionAppsLiveData.FullStoragePackageState>>() {
private val app: Application = PermissionControllerApplication.get()
- private val standardPermGroupsPackagesLiveData = PermGroupsPackagesLiveData.get(
- customGroups = false)
+ private val standardPermGroupsPackagesLiveData =
+ PermGroupsPackagesLiveData.get(customGroups = false)
data class FullStoragePackageState(
val packageName: String,
@@ -51,12 +50,8 @@ object FullStoragePermissionAppsLiveData :
)
init {
- addSource(standardPermGroupsPackagesLiveData) {
- updateAsync()
- }
- addSource(AllPackageInfosLiveData) {
- updateAsync()
- }
+ addSource(standardPermGroupsPackagesLiveData) { updateAsync() }
+ addSource(AllPackageInfosLiveData) { updateAsync() }
}
override suspend fun loadDataAndPostValue(job: Job) {
@@ -65,14 +60,16 @@ object FullStoragePermissionAppsLiveData :
val fullStoragePackages = mutableListOf<FullStoragePackageState>()
for ((user, packageInfoList) in AllPackageInfosLiveData.value ?: emptyMap()) {
- val userPackages = packageInfoList.filter {
- storagePackages.contains(it.packageName to user) ||
- it.requestedPermissions.contains(MANAGE_EXTERNAL_STORAGE)
- }
+ val userPackages =
+ packageInfoList.filter {
+ storagePackages.contains(it.packageName to user) ||
+ it.requestedPermissions.contains(MANAGE_EXTERNAL_STORAGE)
+ }
for (packageInfo in userPackages) {
- fullStoragePackages.add(getFullStorageStateForPackage(appOpsManager,
- packageInfo, user) ?: continue)
+ fullStoragePackages.add(
+ getFullStorageStateForPackage(appOpsManager, packageInfo, user) ?: continue
+ )
}
}
@@ -85,9 +82,8 @@ object FullStoragePermissionAppsLiveData :
* @param appOpsManager The App Ops manager to use, if applicable
* @param packageInfo The package whose state is to be determined
* @param userHandle A preexisting UserHandle object to use. Otherwise, one will be created
- *
* @return the FullStoragePackageState for the package, or null if the package does not request
- * full storage permissions
+ * full storage permissions
*/
fun getFullStorageStateForPackage(
appOpsManager: AppOpsManager,
@@ -97,31 +93,51 @@ object FullStoragePermissionAppsLiveData :
val sdk = packageInfo.targetSdkVersion
val user = userHandle ?: UserHandle.getUserHandleForUid(packageInfo.uid)
if (sdk < Build.VERSION_CODES.P) {
- return FullStoragePackageState(packageInfo.packageName, user,
- isLegacy = true, isGranted = true)
- } else if (sdk <= Build.VERSION_CODES.Q &&
- appOpsManager.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, packageInfo.uid,
- packageInfo.packageName) == MODE_ALLOWED) {
- return FullStoragePackageState(packageInfo.packageName, user,
- isLegacy = true, isGranted = true)
+ return FullStoragePackageState(
+ packageInfo.packageName,
+ user,
+ isLegacy = true,
+ isGranted = true
+ )
+ } else if (
+ sdk <= Build.VERSION_CODES.Q &&
+ appOpsManager.unsafeCheckOpNoThrow(
+ OPSTR_LEGACY_STORAGE,
+ packageInfo.uid,
+ packageInfo.packageName
+ ) == MODE_ALLOWED
+ ) {
+ return FullStoragePackageState(
+ packageInfo.packageName,
+ user,
+ isLegacy = true,
+ isGranted = true
+ )
}
if (MANAGE_EXTERNAL_STORAGE in packageInfo.requestedPermissions) {
- val mode = appOpsManager.unsafeCheckOpNoThrow(OPSTR_MANAGE_EXTERNAL_STORAGE,
- packageInfo.uid, packageInfo.packageName)
- val granted = mode == MODE_ALLOWED || mode == MODE_FOREGROUND ||
- (mode == MODE_DEFAULT &&
- MANAGE_EXTERNAL_STORAGE in packageInfo.grantedPermissions)
- return FullStoragePackageState(packageInfo.packageName, user,
- isLegacy = false, isGranted = granted)
+ val mode =
+ appOpsManager.unsafeCheckOpNoThrow(
+ OPSTR_MANAGE_EXTERNAL_STORAGE,
+ packageInfo.uid,
+ packageInfo.packageName
+ )
+ val granted =
+ mode == MODE_ALLOWED ||
+ mode == MODE_FOREGROUND ||
+ (mode == MODE_DEFAULT &&
+ MANAGE_EXTERNAL_STORAGE in packageInfo.grantedPermissions)
+ return FullStoragePackageState(
+ packageInfo.packageName,
+ user,
+ isLegacy = false,
+ isGranted = granted
+ )
}
return null
}
- /**
- * Recalculate the LiveData
- * TODO ntmyren: Make livedata properly observe app ops
- */
+ /** Recalculate the LiveData TODO ntmyren: Make livedata properly observe app ops */
fun recalculate() {
updateAsync()
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/HasIntentAction.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/HasIntentAction.kt
index 91557e441..1c0325ec6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/HasIntentAction.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/HasIntentAction.kt
@@ -16,9 +16,7 @@
package com.android.permissioncontroller.permission.data
-/**
- * An interface for classes that have an [Intent] action
- */
+/** An interface for classes that have an [Intent] action */
interface HasIntentAction {
val intentAction: String
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt
index 887998a2e..9fdb8411a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt
@@ -25,17 +25,12 @@ import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.utils.Utils.getUserContext
import kotlinx.coroutines.Job
-/**
- * Tracks which packages have been hibernated.
- */
-object HibernatedPackagesLiveData
- : SmartAsyncMediatorLiveData<Set<Pair<String, UserHandle>>>() {
+/** Tracks which packages have been hibernated. */
+object HibernatedPackagesLiveData : SmartAsyncMediatorLiveData<Set<Pair<String, UserHandle>>>() {
private val LOG_TAG = HibernatedPackagesLiveData::class.java.simpleName
init {
- addSource(AllPackageInfosLiveData) {
- update()
- }
+ addSource(AllPackageInfosLiveData) { update() }
}
override suspend fun loadDataAndPostValue(job: Job) {
@@ -57,8 +52,10 @@ object HibernatedPackagesLiveData
hibernatingPackages.add(pkg.packageName to user)
}
} catch (e: Exception) {
- DumpableLog.e(LOG_TAG,
- "Failed to get hibernation state of package: ${pkg.packageName}")
+ DumpableLog.e(
+ LOG_TAG,
+ "Failed to get hibernation state of package: ${pkg.packageName}"
+ )
}
}
}
@@ -69,25 +66,23 @@ object HibernatedPackagesLiveData
}
}
-private val hibernatedOrRevokedPackagesLiveData = object
- : SmartUpdateMediatorLiveData<Set<Pair<String, UserHandle>>>() {
+private val hibernatedOrRevokedPackagesLiveData =
+ object : SmartUpdateMediatorLiveData<Set<Pair<String, UserHandle>>>() {
- init {
- addSource(AutoRevokedPackagesLiveData) {
- update()
- }
- addSource(HibernatedPackagesLiveData) {
- update()
+ init {
+ addSource(AutoRevokedPackagesLiveData) { update() }
+ addSource(HibernatedPackagesLiveData) { update() }
}
- }
- override fun onUpdate() {
- if (!AutoRevokedPackagesLiveData.isInitialized ||
- !HibernatedPackagesLiveData.isInitialized) {
- return
+ override fun onUpdate() {
+ if (
+ !AutoRevokedPackagesLiveData.isInitialized ||
+ !HibernatedPackagesLiveData.isInitialized
+ ) {
+ return
+ }
+ value = AutoRevokedPackagesLiveData.value!!.keys + HibernatedPackagesLiveData.value!!
}
- value = AutoRevokedPackagesLiveData.value!!.keys + HibernatedPackagesLiveData.value!!
}
-}
val unusedHibernatedOrRevokedPackagesLiveData =
- UnusedPackagesLiveData(hibernatedOrRevokedPackagesLiveData) \ No newline at end of file
+ UnusedPackagesLiveData(hibernatedOrRevokedPackagesLiveData)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt
index 606562641..75d965d02 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt
@@ -34,6 +34,7 @@ import com.android.permissioncontroller.hibernation.isPackageHibernationExemptBy
import com.android.permissioncontroller.hibernation.isPackageHibernationExemptByUser
import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS
import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState
+import com.android.permissioncontroller.permission.service.AUTO_REVOKE_EXEMPT_PERMISSIONS
import kotlinx.coroutines.Job
/**
@@ -43,14 +44,14 @@ import kotlinx.coroutines.Job
* @param packageName The package name whose state we want
* @param user The user for whom we want the package
*/
-class HibernationSettingStateLiveData private constructor(
+class HibernationSettingStateLiveData
+private constructor(
private val app: Application,
private val packageName: String,
private val user: UserHandle
) : SmartAsyncMediatorLiveData<HibernationSettingState>(), AppOpsManager.OnOpChangedListener {
- private val packagePermsLiveData =
- PackagePermissionsLiveData[packageName, user]
+ private val packagePermsLiveData = PackagePermissionsLiveData[packageName, user]
private val packageLiveData = LightPackageInfoLiveData[packageName, user]
private val permStateLiveDatas = mutableMapOf<String, PermStateLiveData>()
private val exemptServicesLiveData = ExemptServicesLiveData[user]
@@ -64,26 +65,19 @@ class HibernationSettingStateLiveData private constructor(
private var gotPastIsSystemExempt: Boolean = false
init {
- addSource(packagePermsLiveData) {
- update()
- }
- addSource(packageLiveData) {
- update()
- }
- addSource(exemptServicesLiveData) {
- update()
- }
- addSource(HibernationEnabledLiveData) {
- update()
- }
- Handler(app.mainLooper).postDelayed({
- logState()
- }, DELAY_MS)
+ addSource(packagePermsLiveData) { update() }
+ addSource(packageLiveData) { update() }
+ addSource(exemptServicesLiveData) { update() }
+ addSource(HibernationEnabledLiveData) { update() }
+ Handler(app.mainLooper).postDelayed({ logState() }, DELAY_MS)
}
override suspend fun loadDataAndPostValue(job: Job) {
- if (!packageLiveData.isInitialized || !packagePermsLiveData.isInitialized ||
- !exemptServicesLiveData.isInitialized) {
+ if (
+ !packageLiveData.isInitialized ||
+ !packagePermsLiveData.isInitialized ||
+ !exemptServicesLiveData.isInitialized
+ ) {
return
}
@@ -103,21 +97,30 @@ class HibernationSettingStateLiveData private constructor(
val exemptBySystem = isPackageHibernationExemptBySystem(packageInfo, user)
val exemptByUser = isPackageHibernationExemptByUser(app, packageInfo)
- val eligibility = when {
- !exemptBySystem && !exemptByUser -> HIBERNATION_ELIGIBILITY_ELIGIBLE
- exemptBySystem -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM
- else -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER
- }
+ val eligibility =
+ when {
+ !exemptBySystem && !exemptByUser -> HIBERNATION_ELIGIBILITY_ELIGIBLE
+ exemptBySystem -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM
+ else -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER
+ }
gotPastIsUserExempt = true
val revocableGroups = mutableListOf<String>()
if (!isPackageHibernationExemptBySystem(packageInfo, user)) {
gotPastIsSystemExempt = true
permStateLiveDatas.forEach { (groupName, liveData) ->
- val default = liveData.value?.any { (_, permState) ->
- permState.permFlags and (FLAG_PERMISSION_GRANTED_BY_DEFAULT or
- FLAG_PERMISSION_GRANTED_BY_ROLE) != 0
- } ?: false
- if (!default) {
+ val default =
+ liveData.value?.any { (_, permState) ->
+ permState.permFlags and
+ (FLAG_PERMISSION_GRANTED_BY_DEFAULT or
+ FLAG_PERMISSION_GRANTED_BY_ROLE) != 0
+ }
+ ?: false
+ val allExempt =
+ liveData.value?.all { (permName, _) ->
+ permName in AUTO_REVOKE_EXEMPT_PERMISSIONS
+ }
+ ?: false
+ if (!default && !allExempt) {
revocableGroups.add(groupName)
}
}
@@ -148,29 +151,45 @@ class HibernationSettingStateLiveData private constructor(
if (!isStale) {
return
}
- Log.i(LOG_TAG, "overall state: isStale:$isStale, isInitialized:$isInitialized, " +
+ Log.i(
+ LOG_TAG,
+ "overall state: isStale:$isStale, isInitialized:$isInitialized, " +
"value:$value, got perm LiveDatas:$gotPermLiveDatas, " +
- "got isUserExempt$gotPastIsUserExempt, got isSystemExempt$gotPastIsSystemExempt")
- Log.i(LOG_TAG, "packagePermsLivedata isStale:${packagePermsLiveData.isStale}, " +
- "isInitialized:${packagePermsLiveData.isInitialized}")
- Log.i(LOG_TAG, "ExemptServicesLiveData isStale:${exemptServicesLiveData.isStale}, " +
- "isInitialized:${exemptServicesLiveData.isInitialized}")
+ "got isUserExempt$gotPastIsUserExempt, got isSystemExempt$gotPastIsSystemExempt"
+ )
+ Log.i(
+ LOG_TAG,
+ "packagePermsLivedata isStale:${packagePermsLiveData.isStale}, " +
+ "isInitialized:${packagePermsLiveData.isInitialized}"
+ )
+ Log.i(
+ LOG_TAG,
+ "ExemptServicesLiveData isStale:${exemptServicesLiveData.isStale}, " +
+ "isInitialized:${exemptServicesLiveData.isInitialized}"
+ )
Log.i(LOG_TAG, "HibernationEnabledLivedata value:${HibernationEnabledLiveData.value}")
for ((group, liveData) in permStateLiveDatas) {
- Log.i(LOG_TAG, "permStateLivedata $group isStale:${liveData.isStale}, " +
- "isInitialized:${liveData.isInitialized}")
+ Log.i(
+ LOG_TAG,
+ "permStateLivedata $group isStale:${liveData.isStale}, " +
+ "isInitialized:${liveData.isInitialized}"
+ )
}
}
/**
* Repository for HibernationSettingStateLiveDatas.
+ *
* <p> Key value is a pair of string package name and UserHandle, value is its corresponding
* LiveData.
*/
- companion object : DataRepositoryForPackage<Pair<String, UserHandle>,
- HibernationSettingStateLiveData>() {
+ companion object :
+ DataRepositoryForPackage<Pair<String, UserHandle>, HibernationSettingStateLiveData>() {
override fun newValue(key: Pair<String, UserHandle>): HibernationSettingStateLiveData {
- return HibernationSettingStateLiveData(PermissionControllerApplication.get(),
- key.first, key.second)
+ return HibernationSettingStateLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LauncherPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LauncherPackagesLiveData.kt
index b512c7e4a..94bf230d7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/LauncherPackagesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LauncherPackagesLiveData.kt
@@ -18,39 +18,43 @@
package com.android.permissioncontroller.permission.data
import android.content.Intent
+import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE
import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-import android.content.pm.PackageManager.FEATURE_LEANBACK
import com.android.permissioncontroller.PermissionControllerApplication
import kotlinx.coroutines.Job
-/**
- * A livedata which stores a list of package names of packages which have launcher icons.
- */
-object LauncherPackagesLiveData : SmartAsyncMediatorLiveData<Set<String>>(),
- PackageBroadcastReceiver.PackageBroadcastListener {
+/** A livedata which stores a list of package names of packages which have launcher icons. */
+object LauncherPackagesLiveData :
+ SmartAsyncMediatorLiveData<Set<String>>(), PackageBroadcastReceiver.PackageBroadcastListener {
- private val LAUNCHER_INTENT = Intent(Intent.ACTION_MAIN, null)
- .addCategory(Intent.CATEGORY_LAUNCHER)
+ private val LAUNCHER_INTENT =
+ Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER)
// On ATV some apps may have a leanback launcher icon but no regular launcher icon
- private val LEANBACK_LAUNCHER_INTENT = Intent(Intent.ACTION_MAIN, null)
- .addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER)
+ private val LEANBACK_LAUNCHER_INTENT =
+ Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER)
override suspend fun loadDataAndPostValue(job: Job) {
val launcherPkgs = mutableSetOf<String>()
loadPkgsFromIntent(launcherPkgs, LAUNCHER_INTENT)
- if (PermissionControllerApplication.get().packageManager
- .hasSystemFeature(FEATURE_LEANBACK)) {
+ if (
+ PermissionControllerApplication.get().packageManager.hasSystemFeature(FEATURE_LEANBACK)
+ ) {
loadPkgsFromIntent(launcherPkgs, LEANBACK_LAUNCHER_INTENT)
}
postValue(launcherPkgs)
}
private fun loadPkgsFromIntent(launcherPkgs: MutableSet<String>, intent: Intent) {
- for (info in PermissionControllerApplication.get().packageManager.queryIntentActivities(
- intent, MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE)) {
+ for (info in
+ PermissionControllerApplication.get()
+ .packageManager
+ .queryIntentActivities(
+ intent,
+ MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE
+ )) {
launcherPkgs.add(info.activityInfo.packageName)
}
}
@@ -69,4 +73,4 @@ object LauncherPackagesLiveData : SmartAsyncMediatorLiveData<Set<String>>(),
super.onInactive()
PackageBroadcastReceiver.removeAllCallback(this)
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt
index 3621319a6..db606f68d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt
@@ -39,7 +39,8 @@ import com.android.permissioncontroller.permission.utils.Utils.OS_PKG
* @param permGroupName The name of the permission group
* @param user The user of the package
*/
-class LightAppPermGroupLiveData private constructor(
+class LightAppPermGroupLiveData
+private constructor(
private val app: Application,
private val packageName: String,
private val permGroupName: String,
@@ -55,13 +56,15 @@ class LightAppPermGroupLiveData private constructor(
private val fgPermNamesLiveData = ForegroundPermNamesLiveData
init {
- isSpecialLocation = LocationUtils.isLocationGroupAndProvider(app,
- permGroupName, packageName) ||
- LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName)
+ isSpecialLocation =
+ LocationUtils.isLocationGroupAndProvider(app, permGroupName, packageName) ||
+ LocationUtils.isLocationGroupAndControllerExtraPackage(
+ app,
+ permGroupName,
+ packageName
+ )
- addSource(fgPermNamesLiveData) {
- update()
- }
+ addSource(fgPermNamesLiveData) { update() }
val key = Triple(packageName, permGroupName, user)
@@ -100,8 +103,10 @@ class LightAppPermGroupLiveData private constructor(
val allForegroundPerms = fgPermNamesLiveData.value ?: return
// Do not allow toggling pre-M custom perm groups
- if (packageInfo.targetSdkVersion < Build.VERSION_CODES.M &&
- permGroup.groupInfo.packageName != OS_PKG) {
+ if (
+ packageInfo.targetSdkVersion < Build.VERSION_CODES.M &&
+ permGroup.groupInfo.packageName != OS_PKG
+ ) {
value = LightAppPermGroup(packageInfo, permGroup.groupInfo, emptyMap())
return
}
@@ -110,8 +115,8 @@ class LightAppPermGroupLiveData private constructor(
for ((permName, permState) in permStates) {
val permInfo = permGroup.permissionInfos[permName] ?: continue
val foregroundPerms = allForegroundPerms[permName]
- permissionMap[permName] = LightPermission(packageInfo, permInfo, permState,
- foregroundPerms)
+ permissionMap[permName] =
+ LightPermission(packageInfo, permInfo, permState, foregroundPerms)
}
// Determine if this app permission group is a special location package or provider
@@ -119,17 +124,24 @@ class LightAppPermGroupLiveData private constructor(
val userContext = Utils.getUserContext(app, user)
if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) {
specialLocationGrant = LocationUtils.isLocationEnabled(userContext)
- } else if (LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName,
- packageName)) {
+ } else if (
+ LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName)
+ ) {
// The permission of the extra location controller package is determined by the status
// of the controller package itself.
- specialLocationGrant = LocationUtils.isExtraLocationControllerPackageEnabled(
- userContext)
+ specialLocationGrant =
+ LocationUtils.isExtraLocationControllerPackageEnabled(userContext)
}
val hasInstallToRuntimeSplit = hasInstallToRuntimeSplit(packageInfo, permissionMap)
- value = LightAppPermGroup(packageInfo, permGroup.groupInfo, permissionMap,
- hasInstallToRuntimeSplit, specialLocationGrant)
+ value =
+ LightAppPermGroup(
+ packageInfo,
+ permGroup.groupInfo,
+ permissionMap,
+ hasInstallToRuntimeSplit,
+ specialLocationGrant
+ )
}
/**
@@ -147,12 +159,13 @@ class LightAppPermGroupLiveData private constructor(
for (spi in permissionManager.splitPermissions) {
val splitPerm = spi.splitPermission
- val pi = try {
- app.packageManager.getPermissionInfo(splitPerm, 0)
- } catch (e: PackageManager.NameNotFoundException) {
- Log.w(LOG_TAG, "No such permission: $splitPerm", e)
- continue
- }
+ val pi =
+ try {
+ app.packageManager.getPermissionInfo(splitPerm, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(LOG_TAG, "No such permission: $splitPerm", e)
+ continue
+ }
// Skip if split permission is not "install" permission.
if (pi.protection != PermissionInfo.PROTECTION_NORMAL) {
@@ -199,15 +212,19 @@ class LightAppPermGroupLiveData private constructor(
/**
* Repository for AppPermGroupLiveDatas.
+ *
* <p> Key value is a triple of string package name, string permission group name, and
* UserHandle, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>,
- LightAppPermGroupLiveData>() {
- override fun newValue(key: Triple<String, String, UserHandle>):
- LightAppPermGroupLiveData {
- return LightAppPermGroupLiveData(PermissionControllerApplication.get(),
- key.first, key.second, key.third)
+ companion object :
+ DataRepositoryForPackage<Triple<String, String, UserHandle>, LightAppPermGroupLiveData>() {
+ override fun newValue(key: Triple<String, String, UserHandle>): LightAppPermGroupLiveData {
+ return LightAppPermGroupLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ key.third
+ )
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt
index 27681cd90..e657a6869 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt
@@ -37,11 +37,13 @@ import kotlinx.coroutines.Job
* @param packageName The name of the package this LiveData will watch for mode changes for
* @param user The user for whom the packageInfo will be defined
*/
-class LightPackageInfoLiveData private constructor(
+class LightPackageInfoLiveData
+private constructor(
private val app: Application,
private val packageName: String,
private val user: UserHandle
-) : SmartAsyncMediatorLiveData<LightPackageInfo?>(alwaysUpdateOnActive = false),
+) :
+ SmartAsyncMediatorLiveData<LightPackageInfo?>(alwaysUpdateOnActive = false),
PackageBroadcastReceiver.PackageBroadcastListener,
PermissionListenerMultiplexer.PermissionChangeCallback {
@@ -49,13 +51,9 @@ class LightPackageInfoLiveData private constructor(
private val userPackagesLiveData = UserPackageInfosLiveData[user]
private var uid: Int? = null
- /**
- * The currently registered UID on which this LiveData is listening for permission changes.
- */
+ /** The currently registered UID on which this LiveData is listening for permission changes. */
private var registeredUid: Int? = null
- /**
- * Whether or not this package livedata is watching the UserPackageInfosLiveData
- */
+ /** Whether or not this package livedata is watching the UserPackageInfosLiveData */
private var watchingUserPackagesLiveData: Boolean = false
/**
@@ -73,8 +71,11 @@ class LightPackageInfoLiveData private constructor(
uid = packageInfo.uid
if (hasActiveObservers()) {
- PermissionListenerMultiplexer.addOrReplaceCallback(registeredUid,
- packageInfo.uid, this)
+ PermissionListenerMultiplexer.addOrReplaceCallback(
+ registeredUid,
+ packageInfo.uid,
+ this
+ )
registeredUid = uid
}
}
@@ -95,30 +96,37 @@ class LightPackageInfoLiveData private constructor(
if (job.isCancelled) {
return
}
- postValue(try {
- var flags = PackageManager.GET_PERMISSIONS
- if (SdkLevel.isAtLeastS()) {
- flags = flags or PackageManager.GET_ATTRIBUTIONS
- }
+ postValue(
+ try {
+ var flags = PackageManager.GET_PERMISSIONS
+ if (SdkLevel.isAtLeastS()) {
+ flags = flags or PackageManager.GET_ATTRIBUTIONS
+ }
- LightPackageInfo(Utils.getUserContext(app, user).packageManager
- .getPackageInfo(packageName, flags))
- } catch (e: Exception) {
- if (e is PackageManager.NameNotFoundException) {
- Log.w(LOG_TAG, "Package \"$packageName\" not found for user $user")
- } else {
- val profiles = app.getSystemService(UserManager::class.java)!!.userProfiles
- Log.e(LOG_TAG, "Failed to create context for user $user. " +
- "User exists : ${user in profiles }", e)
+ LightPackageInfo(
+ Utils.getUserContext(app, user)
+ .packageManager
+ .getPackageInfo(packageName, flags)
+ )
+ } catch (e: Exception) {
+ if (e is PackageManager.NameNotFoundException) {
+ Log.w(LOG_TAG, "Package \"$packageName\" not found for user $user")
+ } else {
+ val profiles = app.getSystemService(UserManager::class.java)!!.userProfiles
+ Log.e(
+ LOG_TAG,
+ "Failed to create context for user $user. " +
+ "User exists : ${user in profiles }",
+ e
+ )
+ }
+ invalidateSingle(packageName to user)
+ null
}
- invalidateSingle(packageName to user)
- null
- })
+ )
}
- /**
- * Callback from the PermissionListener. Either deletes or generates package data.
- */
+ /** Callback from the PermissionListener. Either deletes or generates package data. */
override fun onPermissionChange() {
updateAsync()
}
@@ -131,8 +139,11 @@ class LightPackageInfoLiveData private constructor(
registeredUid = uid
PermissionListenerMultiplexer.addCallback(it, this)
}
- if (userPackagesLiveData.hasActiveObservers() && !watchingUserPackagesLiveData &&
- !userPackagesLiveData.permChangeStale) {
+ if (
+ userPackagesLiveData.hasActiveObservers() &&
+ !watchingUserPackagesLiveData &&
+ !userPackagesLiveData.permChangeStale
+ ) {
watchingUserPackagesLiveData = true
addSource(userPackagesLiveData, userPackageInfosObserver)
} else {
@@ -140,9 +151,8 @@ class LightPackageInfoLiveData private constructor(
}
}
- private val userPackageInfosObserver = Observer<List<LightPackageInfo>> {
- updateFromUserPackageInfosLiveData()
- }
+ private val userPackageInfosObserver =
+ Observer<List<LightPackageInfo>> { updateFromUserPackageInfosLiveData() }
@MainThread
private fun updateFromUserPackageInfosLiveData() {
@@ -183,14 +193,18 @@ class LightPackageInfoLiveData private constructor(
/**
* Repository for LightPackageInfoLiveDatas
+ *
* <p> Key value is a string package name and UserHandle pair, value is its corresponding
* LiveData.
*/
- companion object : DataRepositoryForPackage<Pair<String, UserHandle>,
- LightPackageInfoLiveData>() {
+ companion object :
+ DataRepositoryForPackage<Pair<String, UserHandle>, LightPackageInfoLiveData>() {
override fun newValue(key: Pair<String, UserHandle>): LightPackageInfoLiveData {
- return LightPackageInfoLiveData(PermissionControllerApplication.get(),
- key.first, key.second)
+ return LightPackageInfoLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt
index 5c559c0db..68aa55e14 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt
@@ -20,8 +20,8 @@ import android.app.Application
import android.content.pm.PackageManager
import android.util.Log
import com.android.permissioncontroller.PermissionControllerApplication
-import com.android.permissioncontroller.permission.utils.PermissionMapping.isRuntimePlatformPermission
import com.android.permissioncontroller.permission.model.livedatatypes.LightPermInfo
+import com.android.permissioncontroller.permission.utils.PermissionMapping.isRuntimePlatformPermission
import com.android.permissioncontroller.permission.utils.Utils.OS_PKG
import kotlinx.coroutines.Job
@@ -31,11 +31,9 @@ import kotlinx.coroutines.Job
* @param app current Application
* @param permissionName name of the permission this LiveData will watch for mode changes for
*/
-class LightPermInfoLiveData private constructor(
- private val app: Application,
- private val permissionName: String
-) : SmartAsyncMediatorLiveData<LightPermInfo>(),
- PackageBroadcastReceiver.PackageBroadcastListener {
+class LightPermInfoLiveData
+private constructor(private val app: Application, private val permissionName: String) :
+ SmartAsyncMediatorLiveData<LightPermInfo>(), PackageBroadcastReceiver.PackageBroadcastListener {
private val LOG_TAG = LightPermInfoLiveData::class.java.simpleName
@@ -67,13 +65,14 @@ class LightPermInfoLiveData private constructor(
return
}
- val newValue = try {
- LightPermInfo(app.packageManager.getPermissionInfo(permissionName, 0), null)
- } catch (e: PackageManager.NameNotFoundException) {
- Log.w(LOG_TAG, "Permission \"$permissionName\" not found")
- invalidateSingle(permissionName)
- null
- }
+ val newValue =
+ try {
+ LightPermInfo(app.packageManager.getPermissionInfo(permissionName, 0), null)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(LOG_TAG, "Permission \"$permissionName\" not found")
+ invalidateSingle(permissionName)
+ null
+ }
if (isImmutable()) {
stopListeningForChanges()
@@ -82,9 +81,7 @@ class LightPermInfoLiveData private constructor(
postValue(newValue)
}
- /**
- * @return if the permission state can never change
- */
+ /** @return if the permission state can never change */
private fun isImmutable(): Boolean {
// The os package never changes
value?.let {
@@ -97,9 +94,7 @@ class LightPermInfoLiveData private constructor(
return isRuntimePlatformPermission(permissionName)
}
- /**
- * Start listing for changes to this permission if needed
- */
+ /** Start listing for changes to this permission if needed */
private fun startListeningForChanges() {
if (!isListeningForChanges && !isImmutable()) {
isListeningForChanges = true
@@ -107,9 +102,7 @@ class LightPermInfoLiveData private constructor(
}
}
- /**
- * Stop listing for changes to this permission
- */
+ /** Stop listing for changes to this permission */
private fun stopListeningForChanges() {
if (isListeningForChanges) {
PackageBroadcastReceiver.removeAllCallback(this)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LoadAndFreezeLifeData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LoadAndFreezeLifeData.kt
index 5e8789a38..fd572e019 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/LoadAndFreezeLifeData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LoadAndFreezeLifeData.kt
@@ -23,8 +23,8 @@ import androidx.lifecycle.SavedStateHandle
* value forever.
*
* This even extends over live-cycle events as the data is stored in the {@link SaveStateHandle}.
- * This means that the data has to be writable to {@link SavedStateHandle} though, i.e.
- * Serialzable, Parcelable, list, set, map, or a literal
+ * This means that the data has to be writable to {@link SavedStateHandle} though, i.e. Serialzable,
+ * Parcelable, list, set, map, or a literal
*/
class LoadAndFreezeLifeData<T>(
private val state: SavedStateHandle,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/MicMutedLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/MicMutedLiveData.kt
index f53f60345..0bd045e80 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/MicMutedLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/MicMutedLiveData.kt
@@ -24,33 +24,35 @@ import android.media.AudioManager
import com.android.permissioncontroller.PermissionControllerApplication
import kotlinx.coroutines.Job
-/**
- * Tracks whether the mic is muted or not
- */
-val micMutedLiveData = object : SmartAsyncMediatorLiveData<Boolean>() {
- private val app = PermissionControllerApplication.get()
-
- private val isMicMuteRecevicer = object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- update()
+/** Tracks whether the mic is muted or not */
+val micMutedLiveData =
+ object : SmartAsyncMediatorLiveData<Boolean>() {
+ private val app = PermissionControllerApplication.get()
+
+ private val isMicMuteRecevicer =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ update()
+ }
+ }
+
+ override suspend fun loadDataAndPostValue(job: Job) {
+ postValue(app.getSystemService(AudioManager::class.java).isMicrophoneMute())
}
- }
-
- override suspend fun loadDataAndPostValue(job: Job) {
- postValue(app.getSystemService(AudioManager::class.java).isMicrophoneMute())
- }
- override fun onActive() {
- super.onActive()
+ override fun onActive() {
+ super.onActive()
- app.registerReceiver(isMicMuteRecevicer,
- IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED))
- update()
- }
+ app.registerReceiver(
+ isMicMuteRecevicer,
+ IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
+ )
+ update()
+ }
- override fun onInactive() {
- super.onInactive()
+ override fun onInactive() {
+ super.onInactive()
- app.unregisterReceiver(isMicMuteRecevicer)
+ app.unregisterReceiver(isMicMuteRecevicer)
+ }
}
-}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/OpUsageLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/OpUsageLiveData.kt
index 805d497c4..7ca0f2d96 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/OpUsageLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/OpUsageLiveData.kt
@@ -45,26 +45,28 @@ class OpUsageLiveData(
private val app: Application,
private val opNames: List<String>,
private val usageDurationMs: Long
-) : SmartAsyncMediatorLiveData<@JvmSuppressWildcards Map<String, List<OpAccess>>>(),
- AppOpsManager.OnOpActiveChangedListener {
+) :
+ SmartAsyncMediatorLiveData<@JvmSuppressWildcards Map<String, List<OpAccess>>>(),
+ AppOpsManager.OnOpActiveChangedListener {
private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!!
override suspend fun loadDataAndPostValue(job: Job) {
val now = System.currentTimeMillis()
val opMap = mutableMapOf<String, MutableList<OpAccess>>()
- val packageOps = try {
- appOpsManager.getPackagesForOps(opNames.toTypedArray())
- } catch (e: NullPointerException) {
- // older builds might not support all the app-ops requested
- emptyList<AppOpsManager.PackageOps>()
- }
+ val packageOps =
+ try {
+ appOpsManager.getPackagesForOps(opNames.toTypedArray())
+ } catch (e: NullPointerException) {
+ // older builds might not support all the app-ops requested
+ emptyList<AppOpsManager.PackageOps>()
+ }
for (packageOp in packageOps) {
for (opEntry in packageOp.ops) {
for ((attributionTag, attributedOpEntry) in opEntry.attributedOpEntries) {
val user = UserHandle.getUserHandleForUid(packageOp.uid)
- val lastAccessTime: Long = attributedOpEntry.getLastAccessTime(
- OP_FLAGS_ALL_TRUSTED)
+ val lastAccessTime: Long =
+ attributedOpEntry.getLastAccessTime(OP_FLAGS_ALL_TRUSTED)
if (lastAccessTime == -1L) {
// There was no access, so skip
@@ -78,34 +80,55 @@ class OpUsageLiveData(
lastAccessDuration = 0
}
- if (attributedOpEntry.isRunning ||
- lastAccessTime + lastAccessDuration > (now - usageDurationMs)) {
+ if (
+ attributedOpEntry.isRunning ||
+ lastAccessTime + lastAccessDuration > (now - usageDurationMs)
+ ) {
val accessList = opMap.getOrPut(opEntry.opStr) { mutableListOf() }
- val accessTime = if (attributedOpEntry.isRunning) {
- OpAccess.IS_RUNNING
- } else {
- lastAccessTime
- }
+ val accessTime =
+ if (attributedOpEntry.isRunning) {
+ OpAccess.IS_RUNNING
+ } else {
+ lastAccessTime
+ }
val proxy = attributedOpEntry.getLastProxyInfo(OP_FLAGS_ALL_TRUSTED)
var proxyAccess: OpAccess? = null
if (proxy != null && proxy.packageName != null) {
- proxyAccess = OpAccess(proxy.packageName!!, proxy.attributionTag,
- UserHandle.getUserHandleForUid(proxy.uid), accessTime)
+ proxyAccess =
+ OpAccess(
+ proxy.packageName!!,
+ proxy.attributionTag,
+ UserHandle.getUserHandleForUid(proxy.uid),
+ accessTime
+ )
}
- accessList.add(OpAccess(packageOp.packageName, attributionTag,
- user, accessTime, proxyAccess))
+ accessList.add(
+ OpAccess(
+ packageOp.packageName,
+ attributionTag,
+ user,
+ accessTime,
+ proxyAccess
+ )
+ )
// TODO ntmyren: remove logs once b/160724034 is fixed
- Log.i("OpUsageLiveData", "adding ${opEntry.opStr} for " +
+ Log.i(
+ "OpUsageLiveData",
+ "adding ${opEntry.opStr} for " +
"${packageOp.packageName}/$attributionTag, access time of " +
"$lastAccessTime, isRunning: ${attributedOpEntry.isRunning} " +
"current time $now, duration $lastAccessDuration, proxy: " +
- "${proxy?.packageName}")
+ "${proxy?.packageName}"
+ )
} else {
- Log.i("OpUsageLiveData", "NOT adding ${opEntry.opStr} for " +
+ Log.i(
+ "OpUsageLiveData",
+ "NOT adding ${opEntry.opStr} for " +
"${packageOp.packageName}/$attributionTag, access time of " +
"$lastAccessTime, isRunning: ${attributedOpEntry.isRunning} " +
- "current time $now, duration $lastAccessDuration")
+ "current time $now, duration $lastAccessDuration"
+ )
}
}
}
@@ -180,26 +203,31 @@ data class OpAccess(
const val IS_RUNNING = -1L
@JvmField
- val CREATOR = object : Parcelable.Creator<OpAccess> {
- override fun createFromParcel(parcel: Parcel): OpAccess {
- val packageName = parcel.readString()!!
- val attributionTag = parcel.readString()
- val user: UserHandle = parcel.readParcelable(UserHandle::class.java.classLoader)!!
- val lastAccessTime = parcel.readLong()
- var proxyAccess: OpAccess? = null
- val proxyPackageName = parcel.readString()
- if (proxyPackageName != null) {
- proxyAccess = OpAccess(proxyPackageName,
- parcel.readString(),
- parcel.readParcelable(UserHandle::class.java.classLoader)!!,
- lastAccessTime)
+ val CREATOR =
+ object : Parcelable.Creator<OpAccess> {
+ override fun createFromParcel(parcel: Parcel): OpAccess {
+ val packageName = parcel.readString()!!
+ val attributionTag = parcel.readString()
+ val user: UserHandle =
+ parcel.readParcelable(UserHandle::class.java.classLoader)!!
+ val lastAccessTime = parcel.readLong()
+ var proxyAccess: OpAccess? = null
+ val proxyPackageName = parcel.readString()
+ if (proxyPackageName != null) {
+ proxyAccess =
+ OpAccess(
+ proxyPackageName,
+ parcel.readString(),
+ parcel.readParcelable(UserHandle::class.java.classLoader)!!,
+ lastAccessTime
+ )
+ }
+ return OpAccess(packageName, attributionTag, user, lastAccessTime, proxyAccess)
}
- return OpAccess(packageName, attributionTag, user, lastAccessTime, proxyAccess)
- }
- override fun newArray(size: Int): Array<OpAccess?> {
- return arrayOfNulls(size)
+ override fun newArray(size: Int): Array<OpAccess?> {
+ return arrayOfNulls(size)
+ }
}
- }
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt
index b2e0236fa..09a7bb1e4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt
@@ -29,31 +29,24 @@ import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
-/**
- * Listens for package additions, replacements, and removals, and notifies listeners.
- */
+/** Listens for package additions, replacements, and removals, and notifies listeners. */
object PackageBroadcastReceiver : BroadcastReceiver() {
private val app: Application = PermissionControllerApplication.get()
- private val intentFilter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
- addAction(Intent.ACTION_PACKAGE_REMOVED)
- addAction(Intent.ACTION_PACKAGE_REPLACED)
- addAction(Intent.ACTION_PACKAGE_CHANGED)
- addDataScheme("package")
- }
+ private val intentFilter =
+ IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
+ addAction(Intent.ACTION_PACKAGE_REMOVED)
+ addAction(Intent.ACTION_PACKAGE_REPLACED)
+ addAction(Intent.ACTION_PACKAGE_CHANGED)
+ addDataScheme("package")
+ }
- /**
- * Map<packageName, callbacks listenening to package>
- */
+ /** Map<packageName, callbacks listenening to package> */
private val changeCallbacks = mutableMapOf<String, MutableSet<PackageBroadcastListener>>()
- /**
- * A list of listener IDs, which listen to all package additions, changes, and removals.
- */
+ /** A list of listener IDs, which listen to all package additions, changes, and removals. */
private val allCallbacks = mutableSetOf<PackageBroadcastListener>()
- /**
- * Add a callback which will be notified when the specified packaged is changed or removed.
- */
+ /** Add a callback which will be notified when the specified packaged is changed or removed. */
fun addChangeCallback(packageName: String, listener: PackageBroadcastListener) {
GlobalScope.launch(Main.immediate) {
val wasEmpty = hasNoListeners()
@@ -61,8 +54,12 @@ object PackageBroadcastReceiver : BroadcastReceiver() {
changeCallbacks.getOrPut(packageName, { mutableSetOf() }).add(listener)
if (wasEmpty) {
- app.applicationContext.registerReceiverForAllUsers(this@PackageBroadcastReceiver,
- intentFilter, null, null)
+ app.applicationContext.registerReceiverForAllUsers(
+ this@PackageBroadcastReceiver,
+ intentFilter,
+ null,
+ null
+ )
}
}
}
@@ -80,8 +77,12 @@ object PackageBroadcastReceiver : BroadcastReceiver() {
allCallbacks.add(listener)
if (wasEmpty) {
- app.applicationContext.registerReceiverForAllUsers(this@PackageBroadcastReceiver,
- intentFilter, null, null)
+ app.applicationContext.registerReceiverForAllUsers(
+ this@PackageBroadcastReceiver,
+ intentFilter,
+ null,
+ null
+ )
}
}
}
@@ -171,9 +172,7 @@ object PackageBroadcastReceiver : BroadcastReceiver() {
}
}
- /**
- * A listener interface for objects desiring to be notified of package broadcasts.
- */
+ /** A listener interface for objects desiring to be notified of package broadcasts. */
interface PackageBroadcastListener {
/**
* To be called when a specific package has been changed, or when any package has been
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt
index 49be1fbd0..cc050fae2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt
@@ -35,11 +35,9 @@ import kotlinx.coroutines.Job
* @param packageName The name of the package this LiveData will watch for mode changes for
* @param user The user for whom the packageInfo will be defined
*/
-class PackagePermissionsLiveData private constructor(
- private val app: Application,
- packageName: String,
- user: UserHandle
-) : SmartAsyncMediatorLiveData<Map<String, List<String>>?>() {
+class PackagePermissionsLiveData
+private constructor(private val app: Application, packageName: String, user: UserHandle) :
+ SmartAsyncMediatorLiveData<Map<String, List<String>>?>() {
private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
@@ -60,25 +58,32 @@ class PackagePermissionsLiveData private constructor(
for (permName in packageInfo.requestedPermissions) {
var groupName = PermissionMapping.getGroupOfPlatformPermission(permName)
if (groupName == null) {
- val permInfo = try {
- app.packageManager.getPermissionInfo(permName, 0)
- } catch (e: PackageManager.NameNotFoundException) {
- continue
- }
+ val permInfo =
+ try {
+ app.packageManager.getPermissionInfo(permName, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ continue
+ }
- if (permInfo.flags and PermissionInfo.FLAG_INSTALLED == 0 ||
- permInfo.flags and PermissionInfo.FLAG_REMOVED != 0) {
+ if (
+ permInfo.flags and PermissionInfo.FLAG_INSTALLED == 0 ||
+ permInfo.flags and PermissionInfo.FLAG_REMOVED != 0
+ ) {
continue
}
- if (packageInfo.isInstantApp && permInfo.protectionFlags and
- PermissionInfo.PROTECTION_FLAG_INSTANT == 0) {
+ if (
+ packageInfo.isInstantApp &&
+ permInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_INSTANT == 0
+ ) {
continue
}
- if (packageInfo.targetSdkVersion < Build.VERSION_CODES.M &&
- (permInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) !=
- 0) {
+ if (
+ packageInfo.targetSdkVersion < Build.VERSION_CODES.M &&
+ (permInfo.protectionFlags and
+ PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0
+ ) {
continue
}
@@ -104,14 +109,17 @@ class PackagePermissionsLiveData private constructor(
/**
* Repository for PackagePermissionsLiveData objects
+ *
* <p> Key value is a string package name and userHandle, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<Pair<String, UserHandle>,
- PackagePermissionsLiveData>() {
- override fun newValue(key: Pair<String, UserHandle>):
- PackagePermissionsLiveData {
- return PackagePermissionsLiveData(PermissionControllerApplication.get(), key.first,
- key.second)
+ companion object :
+ DataRepositoryForPackage<Pair<String, UserHandle>, PackagePermissionsLiveData>() {
+ override fun newValue(key: Pair<String, UserHandle>): PackagePermissionsLiveData {
+ return PackagePermissionsLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second
+ )
}
const val NON_RUNTIME_NORMAL_PERMS = "nonRuntimeNormalPerms"
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt
index 948815646..e923746d7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt
@@ -37,19 +37,15 @@ import com.android.permissioncontroller.permission.utils.Utils
* @param app The current application
* @param groupName The name of the permission group this LiveData represents
*/
-class PermGroupLiveData private constructor(
- private val app: Application,
- private val groupName: String
-) : SmartUpdateMediatorLiveData<PermGroup>(),
- PackageBroadcastReceiver.PackageBroadcastListener {
+class PermGroupLiveData
+private constructor(private val app: Application, private val groupName: String) :
+ SmartUpdateMediatorLiveData<PermGroup>(), PackageBroadcastReceiver.PackageBroadcastListener {
private val LOG_TAG = this::class.java.simpleName
private val context = app.applicationContext!!
- /**
- * Map<packageName, LiveData<PackageInfo>>
- */
+ /** Map<packageName, LiveData<PackageInfo>> */
private val packageLiveDatas = mutableMapOf<String, LightPackageInfoLiveData>()
private lateinit var groupInfo: PackageItemInfo
@@ -69,12 +65,14 @@ class PermGroupLiveData private constructor(
*/
override fun onUpdate() {
val permissionInfos = mutableMapOf<String, LightPermInfo>()
- groupInfo = Utils.getGroupInfo(groupName, context) ?: run {
- Log.e(LOG_TAG, "Invalid permission group $groupName")
- invalidateSingle(groupName)
- value = null
- return
- }
+ groupInfo =
+ Utils.getGroupInfo(groupName, context)
+ ?: run {
+ Log.e(LOG_TAG, "Invalid permission group $groupName")
+ invalidateSingle(groupName)
+ value = null
+ return
+ }
val permInfos = mutableListOf<PermissionInfo>()
when (groupInfo) {
is PermissionGroupInfo -> {
@@ -129,8 +127,8 @@ class PermGroupLiveData private constructor(
}
/**
- * Load data, and register a package change listener. We must watch for package changes,
- * because there is currently no listener for permission changes.
+ * Load data, and register a package change listener. We must watch for package changes, because
+ * there is currently no listener for permission changes.
*/
override fun onActive() {
update()
@@ -142,6 +140,7 @@ class PermGroupLiveData private constructor(
/**
* Repository for PermGroupLiveDatas.
+ *
* <p> Key value is a string permission group name, value is its corresponding LiveData.
*/
companion object : DataRepository<String, PermGroupLiveData>() {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupUsageLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupUsageLiveData.kt
index 08f9bbfb4..175f389fa 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupUsageLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupUsageLiveData.kt
@@ -41,19 +41,21 @@ class PermGroupUsageLiveData(
private val usageDurationMs: Long
) : SmartUpdateMediatorLiveData<Map<String, List<OpAccess>>>() {
/** Perm group name -> OpUsageLiveData */
- private val permGroupUsages = permGroupsNames.map { permGroup ->
- val appops = getPlatformPermissionNamesOfGroup(permGroup).mapNotNull { permName ->
- permissionToOp(permName)
- }
+ private val permGroupUsages =
+ permGroupsNames
+ .map { permGroup ->
+ val appops =
+ getPlatformPermissionNamesOfGroup(permGroup).mapNotNull { permName ->
+ permissionToOp(permName)
+ }
- permGroup to OpUsageLiveData[appops, usageDurationMs]
- }.toMap()
+ permGroup to OpUsageLiveData[appops, usageDurationMs]
+ }
+ .toMap()
init {
for (usage in permGroupUsages.values) {
- addSource(usage) {
- update()
- }
+ addSource(usage) { update() }
}
}
@@ -68,25 +70,33 @@ class PermGroupUsageLiveData(
}
// Only keep the last access for a permission group
- value = permGroupUsages.map { (permGroupName, usageLiveData) ->
- // (packageName, attributionTag) -> access
- val lastAccess = mutableMapOf<Pair<String, String?>, OpAccess>()
- for (access in usageLiveData.value!!.values.flatten()) {
- val key = access.packageName to access.attributionTag
- if (access.isRunning ||
- lastAccess[key]?.lastAccessTime ?: 0 < access.lastAccessTime) {
- lastAccess[key] = access
- }
- }
+ value =
+ permGroupUsages
+ .map { (permGroupName, usageLiveData) ->
+ // (packageName, attributionTag) -> access
+ val lastAccess = mutableMapOf<Pair<String, String?>, OpAccess>()
+ for (access in usageLiveData.value!!.values.flatten()) {
+ val key = access.packageName to access.attributionTag
+ if (
+ access.isRunning ||
+ lastAccess[key]?.lastAccessTime ?: 0 < access.lastAccessTime
+ ) {
+ lastAccess[key] = access
+ }
+ }
- permGroupName to lastAccess.values.toList()
- }.toMap()
+ permGroupName to lastAccess.values.toList()
+ }
+ .toMap()
}
companion object : DataRepository<Pair<List<String>, Long>, PermGroupUsageLiveData>() {
override fun newValue(key: Pair<List<String>, Long>): PermGroupUsageLiveData {
- return PermGroupUsageLiveData(PermissionControllerApplication.get(), key.first,
- key.second)
+ return PermGroupUsageLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesLiveData.kt
index c44c2b473..7fce5bac7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesLiveData.kt
@@ -31,10 +31,9 @@ import com.android.permissioncontroller.permission.utils.Utils.OS_PKG
*
* @param app The current application
*/
-class PermGroupsPackagesLiveData private constructor(
- private val app: Application,
- groupNamesLiveData: LiveData<List<String>>
-) : SmartUpdateMediatorLiveData<Map<String, Set<Pair<String, UserHandle>>>>() {
+class PermGroupsPackagesLiveData
+private constructor(private val app: Application, groupNamesLiveData: LiveData<List<String>>) :
+ SmartUpdateMediatorLiveData<Map<String, Set<Pair<String, UserHandle>>>>() {
private val packagesLiveData = AllPackageInfosLiveData
private val permGroupLiveDatas = mutableMapOf<String, PermGroupLiveData>()
@@ -43,12 +42,14 @@ class PermGroupsPackagesLiveData private constructor(
init {
addSource(groupNamesLiveData) {
- groupNames = it ?: emptyList()
+ groupNames = it
val getLiveData = { groupName: String -> PermGroupLiveData[groupName] }
setSourcesToDifference(groupNames, permGroupLiveDatas, getLiveData) {
- if (packagesLiveData.isInitialized &&
- permGroupLiveDatas.all { it.value.isInitialized }) {
+ if (
+ packagesLiveData.isInitialized &&
+ permGroupLiveDatas.all { it.value.isInitialized }
+ ) {
update()
}
}
@@ -62,9 +63,9 @@ class PermGroupsPackagesLiveData private constructor(
}
/**
- * Using the current list of permission groups, go through all packages in the system,
- * and figure out which permission groups they have permissions for. If applicable, remove
- * any lone-permission permission that are not requested by any packages.
+ * Using the current list of permission groups, go through all packages in the system, and
+ * figure out which permission groups they have permissions for. If applicable, remove any
+ * lone-permission permission that are not requested by any packages.
*/
override fun onUpdate() {
if (groupNames.isEmpty()) {
@@ -108,8 +109,10 @@ class PermGroupsPackagesLiveData private constructor(
* group, if also empty.
*/
for (permGroup in permGroups) {
- if (permGroup.groupInfo.isSinglePermGroup ||
- permGroup.name == Manifest.permission_group.UNDEFINED) {
+ if (
+ permGroup.groupInfo.isSinglePermGroup ||
+ permGroup.name == Manifest.permission_group.UNDEFINED
+ ) {
val groupPackages = groupApps[permGroup.name] ?: continue
if (groupPackages.isEmpty()) {
groupApps.remove(permGroup.name)
@@ -121,17 +124,22 @@ class PermGroupsPackagesLiveData private constructor(
}
companion object {
- private val customInstance = PermGroupsPackagesLiveData(
- PermissionControllerApplication.get(), CustomPermGroupNamesLiveData)
- private val standardInstance = PermGroupsPackagesLiveData(
- PermissionControllerApplication.get(), StandardPermGroupNamesLiveData)
+ private val customInstance =
+ PermGroupsPackagesLiveData(
+ PermissionControllerApplication.get(),
+ CustomPermGroupNamesLiveData
+ )
+ private val standardInstance =
+ PermGroupsPackagesLiveData(
+ PermissionControllerApplication.get(),
+ StandardPermGroupNamesLiveData
+ )
/**
* Get either the PermGroupsPackageLiveData instance corresponding either to the custom
* permission groups, or the standard permission group.
*
* @param customGroups Whether to get the custom groups instance, or the standard
- *
* @return The specified PermGroupsPackageLiveData
*/
@JvmStatic
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt
index 5d91ebfda..cc5f156ce 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt
@@ -37,8 +37,10 @@ import com.android.permissioncontroller.permission.utils.Utils
class PermGroupsPackagesUiInfoLiveData(
private val app: Application,
private val groupNamesLiveData: LiveData<List<String>>
-) : SmartUpdateMediatorLiveData<
- @kotlin.jvm.JvmSuppressWildcards Map<String, PermGroupPackagesUiInfo?>>() {
+) :
+ SmartUpdateMediatorLiveData<
+ @kotlin.jvm.JvmSuppressWildcards Map<String, PermGroupPackagesUiInfo?>
+ >() {
private val SYSTEM_SHELL = "android.app.role.SYSTEM_SHELL"
private val STAGGER_LOAD_TIME_MS = 50L
@@ -54,18 +56,16 @@ class PermGroupsPackagesUiInfoLiveData(
private val handler: Handler = Handler(Looper.getMainLooper())
- /**
- * Map<permission group name, PermGroupUiLiveDatas>
- */
- private val permGroupPackagesLiveDatas = mutableMapOf<String,
- SinglePermGroupPackagesUiInfoLiveData>()
+ /** Map<permission group name, PermGroupUiLiveDatas> */
+ private val permGroupPackagesLiveDatas =
+ mutableMapOf<String, SinglePermGroupPackagesUiInfoLiveData>()
private val allPackageData = mutableMapOf<String, PermGroupPackagesUiInfo?>()
private lateinit var groupNames: List<String>
init {
addSource(groupNamesLiveData) {
- groupNames = it ?: emptyList()
+ groupNames = it
update()
getPermGroupPackageLiveDatas()
}
@@ -78,7 +78,7 @@ class PermGroupsPackagesUiInfoLiveData(
private fun isGranted(grantState: AppPermGroupUiInfo.PermGrantState): Boolean {
return grantState != AppPermGroupUiInfo.PermGrantState.PERMS_DENIED &&
- grantState != AppPermGroupUiInfo.PermGrantState.PERMS_ASK
+ grantState != AppPermGroupUiInfo.PermGrantState.PERMS_ASK
}
private fun createPermGroupPackageUiInfo(
@@ -118,10 +118,19 @@ class PermGroupsPackagesUiInfoLiveData(
}
}
}
- val onlyShellGranted = grantedNonSystem == 0 && grantedSystem == 1 &&
+ val onlyShellGranted =
+ grantedNonSystem == 0 &&
+ grantedSystem == 1 &&
isPackageShell(firstGrantedSystemPackageName)
- return PermGroupPackagesUiInfo(groupName, nonSystem, grantedNonSystem,
- userInteractedNonSystem, grantedSystem, userInteractedSystem, onlyShellGranted)
+ return PermGroupPackagesUiInfo(
+ groupName,
+ nonSystem,
+ grantedNonSystem,
+ userInteractedNonSystem,
+ grantedSystem,
+ userInteractedSystem,
+ onlyShellGranted
+ )
}
private fun isPackageShell(packageName: String?): Boolean {
@@ -130,27 +139,30 @@ class PermGroupsPackagesUiInfoLiveData(
}
// This method is only called at most once per permission group, so no need to cache value
- val roleManager = Utils.getSystemServiceSafe(PermissionControllerApplication.get(),
- RoleManager::class.java)
+ val roleManager =
+ Utils.getSystemServiceSafe(
+ PermissionControllerApplication.get(),
+ RoleManager::class.java
+ )
return roleManager.getRoleHolders(SYSTEM_SHELL).contains(packageName)
}
override fun onUpdate() {
/**
- * Only update when either-
- * We have a list of groups, and none have loaded their data, or
+ * Only update when either- We have a list of groups, and none have loaded their data, or
* All packages have loaded their data
*/
val haveAllLiveDatas = groupNames.all { permGroupPackagesLiveDatas.contains(it) }
val allInitialized = permGroupPackagesLiveDatas.all { it.value.isInitialized }
for (groupName in groupNames) {
- allPackageData[groupName] = if (haveAllLiveDatas && allInitialized) {
- permGroupPackagesLiveDatas[groupName]?.value?.let { uiInfo ->
- createPermGroupPackageUiInfo(groupName, uiInfo)
+ allPackageData[groupName] =
+ if (haveAllLiveDatas && allInitialized) {
+ permGroupPackagesLiveDatas[groupName]?.value?.let { uiInfo ->
+ createPermGroupPackageUiInfo(groupName, uiInfo)
+ }
+ } else {
+ null
}
- } else {
- null
- }
}
value = allPackageData.toMap()
}
@@ -172,7 +184,7 @@ class PermGroupsPackagesUiInfoLiveData(
private fun addLiveDataDelayed(groupName: String, delayTimeMs: Long) {
val liveData = SinglePermGroupPackagesUiInfoLiveData[groupName]
permGroupPackagesLiveDatas[groupName] = liveData
- handler.postDelayed( { addSource(liveData) { update() } }, delayTimeMs)
+ handler.postDelayed({ addSource(liveData) { update() } }, delayTimeMs)
}
override fun onActive() {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt
index c385cf0e5..53d0da6f5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt
@@ -28,21 +28,22 @@ import kotlinx.coroutines.Job
/**
* A LiveData which tracks the permission state for one permission group for one package. It
- * includes both the granted state of every permission in the group, and the flags stored
- * in the PermissionController service.
+ * includes both the granted state of every permission in the group, and the flags stored in the
+ * PermissionController service.
*
* @param app The current application
* @param packageName The name of the package this LiveData will watch for mode changes for
- * @param permGroupName The name of the permission group whose app ops this LiveData
- * will watch
+ * @param permGroupName The name of the permission group whose app ops this LiveData will watch
* @param user The user of the package
*/
-class PermStateLiveData private constructor(
+class PermStateLiveData
+private constructor(
private val app: Application,
private val packageName: String,
private val permGroupName: String,
private val user: UserHandle
-) : SmartAsyncMediatorLiveData<Map<String, PermState>>(),
+) :
+ SmartAsyncMediatorLiveData<Map<String, PermState>>(),
PermissionListenerMultiplexer.PermissionChangeCallback {
private val context = Utils.getUserContext(app, user)
@@ -58,9 +59,7 @@ class PermStateLiveData private constructor(
updateAsync()
}
- addSource(groupLiveData) {
- updateAsync()
- }
+ addSource(groupLiveData) { updateAsync() }
}
/**
@@ -84,10 +83,11 @@ class PermStateLiveData private constructor(
permissionGroup.permissionInfos[permissionName]?.let { permInfo ->
val packageFlags = packageInfo.requestedPermissionsFlags[index]
- val permFlags = context.packageManager.getPermissionFlags(permInfo.name,
- packageName, user)
- val granted = packageFlags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0 &&
- permFlags and PackageManager.FLAG_PERMISSION_REVOKED_COMPAT == 0
+ val permFlags =
+ context.packageManager.getPermissionFlags(permInfo.name, packageName, user)
+ val granted =
+ packageFlags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0 &&
+ permFlags and PackageManager.FLAG_PERMISSION_REVOKED_COMPAT == 0
if (job.isCancelled) {
return
@@ -105,15 +105,12 @@ class PermStateLiveData private constructor(
private fun checkForUidUpdate(packageInfo: LightPackageInfo?) {
if (packageInfo == null) {
- registeredUid?.let {
- PermissionListenerMultiplexer.removeCallback(it, this)
- }
+ registeredUid?.let { PermissionListenerMultiplexer.removeCallback(it, this) }
return
}
uid = packageInfo.uid
if (uid != registeredUid && hasActiveObservers()) {
- PermissionListenerMultiplexer.addOrReplaceCallback(
- registeredUid, packageInfo.uid, this)
+ PermissionListenerMultiplexer.addOrReplaceCallback(registeredUid, packageInfo.uid, this)
registeredUid = uid
}
}
@@ -136,14 +133,19 @@ class PermStateLiveData private constructor(
/**
* Repository for PermStateLiveDatas.
- * <p> Key value is a triple of string package name, string permission group name, and UserHandle,
- * value is its corresponding LiveData.
+ *
+ * <p> Key value is a triple of string package name, string permission group name, and
+ * UserHandle, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>,
- PermStateLiveData>() {
+ companion object :
+ DataRepositoryForPackage<Triple<String, String, UserHandle>, PermStateLiveData>() {
override fun newValue(key: Triple<String, String, UserHandle>): PermStateLiveData {
- return PermStateLiveData(PermissionControllerApplication.get(),
- key.first, key.second, key.third)
+ return PermStateLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ key.third
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionChange.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionChange.kt
index 29789b0f7..4959a7fba 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionChange.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionChange.kt
@@ -19,10 +19,8 @@ package com.android.permissioncontroller.permission.data
/**
* A record of the user changing permissions for the app but not including any information on what
* actual decision was made. This information is not included for privacy reasons and allows us to
- * persist the data for longer periods of time than we'd be able to otherwise
- * (e.g. [PermissionDecision]).
+ * persist the data for longer periods of time than we'd be able to otherwise (e.g.
+ * [PermissionDecision]).
*/
-data class PermissionChange(
- override val packageName: String,
- override val eventTime: Long
-) : PermissionEvent(packageName, eventTime)
+data class PermissionChange(override val packageName: String, override val eventTime: Long) :
+ PermissionEvent(packageName, eventTime)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionEvent.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionEvent.kt
index ad759795b..2fa64cd43 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionEvent.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionEvent.kt
@@ -20,10 +20,7 @@ package com.android.permissioncontroller.permission.data
* A record of a permission event caused by the user.
*
* @param packageName package name of the app the event is for
- * @param eventTime the time of the event, in epoch time. Should be rounded to day-level
- * precision for user privacy.
+ * @param eventTime the time of the event, in epoch time. Should be rounded to day-level precision
+ * for user privacy.
*/
-abstract class PermissionEvent(
- open val packageName: String,
- open val eventTime: Long
-)
+abstract class PermissionEvent(open val packageName: String, open val eventTime: Long)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionListenerMultiplexer.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionListenerMultiplexer.kt
index d6d532341..fb0f3077a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionListenerMultiplexer.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionListenerMultiplexer.kt
@@ -20,24 +20,19 @@ import android.app.Application
import android.content.pm.PackageManager
import com.android.permissioncontroller.PermissionControllerApplication
-/**
- * Serves as a single shared Permission Change Listener for all AppPermissionGroupLiveDatas.
- *
- */
+/** Serves as a single shared Permission Change Listener for all AppPermissionGroupLiveDatas. */
object PermissionListenerMultiplexer : PackageManager.OnPermissionsChangedListener {
private val app: Application = PermissionControllerApplication.get()
/**
- * Map<UID, list of PermissionChangeCallbacks that wish to be informed when
- * permissions are updated for that UID>
+ * Map<UID, list of PermissionChangeCallbacks that wish to be informed when permissions are
+ * updated for that UID>
*/
private val callbacks = mutableMapOf<Int, MutableList<PermissionChangeCallback>>()
private val pm = app.applicationContext.packageManager
override fun onPermissionsChanged(uid: Int) {
- callbacks[uid]?.toList()?.forEach { callback ->
- callback.onPermissionChange()
- }
+ callbacks[uid]?.toList()?.forEach { callback -> callback.onPermissionChange() }
}
fun addOrReplaceCallback(oldUid: Int?, newUid: Int, callback: PermissionChangeCallback) {
@@ -78,4 +73,4 @@ object PermissionListenerMultiplexer : PackageManager.OnPermissionsChangedListen
interface PermissionChangeCallback {
fun onPermissionChange()
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PreinstalledUserPackageInfosLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PreinstalledUserPackageInfosLiveData.kt
index b4205acff..243fe5b03 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PreinstalledUserPackageInfosLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PreinstalledUserPackageInfosLiveData.kt
@@ -33,23 +33,23 @@ import kotlinx.coroutines.Job
* @param app The current application
* @param user The user whose packages are desired
*/
-class PreinstalledUserPackageInfosLiveData private constructor(
- private val app: Application,
- private val user: UserHandle
-) : SmartAsyncMediatorLiveData<@kotlin.jvm.JvmSuppressWildcards List<LightPackageInfo>>(
- isStaticVal = true, alwaysUpdateOnActive = false
-) {
+class PreinstalledUserPackageInfosLiveData
+private constructor(private val app: Application, private val user: UserHandle) :
+ SmartAsyncMediatorLiveData<@kotlin.jvm.JvmSuppressWildcards List<LightPackageInfo>>(
+ isStaticVal = true,
+ alwaysUpdateOnActive = false
+ ) {
- /**
- * Get all of the preinstalled packages in the system for this user
- */
+ /** Get all of the preinstalled packages in the system for this user */
override suspend fun loadDataAndPostValue(job: Job) {
if (job.isCancelled) {
return
}
- val packageInfos = app.applicationContext.packageManager
- .getInstalledPackagesAsUser(GET_PERMISSIONS or MATCH_UNINSTALLED_PACKAGES
- or MATCH_FACTORY_ONLY, user.identifier)
+ val packageInfos =
+ app.applicationContext.packageManager.getInstalledPackagesAsUser(
+ GET_PERMISSIONS or MATCH_UNINSTALLED_PACKAGES or MATCH_FACTORY_ONLY,
+ user.identifier
+ )
postValue(packageInfos.map { packageInfo -> LightPackageInfo(packageInfo) })
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt
index 2cf17fb95..744b5bdbd 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt
@@ -29,7 +29,8 @@ import kotlinx.coroutines.Job
* @param app The current application
* @param roleName The name of the role
*/
-class RoleHoldersLiveData private constructor(
+class RoleHoldersLiveData
+private constructor(
private val app: Application,
private val roleName: String,
private val user: UserHandle
@@ -57,6 +58,7 @@ class RoleHoldersLiveData private constructor(
/**
* Repository for RoleHoldersLiveData.
+ *
* <p> Key value is the name of the role.
*/
companion object : DataRepository<Pair<String, UserHandle>, RoleHoldersLiveData>() {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt
index ac853439d..fefaa5fc4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt
@@ -23,16 +23,14 @@ import android.os.UserHandle
import androidx.annotation.GuardedBy
import com.android.permissioncontroller.PermissionControllerApplication
-/**
- * Serves as a single shared Role Change Listener.
- */
+/** Serves as a single shared Role Change Listener. */
object RoleListenerMultiplexer : OnRoleHoldersChangedListener {
private val app: Application = PermissionControllerApplication.get()
@GuardedBy("lock")
- private val callbacks = mutableMapOf<UserHandle,
- MutableMap<String, MutableList<RoleHoldersChangeCallback>>>()
+ private val callbacks =
+ mutableMapOf<UserHandle, MutableMap<String, MutableList<RoleHoldersChangeCallback>>>()
private val roleManager = app.getSystemService(RoleManager::class.java)!!
@@ -40,12 +38,8 @@ object RoleListenerMultiplexer : OnRoleHoldersChangedListener {
override fun onRoleHoldersChanged(roleName: String, user: UserHandle) {
val callbacksCopy: List<RoleHoldersChangeCallback>?
- synchronized(lock) {
- callbacksCopy = callbacks[user]?.get(roleName)?.toList()
- }
- callbacksCopy?.forEach { listener ->
- listener.onRoleHoldersChanged()
- }
+ synchronized(lock) { callbacksCopy = callbacks[user]?.get(roleName)?.toList() }
+ callbacksCopy?.forEach { listener -> listener.onRoleHoldersChanged() }
}
fun addCallback(roleName: String, user: UserHandle, callback: RoleHoldersChangeCallback) {
@@ -88,4 +82,4 @@ object RoleListenerMultiplexer : OnRoleHoldersChangedListener {
interface RoleHoldersChangeCallback {
fun onRoleHoldersChanged()
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedAutofillServiceLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedAutofillServiceLiveData.kt
index 9aced3e2b..3ab59237c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedAutofillServiceLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedAutofillServiceLiveData.kt
@@ -29,20 +29,19 @@ import kotlinx.coroutines.Job
* @param app The current application
* @param user The user the services should be determined for
*/
-class SelectedAutofillServiceLiveData(
- private val app: Application,
- private val user: UserHandle
-) : SmartAsyncMediatorLiveData<String?>() {
+class SelectedAutofillServiceLiveData(private val app: Application, private val user: UserHandle) :
+ SmartAsyncMediatorLiveData<String?>() {
override suspend fun loadDataAndPostValue(job: Job) {
if (job.isCancelled) {
return
}
- val packageName = Utils.getUserContext(app, user)
- .getSystemService(AutofillManager::class.java)
- ?.autofillServiceComponentName
- ?.packageName
+ val packageName =
+ Utils.getUserContext(app, user)
+ .getSystemService(AutofillManager::class.java)
+ ?.autofillServiceComponentName
+ ?.packageName
postValue(packageName)
}
@@ -52,10 +51,9 @@ class SelectedAutofillServiceLiveData(
*
* <p> Key value is a user, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<UserHandle,
- SelectedAutofillServiceLiveData>() {
+ companion object : DataRepositoryForPackage<UserHandle, SelectedAutofillServiceLiveData>() {
override fun newValue(key: UserHandle): SelectedAutofillServiceLiveData {
return SelectedAutofillServiceLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedVoiceInteractionServiceLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedVoiceInteractionServiceLiveData.kt
index 72a6da139..9bb749323 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedVoiceInteractionServiceLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedVoiceInteractionServiceLiveData.kt
@@ -40,12 +40,14 @@ class SelectedVoiceInteractionServiceLiveData(
return
}
- val packageName = Settings.Secure.getString(
- Utils.getUserContext(app, user).contentResolver,
- // Settings.Secure.VOICE_INTERACTION_SERVICE
- "voice_interaction_service")
- ?.let(ComponentName::unflattenFromString)
- ?.packageName
+ val packageName =
+ Settings.Secure.getString(
+ Utils.getUserContext(app, user).contentResolver,
+ // Settings.Secure.VOICE_INTERACTION_SERVICE
+ "voice_interaction_service"
+ )
+ ?.let(ComponentName::unflattenFromString)
+ ?.packageName
postValue(packageName)
}
@@ -55,11 +57,13 @@ class SelectedVoiceInteractionServiceLiveData(
*
* <p> Key value is a user, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<UserHandle,
- SelectedVoiceInteractionServiceLiveData>() {
+ companion object :
+ DataRepositoryForPackage<UserHandle, SelectedVoiceInteractionServiceLiveData>() {
override fun newValue(key: UserHandle): SelectedVoiceInteractionServiceLiveData {
return SelectedVoiceInteractionServiceLiveData(
- PermissionControllerApplication.get(), key)
+ PermissionControllerApplication.get(),
+ key
+ )
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedWallpaperServiceLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedWallpaperServiceLiveData.kt
index e4c1314c1..d004f79dc 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedWallpaperServiceLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedWallpaperServiceLiveData.kt
@@ -29,24 +29,23 @@ import kotlinx.coroutines.Job
* @param app The current application
* @param user The user the services should be determined for
*/
-class SelectedWallpaperServiceLiveData(
- private val app: Application,
- private val user: UserHandle
-) : SmartAsyncMediatorLiveData<String?>() {
+class SelectedWallpaperServiceLiveData(private val app: Application, private val user: UserHandle) :
+ SmartAsyncMediatorLiveData<String?>() {
override suspend fun loadDataAndPostValue(job: Job) {
if (job.isCancelled) {
return
}
- val packageName = try {
- Utils.getUserContext(app, user)
+ val packageName =
+ try {
+ Utils.getUserContext(app, user)
.getSystemService(WallpaperManager::class.java)
?.wallpaperInfo
?.packageName
- } catch (e: NullPointerException) {
- null
- }
+ } catch (e: NullPointerException) {
+ null
+ }
postValue(packageName)
}
@@ -56,10 +55,9 @@ class SelectedWallpaperServiceLiveData(
*
* <p> Key value is a user, value is its corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<UserHandle,
- SelectedWallpaperServiceLiveData>() {
+ companion object : DataRepositoryForPackage<UserHandle, SelectedWallpaperServiceLiveData>() {
override fun newValue(key: UserHandle): SelectedWallpaperServiceLiveData {
return SelectedWallpaperServiceLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/ServiceLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/ServiceLiveData.kt
index 6d59fd585..2deae79cc 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/ServiceLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/ServiceLiveData.kt
@@ -49,9 +49,10 @@ class ServiceLiveData(
override val intentAction: String,
private val permission: String,
private val user: UserHandle
-) : SmartAsyncMediatorLiveData<Set<String>>(),
- PackageBroadcastReceiver.PackageBroadcastListener,
- HasIntentAction {
+) :
+ SmartAsyncMediatorLiveData<Set<String>>(),
+ PackageBroadcastReceiver.PackageBroadcastListener,
+ HasIntentAction {
private val name = intentAction.substringAfterLast(".")
@@ -60,7 +61,7 @@ class ServiceLiveData(
private val enabledNotificationListenersLiveData = EnabledNotificationListenersLiveData[user]
private val selectedWallpaperServiceLiveData = SelectedWallpaperServiceLiveData[user]
private val selectedVoiceInteractionServiceLiveData =
- SelectedVoiceInteractionServiceLiveData[user]
+ SelectedVoiceInteractionServiceLiveData[user]
private val selectedAutofillServiceLiveData = SelectedAutofillServiceLiveData[user]
private val enabledDreamServicesLiveData = EnabledDreamServicesLiveData[user]
private val disabledPrintServicesLiveData = DisabledPrintServicesLiveData[user]
@@ -68,49 +69,31 @@ class ServiceLiveData(
init {
if (intentAction == AccessibilityService.SERVICE_INTERFACE) {
- addSource(enabledAccessibilityServicesLiveData) {
- updateAsync()
- }
+ addSource(enabledAccessibilityServicesLiveData) { updateAsync() }
}
if (intentAction == InputMethod.SERVICE_INTERFACE) {
- addSource(enabledInputMethodsLiveData) {
- updateAsync()
- }
+ addSource(enabledInputMethodsLiveData) { updateAsync() }
}
if (intentAction == NotificationListenerService.SERVICE_INTERFACE) {
- addSource(enabledNotificationListenersLiveData) {
- updateAsync()
- }
+ addSource(enabledNotificationListenersLiveData) { updateAsync() }
}
if (intentAction == WallpaperService.SERVICE_INTERFACE) {
- addSource(selectedWallpaperServiceLiveData) {
- updateAsync()
- }
+ addSource(selectedWallpaperServiceLiveData) { updateAsync() }
}
if (intentAction == VoiceInteractionService.SERVICE_INTERFACE) {
- addSource(selectedVoiceInteractionServiceLiveData) {
- updateAsync()
- }
+ addSource(selectedVoiceInteractionServiceLiveData) { updateAsync() }
}
if (intentAction == AutofillService.SERVICE_INTERFACE) {
- addSource(selectedAutofillServiceLiveData) {
- updateAsync()
- }
+ addSource(selectedAutofillServiceLiveData) { updateAsync() }
}
if (intentAction == DreamService.SERVICE_INTERFACE) {
- addSource(enabledDreamServicesLiveData) {
- updateAsync()
- }
+ addSource(enabledDreamServicesLiveData) { updateAsync() }
}
if (intentAction == PrintService.SERVICE_INTERFACE) {
- addSource(disabledPrintServicesLiveData) {
- updateAsync()
- }
+ addSource(disabledPrintServicesLiveData) { updateAsync() }
}
if (intentAction == DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE) {
- addSource(enabledDeviceAdminsLiveDataLiveData) {
- updateAsync()
- }
+ addSource(enabledDeviceAdminsLiveDataLiveData) { updateAsync() }
}
}
@@ -122,48 +105,69 @@ class ServiceLiveData(
if (job.isCancelled) {
return
}
- if (intentAction == AccessibilityService.SERVICE_INTERFACE &&
- !enabledAccessibilityServicesLiveData.isInitialized) {
+ if (
+ intentAction == AccessibilityService.SERVICE_INTERFACE &&
+ !enabledAccessibilityServicesLiveData.isInitialized
+ ) {
return
}
- if (intentAction == InputMethod.SERVICE_INTERFACE &&
- !enabledInputMethodsLiveData.isInitialized) {
+ if (
+ intentAction == InputMethod.SERVICE_INTERFACE &&
+ !enabledInputMethodsLiveData.isInitialized
+ ) {
return
}
- if (intentAction == NotificationListenerService.SERVICE_INTERFACE &&
- !enabledNotificationListenersLiveData.isInitialized) {
+ if (
+ intentAction == NotificationListenerService.SERVICE_INTERFACE &&
+ !enabledNotificationListenersLiveData.isInitialized
+ ) {
return
}
- if (intentAction == WallpaperService.SERVICE_INTERFACE &&
- !selectedWallpaperServiceLiveData.isInitialized) {
+ if (
+ intentAction == WallpaperService.SERVICE_INTERFACE &&
+ !selectedWallpaperServiceLiveData.isInitialized
+ ) {
return
}
- if (intentAction == VoiceInteractionService.SERVICE_INTERFACE &&
- !selectedVoiceInteractionServiceLiveData.isInitialized) {
+ if (
+ intentAction == VoiceInteractionService.SERVICE_INTERFACE &&
+ !selectedVoiceInteractionServiceLiveData.isInitialized
+ ) {
return
}
- if (intentAction == AutofillService.SERVICE_INTERFACE &&
- !selectedAutofillServiceLiveData.isInitialized) {
+ if (
+ intentAction == AutofillService.SERVICE_INTERFACE &&
+ !selectedAutofillServiceLiveData.isInitialized
+ ) {
return
}
- if (intentAction == DreamService.SERVICE_INTERFACE &&
- !enabledDreamServicesLiveData.isInitialized) {
+ if (
+ intentAction == DreamService.SERVICE_INTERFACE &&
+ !enabledDreamServicesLiveData.isInitialized
+ ) {
return
}
- if (intentAction == PrintService.SERVICE_INTERFACE &&
- !disabledPrintServicesLiveData.isInitialized) {
+ if (
+ intentAction == PrintService.SERVICE_INTERFACE &&
+ !disabledPrintServicesLiveData.isInitialized
+ ) {
return
}
- if (intentAction == DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE &&
- !enabledDeviceAdminsLiveDataLiveData.isInitialized) {
+ if (
+ intentAction == DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE &&
+ !enabledDeviceAdminsLiveDataLiveData.isInitialized
+ ) {
return
}
- val packageNames = getUserContext(app, user).packageManager
+ val packageNames =
+ getUserContext(app, user)
+ .packageManager
.queryIntentServices(
- Intent(intentAction),
- PackageManager.GET_SERVICES or PackageManager.GET_META_DATA)
+ Intent(intentAction),
+ PackageManager.GET_SERVICES or PackageManager.GET_META_DATA
+ )
.mapNotNull { resolveInfo ->
if (resolveInfo?.serviceInfo?.permission != permission) {
return@mapNotNull null
@@ -171,17 +175,19 @@ class ServiceLiveData(
val packageName = resolveInfo.serviceInfo?.packageName
if (!isServiceEnabled(packageName)) {
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG,
- "Not exempting $packageName - not an active $name " +
- "for u${user.identifier}")
+ DumpableLog.i(
+ LOG_TAG,
+ "Not exempting $packageName - not an active $name " +
+ "for u${user.identifier}"
+ )
}
return@mapNotNull null
}
packageName
- }.toSet()
+ }
+ .toSet()
if (DEBUG_HIBERNATION_POLICY) {
- DumpableLog.i(LOG_TAG,
- "Detected ${name}s: $packageNames")
+ DumpableLog.i(LOG_TAG, "Detected ${name}s: $packageNames")
}
postValue(packageNames)
@@ -241,13 +247,17 @@ class ServiceLiveData(
* <p> Key value is a (string service name, required permission, user) triple, value is its
* corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>,
- ServiceLiveData>() {
+ companion object :
+ DataRepositoryForPackage<Triple<String, String, UserHandle>, ServiceLiveData>() {
private const val LOG_TAG = "ServiceLiveData"
override fun newValue(key: Triple<String, String, UserHandle>): ServiceLiveData {
- return ServiceLiveData(PermissionControllerApplication.get(),
- key.first, key.second, key.third)
+ return ServiceLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ key.third
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SinglePermGroupPackagesUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SinglePermGroupPackagesUiInfoLiveData.kt
index a46882c04..b2348a17a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/SinglePermGroupPackagesUiInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SinglePermGroupPackagesUiInfoLiveData.kt
@@ -19,8 +19,8 @@ package com.android.permissioncontroller.permission.data
import android.app.Application
import android.os.UserHandle
import com.android.permissioncontroller.PermissionControllerApplication
-import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo
+import com.android.permissioncontroller.permission.utils.PermissionMapping
/**
* LiveData for the UI info for all packages in a single permission group. Tracks which packages
@@ -30,26 +30,21 @@ import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGr
* @param app The current application
* @param permGroupName The name of the permission group this LiveData represents
*/
-class SinglePermGroupPackagesUiInfoLiveData private constructor(
- private val app: Application,
- private val permGroupName: String
-) : SmartUpdateMediatorLiveData<Map<Pair<String, UserHandle>, AppPermGroupUiInfo>>() {
+class SinglePermGroupPackagesUiInfoLiveData
+private constructor(private val app: Application, private val permGroupName: String) :
+ SmartUpdateMediatorLiveData<Map<Pair<String, UserHandle>, AppPermGroupUiInfo>>() {
private val permGroupLiveData = PermGroupLiveData[permGroupName]
private val isCustomGroup =
!PermissionMapping.getPlatformPermissionGroups().contains(permGroupName)
- private val permGroupPackagesLiveData = PermGroupsPackagesLiveData.get(
- customGroups = isCustomGroup)
+ private val permGroupPackagesLiveData =
+ PermGroupsPackagesLiveData.get(customGroups = isCustomGroup)
- /**
- * Map<Pair<package name, UserHandle>, UI data LiveData>
- */
- private val appPermGroupLiveDatas = mutableMapOf<Pair<String, UserHandle>,
- AppPermGroupUiInfoLiveData>()
+ /** Map<Pair<package name, UserHandle>, UI data LiveData> */
+ private val appPermGroupLiveDatas =
+ mutableMapOf<Pair<String, UserHandle>, AppPermGroupUiInfoLiveData>()
- /**
- * Map<Pair<packageName, userHandle>, UI data>.
- */
+ /** Map<Pair<packageName, userHandle>, UI data>. */
private val shownPackages = mutableMapOf<Pair<String, UserHandle>, AppPermGroupUiInfo>()
init {
@@ -60,9 +55,7 @@ class SinglePermGroupPackagesUiInfoLiveData private constructor(
}
}
- addSource(permGroupPackagesLiveData) {
- update()
- }
+ addSource(permGroupPackagesLiveData) { update() }
}
override fun onUpdate() {
@@ -71,9 +64,7 @@ class SinglePermGroupPackagesUiInfoLiveData private constructor(
addAndRemoveAppPermGroupLiveDatas(thisPermGroupPackages.toList())
if (thisPermGroupPackages.isEmpty()) {
- permGroupLiveData.value?.groupInfo?.let {
- value = emptyMap()
- }
+ permGroupLiveData.value?.groupInfo?.let { value = emptyMap() }
}
}
}
@@ -83,48 +74,46 @@ class SinglePermGroupPackagesUiInfoLiveData private constructor(
AppPermGroupUiInfoLiveData[key.first, permGroupName, key.second]
}
- val (_, removed) = setSourcesToDifference(pkgs, appPermGroupLiveDatas, getLiveData) { key ->
- val appPermGroupUiInfoLiveData = appPermGroupLiveDatas[key]
- val appPermGroupUiInfo = appPermGroupUiInfoLiveData?.value
- shownPackages.remove(key)
-
- if (appPermGroupUiInfo == null) {
- if (appPermGroupUiInfoLiveData != null &&
- appPermGroupUiInfoLiveData.isInitialized) {
- removeSource(appPermGroupUiInfoLiveData)
- appPermGroupLiveDatas.remove(key)
+ val (_, removed) =
+ setSourcesToDifference(pkgs, appPermGroupLiveDatas, getLiveData) { key ->
+ val appPermGroupUiInfoLiveData = appPermGroupLiveDatas[key]
+ val appPermGroupUiInfo = appPermGroupUiInfoLiveData?.value
+ shownPackages.remove(key)
+
+ if (appPermGroupUiInfo == null) {
+ if (
+ appPermGroupUiInfoLiveData != null &&
+ appPermGroupUiInfoLiveData.isInitialized
+ ) {
+ removeSource(appPermGroupUiInfoLiveData)
+ appPermGroupLiveDatas.remove(key)
+ }
+ } else {
+ shownPackages[key] = appPermGroupUiInfo
}
- } else {
- shownPackages[key] = appPermGroupUiInfo
- }
- if (appPermGroupLiveDatas.all { entry -> entry.value.isInitialized }) {
- permGroupLiveData.value?.groupInfo?.let {
- value = shownPackages.toMap()
+ if (appPermGroupLiveDatas.all { entry -> entry.value.isInitialized }) {
+ permGroupLiveData.value?.groupInfo?.let { value = shownPackages.toMap() }
}
}
- }
for (removedKey in removed) {
shownPackages.remove(removedKey)
}
if (appPermGroupLiveDatas.all { entry -> entry.value.isInitialized }) {
- permGroupLiveData.value?.groupInfo?.let {
- value = shownPackages.toMap()
- }
+ permGroupLiveData.value?.groupInfo?.let { value = shownPackages.toMap() }
}
}
/**
* Repository for SinglePermGroupPackagesUiInfoLiveData objects.
+ *
* <p> Key value is a string permission group name, value is its corresponding LiveData.
*/
- companion object : DataRepository<String,
- SinglePermGroupPackagesUiInfoLiveData>() {
+ companion object : DataRepository<String, SinglePermGroupPackagesUiInfoLiveData>() {
override fun newValue(key: String): SinglePermGroupPackagesUiInfoLiveData {
- return SinglePermGroupPackagesUiInfoLiveData(PermissionControllerApplication.get(),
- key)
+ return SinglePermGroupPackagesUiInfoLiveData(PermissionControllerApplication.get(), key)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartAsyncMediatorLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartAsyncMediatorLiveData.kt
index b7491a7a4..1cc248956 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartAsyncMediatorLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartAsyncMediatorLiveData.kt
@@ -36,14 +36,12 @@ abstract class SmartAsyncMediatorLiveData<T>(
) : SmartUpdateMediatorLiveData<T>(isStaticVal) {
private var currentJob: Job? = null
- @Volatile
- private var jobQueued = false
- @Volatile
- private var jobRunning = false
+ @Volatile private var jobQueued = false
+ @Volatile private var jobRunning = false
/**
- * The main function which will load data. It should periodically check isCancelled to see if
- * it should stop working. If data is loaded, it should call "postValue".
+ * The main function which will load data. It should periodically check isCancelled to see if it
+ * should stop working. If data is loaded, it should call "postValue".
*/
abstract suspend fun loadDataAndPostValue(job: Job)
@@ -67,9 +65,7 @@ abstract class SmartAsyncMediatorLiveData<T>(
jobRunning = false
if (jobQueued) {
jobQueued = false
- GlobalScope.launch(Main.immediate) {
- updateAsync()
- }
+ GlobalScope.launch(Main.immediate) { updateAsync() }
}
}
}
@@ -95,4 +91,4 @@ abstract class SmartAsyncMediatorLiveData<T>(
}
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt
index d7fe4fb2e..73b8ed198 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt
@@ -31,8 +31,8 @@ import kotlinx.coroutines.launch
/**
* A MediatorLiveData which tracks how long it has been inactive, compares new values before setting
- * its value (avoiding unnecessary updates), and can calculate the set difference between a list
- * and a map (used when determining whether or not to add a LiveData as a source).
+ * its value (avoiding unnecessary updates), and can calculate the set difference between a list and
+ * a map (used when determining whether or not to add a LiveData as a source).
*
* @param isStaticVal Whether or not this LiveData value is expected to change
*/
@@ -45,14 +45,6 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
}
/**
- * Boolean, whether or not the value of this uiDataLiveData has been explicitly set yet.
- * Differentiates between "null value because liveData is new" and "null value because
- * liveData is invalid"
- */
- var isInitialized = false
- private set
-
- /**
* Boolean, whether or not this liveData has a stale value or not. Every time the liveData goes
* inactive, its data becomes stale, until it goes active again, and is explicitly set.
*/
@@ -66,7 +58,6 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
ensureMainThread()
if (!isInitialized) {
- isInitialized = true
// If we have received an invalid value, and this is the first time we are set,
// notify observers.
if (newValue == null) {
@@ -105,8 +96,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
onUpdate()
}
- @MainThread
- protected abstract fun onUpdate()
+ @MainThread protected abstract fun onUpdate()
override var timeWentInactive: Long? = System.nanoTime()
@@ -116,7 +106,6 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
*
* @param valOne The first T to be compared
* @param valTwo The second T to be compared
- *
* @return True if the two values are different, false otherwise
*/
protected open fun valueNotEqual(valOne: T?, valTwo: T?): Boolean {
@@ -124,8 +113,11 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
}
override fun <S : Any?> addSource(source: LiveData<S>, onChanged: Observer<in S>) {
- addSourceWithStackTraceAttribution(source, onChanged,
- IllegalStateException().getStackTrace())
+ addSourceWithStackTraceAttribution(
+ source,
+ onChanged,
+ IllegalStateException().getStackTrace()
+ )
}
private fun <S : Any?> addSourceWithStackTraceAttribution(
@@ -167,8 +159,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
* @param have The map of livedatas we currently have as sources
* @param getLiveDataFun A function to turn a key into a liveData
* @param onUpdateFun An optional function which will update differently based on different
- * LiveDatas. If blank, will simply call update.
- *
+ * LiveDatas. If blank, will simply call update.
* @return a pair of (all keys added, all keys removed)
*/
fun <K, V : LiveData<*>> setSourcesToDifference(
@@ -176,7 +167,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
have: MutableMap<K, V>,
getLiveDataFun: (K) -> V,
onUpdateFun: ((K) -> Unit)? = null
- ): Pair<Set<K>, Set<K>>{
+ ): Pair<Set<K>, Set<K>> {
// Ensure the map is correct when method returns
val (toAdd, toRemove) = KotlinUtils.getMapAndListDifferences(desired, have)
for (key in toAdd) {
@@ -201,13 +192,14 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
val liveData = getLiveDataFun(key)
// Should be a no op, but there is a slight possibility it isn't
have[key] = liveData
- val observer = Observer<Any?> {
- if (onUpdateFun != null) {
- onUpdateFun(key)
- } else {
- update()
+ val observer =
+ Observer<Any?> {
+ if (onUpdateFun != null) {
+ onUpdateFun(key)
+ } else {
+ update()
+ }
}
- }
addSourceWithStackTraceAttribution(liveData, observer, stackTrace)
}
}
@@ -218,8 +210,11 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
timeWentInactive = null
// If this is not an async livedata, and we have sources, and all sources are non-stale,
// force update our value
- if (sources.isNotEmpty() && sources.all { !it.isStale } &&
- this !is SmartAsyncMediatorLiveData<T>) {
+ if (
+ sources.isNotEmpty() &&
+ sources.all { !it.isStale } &&
+ this !is SmartAsyncMediatorLiveData<T>
+ ) {
update()
}
super.onActive()
@@ -247,6 +242,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
update()
}
},
- isInitialized = { isInitialized && (staleOk || !isStale) })
+ isValueInitialized = { isInitialized && (staleOk || !isStale) }
+ )
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/StandardPermGroupNamesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/StandardPermGroupNamesLiveData.kt
index 3b3b76171..a3b1799de 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/StandardPermGroupNamesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/StandardPermGroupNamesLiveData.kt
@@ -19,9 +19,7 @@ package com.android.permissioncontroller.permission.data
import androidx.lifecycle.LiveData
import com.android.permissioncontroller.permission.utils.PermissionMapping
-/**
- * A LiveData which tracks Platform Permission Group names.
- */
+/** A LiveData which tracks Platform Permission Group names. */
object StandardPermGroupNamesLiveData : LiveData<List<String>>() {
init {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UnusedPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UnusedPackagesLiveData.kt
index b97c27501..89bb93dbd 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/UnusedPackagesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UnusedPackagesLiveData.kt
@@ -28,11 +28,12 @@ import com.android.permissioncontroller.hibernation.lastTimePackageUsed
import com.android.permissioncontroller.permission.utils.Utils
/**
- * Gets all unused packages from an existing live data that have not been opened in a few months
- * and the permission groups that have been revoked for them, if any. This will let us removed used
- * apps from the Unused Apps screen.
+ * Gets all unused packages from an existing live data that have not been opened in a few months and
+ * the permission groups that have been revoked for them, if any. This will let us removed used apps
+ * from the Unused Apps screen.
*
* @param sourceLiveData the live data for packages to base this list of unused apps on
+ *
* ```(packageName, user) -> [groupName]```
*/
class UnusedPackagesLiveData(
@@ -45,15 +46,9 @@ class UnusedPackagesLiveData(
private var usageStatsLiveData = UsageStatsLiveData[unusedThreshold]
init {
- addSource(usageStatsLiveData) {
- update()
- }
- addSource(AutoRevokedPackagesLiveData) {
- update()
- }
- addSource(sourceLiveData) {
- update()
- }
+ addSource(usageStatsLiveData) { update() }
+ addSource(AutoRevokedPackagesLiveData) { update() }
+ addSource(sourceLiveData) { update() }
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_PERMISSIONS,
PermissionControllerApplication.get().mainExecutor,
@@ -63,9 +58,7 @@ class UnusedPackagesLiveData(
removeSource(usageStatsLiveData)
unusedThreshold = getUnusedThresholdMs()
usageStatsLiveData = UsageStatsLiveData[unusedThreshold]
- addSource(usageStatsLiveData) {
- update()
- }
+ addSource(usageStatsLiveData) { update() }
}
}
}
@@ -73,9 +66,11 @@ class UnusedPackagesLiveData(
}
override fun onUpdate() {
- if (!usageStatsLiveData.isInitialized ||
- !AutoRevokedPackagesLiveData.isInitialized ||
- !sourceLiveData.isInitialized) {
+ if (
+ !usageStatsLiveData.isInitialized ||
+ !AutoRevokedPackagesLiveData.isInitialized ||
+ !sourceLiveData.isInitialized
+ ) {
return
}
@@ -92,8 +87,10 @@ class UnusedPackagesLiveData(
for ((user, stats) in usageStatsLiveData.value!!) {
for (stat in stats) {
val userPackage = stat.packageName to user
- if (userPackage in autoRevokedPackages &&
- (now - stat.lastTimePackageUsed()) < unusedThreshold) {
+ if (
+ userPackage in autoRevokedPackages &&
+ (now - stat.lastTimePackageUsed()) < unusedThreshold
+ ) {
unusedPackages.remove(userPackage)
}
}
@@ -111,4 +108,4 @@ fun getUnusedPackages(): UnusedPackagesLiveData {
} else {
unusedAutoRevokePackagesLiveData
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UsageStatsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UsageStatsLiveData.kt
index b7a44d1d3..72ff21a0a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/UsageStatsLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UsageStatsLiveData.kt
@@ -30,19 +30,18 @@ import kotlinx.coroutines.Job
*
* @param app The current application
* @param searchTimeMs The length of time, in milliseconds, that this LiveData will track. The time
- * will start when the liveData is loaded, and extend backwards searchTimeMs milliseconds.
+ * will start when the liveData is loaded, and extend backwards searchTimeMs milliseconds.
* @param interval The interval to measure in. Default is monthly.
*/
-class UsageStatsLiveData private constructor(
+class UsageStatsLiveData
+private constructor(
private val app: Application,
private val searchTimeMs: Long,
private val interval: Int = INTERVAL_MONTHLY
) : SmartAsyncMediatorLiveData<Map<UserHandle, List<UsageStats>>>() {
init {
- addSource(UsersLiveData) {
- update()
- }
+ addSource(UsersLiveData) { update() }
}
override suspend fun loadDataAndPostValue(job: Job) {
@@ -58,8 +57,8 @@ class UsageStatsLiveData private constructor(
if (Utils.isUserDisabledOrWorkProfile(user)) {
continue
}
- val statsManager = Utils.getUserContext(app, user).getSystemService(
- UsageStatsManager::class.java)!!
+ val statsManager =
+ Utils.getUserContext(app, user).getSystemService(UsageStatsManager::class.java)!!
statsManager.queryUsageStats(interval, now - searchTimeMs, now)?.let { stats ->
userMap[user] = stats
}
@@ -77,4 +76,4 @@ class UsageStatsLiveData private constructor(
return get(interval to INTERVAL_MONTHLY)
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt
index 02285809c..884772f37 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt
@@ -85,11 +85,13 @@ private constructor(private val app: Application, private val user: UserHandle)
)
} else if (SdkLevel.isAtLeastS()) {
app.applicationContext.packageManager.getInstalledPackagesAsUser(
- GET_PERMISSIONS or GET_ATTRIBUTIONS or MATCH_ALL, user.identifier
+ GET_PERMISSIONS or GET_ATTRIBUTIONS or MATCH_ALL,
+ user.identifier
)
} else {
app.applicationContext.packageManager.getInstalledPackagesAsUser(
- GET_PERMISSIONS or MATCH_ALL, user.identifier
+ GET_PERMISSIONS or MATCH_ALL,
+ user.identifier
)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UserSensitivityLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UserSensitivityLiveData.kt
index c138dc36d..25dc97d8a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/UserSensitivityLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UserSensitivityLiveData.kt
@@ -23,25 +23,25 @@ import android.content.pm.PackageManager
import android.os.Process
import android.os.Process.INVALID_UID
import android.os.UserHandle
-
import com.android.permissioncontroller.PermissionControllerApplication
-import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.model.livedatatypes.UidSensitivityState
import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.Utils
-import kotlinx.coroutines.Job
import java.lang.IllegalArgumentException
+import kotlinx.coroutines.Job
/**
- * Live data of the user sensitivity of either one uid, or all uids that belong to a user.
- * Maps <uid, user sensitive state>
+ * Live data of the user sensitivity of either one uid, or all uids that belong to a user. Maps
+ * <uid, user sensitive state>
*
* @param app The current application
* @param uid The uid whose user sensitivity we would like to observer, or INVALID_UID if we want
- * all uids for a user
+ * all uids for a user
* @param user The user for whom we want the uid/s
*/
-class UserSensitivityLiveData private constructor(
+class UserSensitivityLiveData
+private constructor(
private val app: Application,
private val uid: Int,
private val user: UserHandle
@@ -60,12 +60,8 @@ class UserSensitivityLiveData private constructor(
}
if (getAllUids) {
- addSource(userPackageInfosLiveData) {
- update()
- }
- addSource(LauncherPackagesLiveData) {
- update()
- }
+ addSource(userPackageInfosLiveData) { update() }
+ addSource(LauncherPackagesLiveData) { update() }
} else {
update()
}
@@ -76,10 +72,10 @@ class UserSensitivityLiveData private constructor(
if (!getAllUids) {
val uidHasPackages = getAndObservePackageLiveDatas()
- if (!uidHasPackages || packageLiveDatas.all {
- it.value.isInitialized &&
- it.value.value == null
- }) {
+ if (
+ !uidHasPackages ||
+ packageLiveDatas.all { it.value.isInitialized && it.value.value == null }
+ ) {
packageLiveDatas.clear()
invalidateSingle(uid to user)
postValue(null)
@@ -88,11 +84,12 @@ class UserSensitivityLiveData private constructor(
return
}
}
- val pkgs = if (getAllUids) {
- userPackageInfosLiveData.value ?: return
- } else {
- packageLiveDatas.mapNotNull { it.value.value }
- }
+ val pkgs =
+ if (getAllUids) {
+ userPackageInfosLiveData.value ?: return
+ } else {
+ packageLiveDatas.mapNotNull { it.value.value }
+ }
if (job.isCancelled) {
return
}
@@ -105,17 +102,19 @@ class UserSensitivityLiveData private constructor(
for (pkg in pkgs) {
// sensitivityState for one uid
- val userSensitiveState = sensitiveStatePerUid.getOrPut(pkg.uid) {
- UidSensitivityState(mutableSetOf(), mutableMapOf())
- }
+ val userSensitiveState =
+ sensitiveStatePerUid.getOrPut(pkg.uid) {
+ UidSensitivityState(mutableSetOf(), mutableMapOf())
+ }
userSensitiveState.packages.add(pkg)
- val pkgHasLauncherIcon = if (getAllUids) {
- // The launcher packages set will only be null when it is uninitialized.
- LauncherPackagesLiveData.value?.contains(pkg.packageName) ?: return
- } else {
- KotlinUtils.packageHasLaunchIntent(context, pkg.packageName)
- }
+ val pkgHasLauncherIcon =
+ if (getAllUids) {
+ // The launcher packages set will only be null when it is uninitialized.
+ LauncherPackagesLiveData.value?.contains(pkg.packageName) ?: return
+ } else {
+ KotlinUtils.packageHasLaunchIntent(context, pkg.packageName)
+ }
val pkgIsSystemApp = pkg.appFlags and ApplicationInfo.FLAG_SYSTEM != 0
// Iterate through all runtime perms, setting their keys
for (perm in pkg.requestedPermissions.intersect(runtimePerms)) {
@@ -125,18 +124,20 @@ class UserSensitivityLiveData private constructor(
* - the permission is not pre-granted, or
* - the package is not a system app (i.e. not preinstalled)
*/
- var flags = if (pkgIsSystemApp && !pkgHasLauncherIcon) {
- val permGrantedByDefault = pm.getPermissionFlags(perm, pkg.packageName,
- user) and PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT != 0
-
- if (permGrantedByDefault) {
- 0
+ var flags =
+ if (pkgIsSystemApp && !pkgHasLauncherIcon) {
+ val permGrantedByDefault =
+ pm.getPermissionFlags(perm, pkg.packageName, user) and
+ PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT != 0
+
+ if (permGrantedByDefault) {
+ 0
+ } else {
+ PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+ }
} else {
- PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+ Utils.FLAGS_ALWAYS_USER_SENSITIVE
}
- } else {
- Utils.FLAGS_ALWAYS_USER_SENSITIVE
- }
/*
* If two packages share a UID there can be two cases:
@@ -147,11 +148,12 @@ class UserSensitivityLiveData private constructor(
*/
val previousFlags = userSensitiveState.permStates[perm]
if (previousFlags != null) {
- flags = if (pkg.uid < Process.FIRST_APPLICATION_UID) {
- flags and previousFlags
- } else {
- flags or previousFlags
- }
+ flags =
+ if (pkg.uid < Process.FIRST_APPLICATION_UID) {
+ flags and previousFlags
+ } else {
+ flags or previousFlags
+ }
}
userSensitiveState.permStates[perm] = flags
@@ -165,21 +167,27 @@ class UserSensitivityLiveData private constructor(
}
private fun getAndObservePackageLiveDatas(): Boolean {
- val packageNames = app.packageManager.getPackagesForUid(uid)?.toList() ?: emptyList()
- val getLiveData = { packageName: String -> LightPackageInfoLiveData[packageName, user] }
- setSourcesToDifference(packageNames, packageLiveDatas, getLiveData)
- return packageNames.isNotEmpty()
+ synchronized(this) {
+ val packageNames = app.packageManager.getPackagesForUid(uid)?.toList() ?: emptyList()
+ val getLiveData = { packageName: String -> LightPackageInfoLiveData[packageName, user] }
+ setSourcesToDifference(packageNames, packageLiveDatas, getLiveData)
+ return packageNames.isNotEmpty()
+ }
}
/**
* Repository for a UserSensitivityLiveData
- * <p> Key value is a pair of int uid (INVALID_UID for all uids), and UserHandle,
- * value is its corresponding LiveData.
+ *
+ * <p> Key value is a pair of int uid (INVALID_UID for all uids), and UserHandle, value is its
+ * corresponding LiveData.
*/
companion object : DataRepository<Pair<Int, UserHandle>, UserSensitivityLiveData>() {
override fun newValue(key: Pair<Int, UserHandle>): UserSensitivityLiveData {
- return UserSensitivityLiveData(PermissionControllerApplication.get(), key.first,
- key.second)
+ return UserSensitivityLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second
+ )
}
/**
@@ -187,7 +195,6 @@ class UserSensitivityLiveData private constructor(
* throw an exception if the uid is INVALID_UID.
*
* @param uid The uid for which we want the liveData
- *
* @return The liveData associated with the given UID
*/
operator fun get(uid: Int): UserSensitivityLiveData {
@@ -201,7 +208,6 @@ class UserSensitivityLiveData private constructor(
* Gets a liveData for a user, which will track all uids under
*
* @param user The user for whom we want the liveData
- *
* @return The liveData associated with that user, for all uids
*/
operator fun get(user: UserHandle): UserSensitivityLiveData {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UsersLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UsersLiveData.kt
index 0e78ec5f6..0fe3f6007 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/UsersLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UsersLiveData.kt
@@ -23,31 +23,26 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.UserHandle
import android.os.UserManager
-
import com.android.permissioncontroller.PermissionControllerApplication
-import com.android.permissioncontroller.permission.utils.Utils
/**
* Live data of the users of the current profile group.
*
- *
* Data source: system server
*/
object UsersLiveData : SmartUpdateMediatorLiveData<List<UserHandle>>() {
- @SuppressLint("StaticFieldLeak")
- private val app = PermissionControllerApplication.get()
+ @SuppressLint("StaticFieldLeak") private val app = PermissionControllerApplication.get()
- /** Monitors changes to the users on this device */
- private val mUserMonitor = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- onUpdate()
+ /** Monitors changes to the users on this device */
+ private val mUserMonitor =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ onUpdate()
+ }
}
- }
- /**
- * Update the encapsulated data with the current list of users.
- */
+ /** Update the encapsulated data with the current list of users. */
override fun onUpdate() {
value = app.getSystemService(UserManager::class.java)!!.userProfiles
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt
index 6483c9f9c..74b29043d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt
@@ -117,7 +117,9 @@ class AllLightPackageOpsLiveData(app: Application) :
.filter { UserHandle.getUserHandleForUid(it.uid) in allProfilesInCurrentUser }
.associateBy(
{ Pair(it.packageName, UserHandle.getUserHandleForUid(it.uid)) },
- { LightPackageOps(opNames, it) }))
+ { LightPackageOps(opNames, it) }
+ )
+ )
}
override fun onOpChanged(op: String?, packageName: String?) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v33/RecentPermissionDecisionsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v33/RecentPermissionDecisionsLiveData.kt
index 4c18d6987..ec4f936b7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/v33/RecentPermissionDecisionsLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v33/RecentPermissionDecisionsLiveData.kt
@@ -20,8 +20,8 @@ import android.os.Build
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData
-import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl
import com.android.permissioncontroller.permission.service.PermissionEventStorage
+import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl
import kotlinx.coroutines.Job
/** Gets all recent permission decisions made by the user. */
@@ -39,8 +39,6 @@ class RecentPermissionDecisionsLiveData(
// no need to subscribe to decision changes, since those will also be bubbled up through
// package info changes
- recentDecisionsStorage.loadEvents().also {
- postValue(it)
- }
+ recentDecisionsStorage.loadEvents().also { postValue(it) }
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/AppDataSharingUpdatesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/AppDataSharingUpdatesLiveData.kt
index d5fd59242..7844a172f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/AppDataSharingUpdatesLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/AppDataSharingUpdatesLiveData.kt
@@ -40,14 +40,16 @@ class AppDataSharingUpdatesLiveData(val app: Application) :
DeviceConfig.getLong(
DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_DATA_SHARING_UPDATE_PERIOD_MILLIS,
- Duration.ofDays(DEFAULT_DATA_SHARING_UPDATE_PERIOD_DAYS).toMillis())
+ Duration.ofDays(DEFAULT_DATA_SHARING_UPDATE_PERIOD_DAYS).toMillis()
+ )
val file =
AppsSafetyLabelHistoryPersistence.getSafetyLabelHistoryFile(app.applicationContext)
val appSafetyLabelDiffsFromPersistence =
AppsSafetyLabelHistoryPersistence.getAppSafetyLabelDiffs(
Instant.now().atZone(ZoneId.systemDefault()).toInstant().minusMillis(updatePeriod),
- file)
+ file
+ )
val updatesFromPersistence =
appSafetyLabelDiffsFromPersistence.mapNotNull { it.buildUpdateIfSignificantChange() }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt
index bbc62dfc9..716d8dfe5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt
@@ -74,7 +74,9 @@ private constructor(
try {
val installSourceInfo = getInstallSourceInfo(packageName)
LightInstallSourceInfo(
- installSourceInfo.packageSource, installSourceInfo.initiatingPackageName)
+ installSourceInfo.packageSource,
+ installSourceInfo.initiatingPackageName
+ )
} catch (e: PackageManager.NameNotFoundException) {
Log.w(LOG_TAG, "InstallSourceInfo for $packageName not found")
invalidateSingle(packageName to user)
@@ -101,7 +103,10 @@ private constructor(
override fun newValue(key: Pair<String, UserHandle>): LightInstallSourceInfoLiveData {
return LightInstallSourceInfoLiveData(
- PermissionControllerApplication.get(), key.first, key.second)
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt
index 6229218d4..5c63de6ce 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt
@@ -115,7 +115,8 @@ private constructor(
}
return SafetyLabel.getSafetyLabelFromMetadata(
- userContext.packageManager.getAppMetadata(packageName))
+ userContext.packageManager.getAppMetadata(packageName)
+ )
}
companion object :
@@ -124,7 +125,10 @@ private constructor(
override fun newValue(key: Pair<String, UserHandle>): SafetyLabelInfoLiveData {
return SafetyLabelInfoLiveData(
- PermissionControllerApplication.get(), key.first, key.second)
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt
index 7b5b7994a..fd9c49f3d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt
@@ -23,25 +23,23 @@ import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_EX
* Tracks the setting state of hibernation and auto revoke for a package
*
* @param hibernationEligibility state saying whether the package is eligible for hibernation. See
- * [HIBERNATION_ELIGIBILITY_ELIGIBLE].
+ * [HIBERNATION_ELIGIBILITY_ELIGIBLE].
* @param revocableGroupNames A list of which permission groups of this package are eligible for
- * auto-revoke. A permission group is auto-revocable if it does not contain a default granted
- * permission.
+ * auto-revoke. A permission group is auto-revocable if it does not contain a default granted
+ * permission.
*/
data class HibernationSettingState(
val hibernationEligibility: Int,
val revocableGroupNames: List<String>
) {
- /**
- * Whether package will hibernate if it is unused.
- */
+ /** Whether package will hibernate if it is unused. */
fun isEligibleForHibernation(): Boolean {
return hibernationEligibility == HIBERNATION_ELIGIBILITY_ELIGIBLE
}
/**
- * Whether the package is exempt from hibernation by the system. This means the app can never
- * be hibernated, and the user setting to exempt it is disabled.
+ * Whether the package is exempt from hibernation by the system. This means the app can never be
+ * hibernated, and the user setting to exempt it is disabled.
*/
fun isExemptBySystem(): Boolean {
return hibernationEligibility == HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
index e98f01e47..c7e78d414 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
@@ -29,12 +29,12 @@ import com.android.permissioncontroller.permission.utils.Utils
* @param packageInfo Information about the package
* @param permGroupInfo Information about the permission group
* @param allPermissions The permissions in the permission group that the package requests
- * (including restricted ones).
+ * (including restricted ones).
* @param hasInstallToRuntimeSplit If this group contains a permission that was previously an
- * install permission, but is currently a runtime permission
+ * install permission, but is currently a runtime permission
* @param specialLocationGrant If this package is the location provider, or the extra location
- * package, then the grant state of the group is not determined by the grant state of individual
- * permissions, but by other system properties
+ * package, then the grant state of the group is not determined by the grant state of individual
+ * permissions, but by other system properties
*/
data class LightAppPermGroup(
val packageInfo: LightPackageInfo,
@@ -49,25 +49,17 @@ data class LightAppPermGroup(
perms: Map<String, LightPermission>
) : this(pI, pGI, perms, false, null)
- /**
- * All unrestricted permissions. Usually restricted permissions are ignored
- */
+ /** All unrestricted permissions. Usually restricted permissions are ignored */
val permissions: Map<String, LightPermission> =
- allPermissions.filter { (_, permission) -> !permission.isRestricted }
+ allPermissions.filter { (_, permission) -> !permission.isRestricted }
- /**
- * The package name of this group
- */
+ /** The package name of this group */
val packageName = packageInfo.packageName
- /**
- * The permission group name of this group
- */
+ /** The permission group name of this group */
val permGroupName = permGroupInfo.name
- /**
- * The current userHandle of this AppPermGroup.
- */
+ /** The current userHandle of this AppPermGroup. */
val userHandle: UserHandle = UserHandle.getUserHandleForUid(packageInfo.uid)
/**
@@ -76,36 +68,40 @@ data class LightAppPermGroup(
*/
val backgroundPermNames = permissions.mapNotNull { it.value.backgroundPermission }
- /**
- * All foreground permissions in the permission group which are requested by the package.
- */
- val foregroundPermNames get() = permissions.mapNotNull { (name, _) ->
- if (name !in backgroundPermNames) name else null
- }
+ /** All foreground permissions in the permission group which are requested by the package. */
+ val foregroundPermNames
+ get() =
+ permissions.mapNotNull { (name, _) -> if (name !in backgroundPermNames) name else null }
val isPlatformPermissionGroup = permGroupInfo.packageName == Utils.OS_PKG
- val foreground = AppPermSubGroup(permissions.filter { it.key in foregroundPermNames },
- packageInfo, isPlatformPermissionGroup, specialLocationGrant)
+ val foreground =
+ AppPermSubGroup(
+ permissions.filter { it.key in foregroundPermNames },
+ packageInfo,
+ isPlatformPermissionGroup,
+ specialLocationGrant
+ )
- val background = AppPermSubGroup(permissions.filter { it.key in backgroundPermNames },
- packageInfo, isPlatformPermissionGroup, specialLocationGrant)
+ val background =
+ AppPermSubGroup(
+ permissions.filter { it.key in backgroundPermNames },
+ packageInfo,
+ isPlatformPermissionGroup,
+ specialLocationGrant
+ )
- /**
- * Whether or not this App Permission Group has a permission which has a background mode
- */
+ /** Whether or not this App Permission Group has a permission which has a background mode */
val hasPermWithBackgroundMode = backgroundPermNames.isNotEmpty()
- /**
- * Whether or not this App Permission Group requests a background permission
- */
+ /** Whether or not this App Permission Group requests a background permission */
val hasBackgroundGroup = backgroundPermNames.any { permissions.contains(it) }
/**
* Whether this App Permission Group's background and foreground permissions are fixed by policy
*/
- val isPolicyFullyFixed = foreground.isPolicyFixed && (!hasBackgroundGroup ||
- background.isPolicyFixed)
+ val isPolicyFullyFixed =
+ foreground.isPolicyFixed && (!hasBackgroundGroup || background.isPolicyFixed)
/**
* Whether this App Permission Group's background permissions are fixed by the system or policy
@@ -117,78 +113,60 @@ data class LightAppPermGroup(
*/
val isForegroundFixed = foreground.isPolicyFixed || foreground.isSystemFixed
- /**
- * Whether or not this group supports runtime permissions
- */
+ /** Whether or not this group supports runtime permissions */
val supportsRuntimePerms = packageInfo.targetSdkVersion >= Build.VERSION_CODES.M
/**
* Whether this App Permission Group is one-time. 2 cases:
* 1. If the perm group is not LOCATION, check if any of the permissions is one-time and none of
- * the granted permissions are not one-time.
+ * the granted permissions are not one-time.
* 2. If the perm group is LOCATION, check if ACCESS_COARSE_LOCATION is one-time.
*/
- val isOneTime = (permGroupName != Manifest.permission_group.LOCATION &&
+ val isOneTime =
+ (permGroupName != Manifest.permission_group.LOCATION &&
permissions.any { it.value.isOneTime } &&
permissions.none { !it.value.isOneTime && it.value.isGranted }) ||
(permGroupName == Manifest.permission_group.LOCATION &&
- permissions[ACCESS_COARSE_LOCATION]?.isOneTime == true)
+ permissions[ACCESS_COARSE_LOCATION]?.isOneTime == true)
- /**
- * Whether any permissions in this group are granted by default (pregrant)
- */
+ /** Whether any permissions in this group are granted by default (pregrant) */
val isGrantedByDefault = foreground.isGrantedByDefault || background.isGrantedByDefault
- /**
- * Whether any permissions in this group are granted by being a role holder
- */
+ /** Whether any permissions in this group are granted by being a role holder */
val isGrantedByRole = foreground.isGrantedByRole || background.isGrantedByRole
- /**
- * Whether any of the permission (foreground/background) is fixed by the system
- */
+ /** Whether any of the permission (foreground/background) is fixed by the system */
val isSystemFixed = foreground.isSystemFixed || background.isSystemFixed
- /**
- * Whether any of the permission (foreground/background) in this group requires a review
- */
+ /** Whether any of the permission (foreground/background) in this group requires a review */
val isReviewRequired = foreground.isReviewRequired || background.isReviewRequired
- /**
- * Whether any of the permission (foreground/background) is granted in this permission group
- */
+ /** Whether any of the permission (foreground/background) is granted in this permission group */
var isGranted = foreground.isGranted || background.isGranted
- /**
- * Whether any permissions in this group are user sensitive
- */
+ /** Whether any permissions in this group are user sensitive */
val isUserSensitive = permissions.any { it.value.isUserSensitive }
- /**
- * Whether any permissions in this group are revoke-when-requested
- */
+ /** Whether any permissions in this group are revoke-when-requested */
val isRevokeWhenRequested = permissions.any { it.value.isRevokeWhenRequested }
- /**
- * Whether any of this App Permission Groups permissions are fixed by the user
- */
+ /** Whether any of this App Permission Groups permissions are fixed by the user */
val isUserFixed = foreground.isUserFixed || background.isUserFixed
- /**
- * Whether any of this App Permission Group's permissions are set by the user
- */
+ /** Whether any of this App Permission Group's permissions are set by the user */
val isUserSet = foreground.isUserSet || background.isUserSet
/**
- * A subset of the AppPermissionGroup, representing either the background or foreground permissions
- * of the full group.
+ * A subset of the AppPermissionGroup, representing either the background or foreground
+ * permissions of the full group.
*
- * @param permissions The permissions contained within this subgroup, a subset of those contained
- * in the full group
+ * @param permissions The permissions contained within this subgroup, a subset of those
+ * contained in the full group
* @param isPlatformPermissionGroup Whether this is a platform permission group
* @param specialLocationGrant Whether this is a special location package
*/
- data class AppPermSubGroup internal constructor(
+ data class AppPermSubGroup
+ internal constructor(
private val permissions: Map<String, LightPermission>,
private val packageInfo: LightPackageInfo,
private val isPlatformPermissionGroup: Boolean,
@@ -206,9 +184,9 @@ data class LightAppPermGroup(
/**
* Whether this App Permission SubGroup should be treated as granted. This means either:
* 1) At least one permission was granted excluding auto-granted permissions (i.e., granted
- * during install time with flag RevokeWhenRequested.) Or,
+ * during install time with flag RevokeWhenRequested.) Or,
* 2) All permissions were auto-granted (all permissions are all granted and all
- * RevokeWhenRequested.)
+ * RevokeWhenRequested.)
*/
val allowFullGroupGrant =
specialLocationGrant
@@ -216,20 +194,17 @@ data class LightAppPermGroup(
val mayGrantByPlatformOrSystem =
!isPlatformPermissionGroup || it.value.isPlatformOrSystem
it.value.allowFullGroupGrant && mayGrantByPlatformOrSystem
- } || permissions.all {
- it.value.isGranted && it.value.isRevokeWhenRequested
- })
+ } || permissions.all { it.value.isGranted && it.value.isRevokeWhenRequested })
- /**
- * Whether any of this App Permission SubGroup's permissions are granted by default
- */
+ /** Whether any of this App Permission SubGroup's permissions are granted by default */
val isGrantedByDefault = permissions.any { it.value.isGrantedByDefault }
/**
- * Whether at least one of this App Permission SubGroup's permissions is one-time and
- * none of the granted permissions are not one-time.
+ * Whether at least one of this App Permission SubGroup's permissions is one-time and none
+ * of the granted permissions are not one-time.
*/
- val isOneTime = permissions.any { it.value.isOneTime } &&
+ val isOneTime =
+ permissions.any { it.value.isOneTime } &&
permissions.none { it.value.isGranted && !it.value.isOneTime }
/**
@@ -237,24 +212,16 @@ data class LightAppPermGroup(
*/
val isPolicyFixed = permissions.any { it.value.isPolicyFixed }
- /**
- * Whether any of this App Permission Subgroup's permissions are fixed by the system
- */
+ /** Whether any of this App Permission Subgroup's permissions are fixed by the system */
val isSystemFixed = permissions.any { it.value.isSystemFixed }
- /**
- * Whether any of this App Permission Subgroup's permissions are fixed by the user
- */
+ /** Whether any of this App Permission Subgroup's permissions are fixed by the user */
val isUserFixed = permissions.any { it.value.isUserFixed }
- /**
- * Whether any of this App Permission Subgroup's permissions are set by the user
- */
+ /** Whether any of this App Permission Subgroup's permissions are set by the user */
val isUserSet = permissions.any { it.value.isUserSet }
- /**
- * whether review is required or not for the permission group
- */
+ /** whether review is required or not for the permission group */
val isReviewRequired = permissions.any { it.value.isReviewRequired }
/**
@@ -266,10 +233,9 @@ data class LightAppPermGroup(
private val hasInstantPerm = permissions.any { (_, perm) -> perm.isInstantPerm }
- /**
- * Whether or not any permissions in this App Permission Subgroup can be granted
- */
- val isGrantable = (!packageInfo.isInstantApp || hasInstantPerm) &&
+ /** Whether or not any permissions in this App Permission Subgroup can be granted */
+ val isGrantable =
+ (!packageInfo.isInstantApp || hasInstantPerm) &&
(packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntimePerm)
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt
index cb6c47c76..f8e395c16 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt
@@ -60,7 +60,8 @@ data class LightPackageInfo(
pI.packageName,
pI.permissions?.map { perm ->
LightPermInfo(perm, pI.applicationInfo!!.flags and ApplicationInfo.FLAG_SYSTEM != 0)
- } ?: emptyList(),
+ }
+ ?: emptyList(),
pI.requestedPermissions?.toList() ?: emptyList(),
pI.requestedPermissionsFlags?.toList() ?: emptyList(),
pI.applicationInfo.uid,
@@ -71,15 +72,17 @@ data class LightPackageInfo(
pI.firstInstallTime,
pI.lastUpdateTime,
if (SdkLevel.isAtLeastS()) pI.applicationInfo.areAttributionsUserVisible() else false,
- if (SdkLevel.isAtLeastS()) buildAttributionTagsToLabelsMap(pI.attributions) else emptyMap())
+ if (SdkLevel.isAtLeastS()) buildAttributionTagsToLabelsMap(pI.attributions) else emptyMap()
+ )
/** Permissions which are granted according to the [requestedPermissionsFlags] */
val grantedPermissions: List<String>
get() {
val grantedPermissions = mutableListOf<String>()
for (i in 0 until requestedPermissions.size) {
- if ((requestedPermissionsFlags[i] and PackageInfo.REQUESTED_PERMISSION_GRANTED) !=
- 0) {
+ if (
+ (requestedPermissionsFlags[i] and PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0
+ ) {
grantedPermissions.add(requestedPermissions[i])
}
}
@@ -91,9 +94,8 @@ data class LightPackageInfo(
* often.
*
* @param app The current application, which will be used to get the ApplicationInfo
- *
* @return The ApplicationInfo corresponding to this package, with this UID, or null, if no such
- * package exists
+ * package exists
*/
fun getApplicationInfo(app: Application): ApplicationInfo? {
try {
@@ -114,7 +116,9 @@ data class LightPackageInfo(
try {
val userContext = Utils.getUserContext(app, UserHandle.getUserHandleForUid(uid))
return userContext.packageManager.getPackageInfo(
- packageName, PackageManager.GET_PERMISSIONS)
+ packageName,
+ PackageManager.GET_PERMISSIONS
+ )
} catch (e: PackageManager.NameNotFoundException) {}
return null
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermGroupInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermGroupInfo.kt
index 6aff2f3c9..7abf5ff1a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermGroupInfo.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermGroupInfo.kt
@@ -41,21 +41,22 @@ data class LightPermGroupInfo(
val isSinglePermGroup: Boolean
) {
- constructor(pII: PackageItemInfo) : this(pII.name, pII.packageName, pII.labelRes, pII.icon,
- 0, pII is PermissionInfo)
+ constructor(
+ pII: PackageItemInfo
+ ) : this(pII.name, pII.packageName, pII.labelRes, pII.icon, 0, pII is PermissionInfo)
- constructor(pGI: PermissionGroupInfo) : this(pGI.name, pGI.packageName, pGI.labelRes, pGI.icon,
- pGI.descriptionRes, false)
+ constructor(
+ pGI: PermissionGroupInfo
+ ) : this(pGI.name, pGI.packageName, pGI.labelRes, pGI.icon, pGI.descriptionRes, false)
/**
* Gets the PackageItemInfo for this permission group from the system.
*
* @param app The current application, which will be used to get the PackageItemInfo
- *
- * @return The PackageItemInfo corresponding to this permission group, or null, if no
- * such group exists
+ * @return The PackageItemInfo corresponding to this permission group, or null, if no such group
+ * exists
*/
fun toPackageItemInfo(app: Application): PackageItemInfo? {
return Utils.getGroupInfo(name, app.applicationContext)
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt
index 582742da4..caa4d0756 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt
@@ -42,24 +42,31 @@ data class LightPermInfo(
val flags: Int,
val isSystem: Boolean?
) {
- constructor (permInfo: PermissionInfo, isSystem: Boolean?) : this(
- permInfo.name, permInfo.packageName, permInfo.group, permInfo.backgroundPermission,
- permInfo.protection, permInfo.protectionFlags, permInfo.flags, isSystem
+ constructor(
+ permInfo: PermissionInfo,
+ isSystem: Boolean?
+ ) : this(
+ permInfo.name,
+ permInfo.packageName,
+ permInfo.group,
+ permInfo.backgroundPermission,
+ permInfo.protection,
+ permInfo.protectionFlags,
+ permInfo.flags,
+ isSystem
)
/**
* Gets the PermissionInfo for this permission from the system.
*
* @param app The current application, which will be used to get the PermissionInfo
- *
- * @return The PermissionInfo corresponding to this permission, or null, if no
- * such permission exists
+ * @return The PermissionInfo corresponding to this permission, or null, if no such permission
+ * exists
*/
fun toPermissionInfo(app: Application): PermissionInfo? {
try {
return app.packageManager.getPermissionInfo(name, 0)
- } catch (e: PackageManager.NameNotFoundException) {
- }
+ } catch (e: PackageManager.NameNotFoundException) {}
return null
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt
index 0ee60e5bf..66a076551 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt
@@ -28,11 +28,11 @@ import com.android.permissioncontroller.permission.utils.Utils
*
* @param pkgInfo The package requesting the permission
* @param permInfo The permissionInfo this represents
- * @param isGranted Whether or not this permission is functionally granted.
- * A non-granted app op but granted permission is counted as not granted
+ * @param isGranted Whether or not this permission is functionally granted. A non-granted app op but
+ * granted permission is counted as not granted
* @param flags The PermissionController flags for this permission
* @param foregroundPerms The foreground permission names corresponding to this permission, if this
- * permission is a background permission
+ * permission is a background permission
*/
data class LightPermission(
val pkgInfo: LightPackageInfo,
@@ -47,14 +47,13 @@ data class LightPermission(
permInfo: LightPermInfo,
permState: PermState,
foregroundPerms: List<String>?
- ) :
- this(pkgInfo, permInfo, permState.granted, permState.permFlags, foregroundPerms)
+ ) : this(pkgInfo, permInfo, permState.granted, permState.permFlags, foregroundPerms)
/** The name of this permission */
val name = permInfo.name
/** The background permission name of this permission, if it exists */
val backgroundPermission: String? = permInfo.backgroundPermission
- /** If this is a background permission **/
+ /** If this is a background permission */
val isBackgroundPermission = foregroundPerms?.isNotEmpty() ?: false
/** Whether this permission is fixed by policy */
val isPolicyFixed = flags and PackageManager.FLAG_PERMISSION_POLICY_FIXED != 0
@@ -76,9 +75,10 @@ data class LightPermission(
val isImplicit: Boolean by lazy {
var implicit = false
for ((permName, permFlags) in
- pkgInfo.requestedPermissions.zip(pkgInfo.requestedPermissionsFlags)) {
- if (permName == permInfo.name &&
- (permFlags and PackageInfo.REQUESTED_PERMISSION_IMPLICIT) != 0
+ pkgInfo.requestedPermissions.zip(pkgInfo.requestedPermissionsFlags)) {
+ if (
+ permName == permInfo.name &&
+ (permFlags and PackageInfo.REQUESTED_PERMISSION_IMPLICIT) != 0
) {
implicit = true
break
@@ -96,23 +96,25 @@ data class LightPermission(
/** Whether this permission is set to be revoked upon being requested */
val isRevokeWhenRequested = flags and PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED != 0
/** Whether this permission is user sensitive in its current grant state */
- val isUserSensitive = !isRuntimePlatformPermission(permInfo.name) ||
+ val isUserSensitive =
+ !isRuntimePlatformPermission(permInfo.name) ||
(isGranted &&
- (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0) ||
+ (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0) ||
(!isGranted &&
- (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0)
+ (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0)
/** Whether the permission is restricted */
- val isRestricted = when {
- (permInfo.flags and PermissionInfo.FLAG_HARD_RESTRICTED) != 0 -> {
- flags and Utils.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT == 0
- }
- (permInfo.flags and PermissionInfo.FLAG_SOFT_RESTRICTED) != 0 -> {
- !SoftRestrictedPermissionPolicy.shouldShow(pkgInfo, permInfo.name, flags)
- }
- else -> {
- false
+ val isRestricted =
+ when {
+ (permInfo.flags and PermissionInfo.FLAG_HARD_RESTRICTED) != 0 -> {
+ flags and Utils.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT == 0
+ }
+ (permInfo.flags and PermissionInfo.FLAG_SOFT_RESTRICTED) != 0 -> {
+ !SoftRestrictedPermissionPolicy.shouldShow(pkgInfo, permInfo.name, flags)
+ }
+ else -> {
+ false
+ }
}
- }
/** Whether the permission is auto revoked */
val isAutoRevoked = flags and PackageManager.FLAG_PERMISSION_AUTO_REVOKED != 0
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroup.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroup.kt
index c5079a950..e6913f57e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroup.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroup.kt
@@ -17,8 +17,8 @@
package com.android.permissioncontroller.permission.model.livedatatypes
/**
- * A permission Group, represented as a PackageItemInfo groupInfo, and a map of permission name
- * to PermissionInfo objects.
+ * A permission Group, represented as a PackageItemInfo groupInfo, and a map of permission name to
+ * PermissionInfo objects.
*
* @param groupInfo information about the permission group
* @param permissionInfos the Permissions in this group
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroupPackagesUiInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroupPackagesUiInfo.kt
index e11e895a9..ba4aa0a20 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroupPackagesUiInfo.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroupPackagesUiInfo.kt
@@ -22,17 +22,17 @@ package com.android.permissioncontroller.permission.model.livedatatypes
*
* @param name The name of the permission group whose UI data this represents
* @param nonSystemTotal The total number of non-system applications that request permissions in
- * this group
+ * this group
* @param nonSystemGranted The total number of non-system applications that request permissions in
- * this group, and have at least one permission in this group granted.
+ * this group, and have at least one permission in this group granted.
* @param nonSystemUserSetOrPreGranted The total number of non-system applications that request
- * permissions in this group, and have at least one permission in this group granted, or one
- * permission denied by the user
- * @param systemGranted The total number of system applications that request permissions in
- * this group, and have at least one permission in this group granted.
- * @param systemUserSetOrPreGranted The total number of system applications that request
- * permissions in this group, and have at least one permission in this group granted, or one
- * permission denied by the user
+ * permissions in this group, and have at least one permission in this group granted, or one
+ * permission denied by the user
+ * @param systemGranted The total number of system applications that request permissions in this
+ * group, and have at least one permission in this group granted.
+ * @param systemUserSetOrPreGranted The total number of system applications that request permissions
+ * in this group, and have at least one permission in this group granted, or one permission denied
+ * by the user
*/
data class PermGroupPackagesUiInfo(
val name: String,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/UidSensitivityState.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/UidSensitivityState.kt
index 3d0be0c0a..d8cdf01d6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/UidSensitivityState.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/UidSensitivityState.kt
@@ -22,8 +22,8 @@ package com.android.permissioncontroller.permission.model.livedatatypes
*
* @param packages A LightPackageInfo for every package with this uid
* @param permStates A map <requested permission name, use sensitive state>, with the state being a
- * combination of FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED and
- * FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
+ * combination of FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED and
+ * FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
*/
data class UidSensitivityState(
val packages: MutableSet<LightPackageInfo>,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt
index 4c2051f9c..d5451c208 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt
@@ -82,7 +82,10 @@ data class LightHistoricalPackageOps(
this.getDiscreteAccesses(permissionToOpNames.value)?.let {
appPermissionDiscreteAccesses.add(
AppPermissionDiscreteAccesses(
- AppPermissionId(packageName, userHandle, permissionToOpNames.key), it))
+ AppPermissionId(packageName, userHandle, permissionToOpNames.key),
+ it
+ )
+ )
}
}
@@ -117,7 +120,9 @@ data class LightHistoricalPackageOps(
mutableMapOf()
}
attributedAppPermissionDiscreteAccesses[appPermissionId]?.put(
- attributedHistoricalOps.tag ?: NO_ATTRIBUTION_TAG, discAccessData)
+ attributedHistoricalOps.tag ?: NO_ATTRIBUTION_TAG,
+ discAccessData
+ )
}
}
}
@@ -152,7 +157,9 @@ data class LightHistoricalPackageOps(
DiscreteAccess(
opEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS),
opEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS),
- opEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)))
+ opEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)
+ )
+ )
}
}
@@ -187,7 +194,9 @@ data class LightHistoricalPackageOps(
DiscreteAccess(
attributedOpEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS),
attributedOpEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS),
- attributedOpEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)))
+ attributedOpEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)
+ )
+ )
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt
index dde4857e2..b65fda5ea 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt
@@ -44,7 +44,8 @@ data class LightPackageOps(
) : this(
packageOps.packageName,
UserHandle.getUserHandleForUid(packageOps.uid),
- createLastPermissionGroupAccessTimesMap(ops, packageOps))
+ createLastPermissionGroupAccessTimesMap(ops, packageOps)
+ )
/** Companion object for [LightPackageOps]. */
companion object {
@@ -70,7 +71,8 @@ data class LightPackageOps(
lastAccessTimeMs[permissionGroupOfOp] =
maxOf(
lastAccessTimeMs[permissionGroupOfOp] ?: -1,
- opEntry.getLastAccessTime(OPS_LAST_ACCESS_FLAGS))
+ opEntry.getLastAccessTime(OPS_LAST_ACCESS_FLAGS)
+ )
}
return lastAccessTimeMs
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt
index e75c1eadf..85f77b823 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt
@@ -36,12 +36,12 @@ class LightInstallSourceInfo {
// default source of unspecified. All other sources should be explicitly set to another
// PACKAGE_SOURCE_ value
val isStoreInstalled =
- initiatingPackageName != null &&
- (packageSource == PACKAGE_SOURCE_STORE ||
- packageSource == PACKAGE_SOURCE_UNSPECIFIED)
+ initiatingPackageName != null &&
+ (packageSource == PACKAGE_SOURCE_STORE ||
+ packageSource == PACKAGE_SOURCE_UNSPECIFIED)
- isPreloadedApp = initiatingPackageName == null &&
- packageSource == PACKAGE_SOURCE_UNSPECIFIED
+ isPreloadedApp =
+ initiatingPackageName == null && packageSource == PACKAGE_SOURCE_UNSPECIFIED
supportsSafetyLabel = isStoreInstalled || isPreloadedApp
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt
index 7128e3069..2107e9944 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt
@@ -28,7 +28,7 @@ import com.android.permissioncontroller.permission.model.livedatatypes.v34.Light
class SafetyLabelInfo(
val safetyLabel: SafetyLabel?,
val installSourceInfo: LightInstallSourceInfo
- ) {
+) {
companion object {
/** Default definition of unavailable or no safety label found */
val UNAVAILABLE = SafetyLabelInfo(null, INSTALL_SOURCE_UNAVAILABLE)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt
index 3845a73dc..ae9a1ce8d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt
@@ -32,25 +32,25 @@ import com.android.permissioncontroller.PermissionControllerStatsLog
import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED
import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED
import com.android.permissioncontroller.hibernation.getUnusedThresholdMs
-import com.android.permissioncontroller.permission.utils.PermissionMapping
+import com.android.permissioncontroller.permission.data.AutoRevokedPackagesLiveData
import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
import com.android.permissioncontroller.permission.data.get
import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.application
import com.android.permissioncontroller.permission.utils.forEachInParallel
import com.android.permissioncontroller.permission.utils.updatePermissionFlags
-import kotlinx.coroutines.Dispatchers.Main
import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.Dispatchers.Main
private const val LOG_TAG = "AutoRevokePermissions"
const val DEBUG_AUTO_REVOKE = true
-private val EXEMPT_PERMISSIONS = listOf(
- Manifest.permission.ACTIVITY_RECOGNITION,
- Manifest.permission.POST_NOTIFICATIONS)
+val AUTO_REVOKE_EXEMPT_PERMISSIONS =
+ listOf(Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.POST_NOTIFICATIONS)
private val SERVER_LOG_ID =
PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED
@@ -78,10 +78,10 @@ suspend fun revokeAppPermissions(
continue
}
- val pkgPermChanges = PermissionChangeStorageImpl.getInstance().loadEvents()
- .associateBy { it.packageName }
+ val pkgPermChanges =
+ PermissionChangeStorageImpl.getInstance().loadEvents().associateBy { it.packageName }
// For each autorevoke-eligible app...
- userApps.forEachInParallel(Main) forEachInParallelOuter@ { pkg: LightPackageInfo ->
+ userApps.forEachInParallel(Main) forEachInParallelOuter@{ pkg: LightPackageInfo ->
if (pkg.grantedPermissions.isEmpty()) {
return@forEachInParallelOuter
}
@@ -90,15 +90,18 @@ suspend fun revokeAppPermissions(
val now = System.currentTimeMillis()
if (pkgPermChange != null && now - pkgPermChange.eventTime < getUnusedThresholdMs()) {
if (DEBUG_AUTO_REVOKE) {
- DumpableLog.i(LOG_TAG, "Not revoking because permissions were changed " +
- "recently for package $packageName")
+ DumpableLog.i(
+ LOG_TAG,
+ "Not revoking because permissions were changed " +
+ "recently for package $packageName"
+ )
}
return@forEachInParallelOuter
}
val targetSdk = pkg.targetSdkVersion
val pkgPermGroups: Map<String, List<String>> =
- PackagePermissionsLiveData[packageName, user]
- .getInitializedValue() ?: return@forEachInParallelOuter
+ PackagePermissionsLiveData[packageName, user].getInitializedValue()
+ ?: return@forEachInParallelOuter
// Determine which permGroups are revocable
val revocableGroups = mutableSetOf<String>()
@@ -110,26 +113,30 @@ suspend fun revokeAppPermissions(
continue
}
val group: LightAppPermGroup =
- LightAppPermGroupLiveData[packageName, groupName, user]
- .getInitializedValue() ?: continue
+ LightAppPermGroupLiveData[packageName, groupName, user].getInitializedValue()
+ ?: continue
val fixed = group.isBackgroundFixed || group.isForegroundFixed
- val granted = group.permissions.any { (_, perm) ->
- perm.isGranted && perm.name !in EXEMPT_PERMISSIONS
- }
- if (!fixed && granted &&
- !group.isGrantedByDefault &&
- !group.isGrantedByRole &&
- !group.isRevokeWhenRequested &&
- group.isUserSensitive) {
+ val granted =
+ group.permissions.any { (_, perm) ->
+ perm.isGranted && perm.name !in AUTO_REVOKE_EXEMPT_PERMISSIONS
+ }
+ if (
+ !fixed &&
+ granted &&
+ !group.isGrantedByDefault &&
+ !group.isGrantedByRole &&
+ !group.isRevokeWhenRequested &&
+ group.isUserSensitive
+ ) {
revocableGroups.add(groupName)
}
}
// Mark any groups that split from an install-time permission as unrevocable
for (fromPerm in
- pkgPermGroups[PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS] ?: emptyList()) {
+ pkgPermGroups[PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS] ?: emptyList()) {
for (toGroup in
- splitPermissionIndex.getPermToGroupSplitsFrom(fromPerm, targetSdk)) {
+ splitPermissionIndex.getPermToGroupSplitsFrom(fromPerm, targetSdk)) {
revocableGroups.remove(toGroup)
}
}
@@ -138,11 +145,11 @@ suspend fun revokeAppPermissions(
for (groupName in pkgPermGroups.keys) {
if (!revocableGroups.contains(groupName)) {
for (fromGroup in
- splitPermissionIndex.getGroupToGroupSplitsTo(groupName, targetSdk)) {
+ splitPermissionIndex.getGroupToGroupSplitsTo(groupName, targetSdk)) {
revocableGroups.remove(fromGroup)
}
for (toGroup in
- splitPermissionIndex.getGroupToGroupSplitsFrom(groupName, targetSdk)) {
+ splitPermissionIndex.getGroupToGroupSplitsFrom(groupName, targetSdk)) {
revocableGroups.remove(toGroup)
}
}
@@ -152,74 +159,97 @@ suspend fun revokeAppPermissions(
val anyPermsRevoked = AtomicBoolean(false)
pkgPermGroups.entries
.filter { revocableGroups.contains(it.key) }
- .forEachInParallel(Main) forEachInParallelInner@ { (groupName, _) ->
- val group: LightAppPermGroup =
- LightAppPermGroupLiveData[packageName, groupName, user]
- .getInitializedValue()!!
+ .forEachInParallel(Main) forEachInParallelInner@{ (groupName, _) ->
+ val group: LightAppPermGroup =
+ LightAppPermGroupLiveData[packageName, groupName, user]
+ .getInitializedValue()!!
- val revocablePermissions = group.permissions.keys.toList()
+ val revocablePermissions = group.permissions.keys.toList()
- if (revocablePermissions.isEmpty()) {
- return@forEachInParallelInner
- }
-
- if (DEBUG_AUTO_REVOKE) {
- DumpableLog.i(LOG_TAG,
- "revokeUnused $packageName - $revocablePermissions")
- }
+ if (revocablePermissions.isEmpty()) {
+ return@forEachInParallelInner
+ }
- val uid = group.packageInfo.uid
- for (permName in revocablePermissions) {
- PermissionControllerStatsLog.write(
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED,
- sessionId, uid, packageName, permName, false, SERVER_LOG_ID,
- /* permission_rationale_shown = */ false)
- }
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(LOG_TAG, "revokeUnused $packageName - $revocablePermissions")
+ }
- if (DEBUG_AUTO_REVOKE) {
- DumpableLog.i(LOG_TAG, "revoking $packageName - $revocablePermissions")
- DumpableLog.i(LOG_TAG, "State pre revocation: ${group.allPermissions}")
- }
- anyPermsRevoked.compareAndSet(false, true)
+ val uid = group.packageInfo.uid
+ for (permName in revocablePermissions) {
+ PermissionControllerStatsLog.write(
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED,
+ sessionId,
+ uid,
+ packageName,
+ permName,
+ false,
+ SERVER_LOG_ID,
+ /* permission_rationale_shown = */ false
+ )
+ }
- val bgRevokedState = KotlinUtils.revokeBackgroundRuntimePermissions(
- context.application, group,
- userFixed = false, oneTime = false,
- filterPermissions = revocablePermissions)
- if (DEBUG_AUTO_REVOKE) {
- DumpableLog.i(LOG_TAG,
- "Bg state post revocation: ${bgRevokedState.allPermissions}")
- }
- val fgRevokedState = KotlinUtils.revokeForegroundRuntimePermissions(
- context.application, group,
- userFixed = false, oneTime = false,
- filterPermissions = revocablePermissions)
- if (DEBUG_AUTO_REVOKE) {
- DumpableLog.i(LOG_TAG,
- "Fg state post revocation: ${fgRevokedState.allPermissions}")
- }
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(LOG_TAG, "revoking $packageName - $revocablePermissions")
+ DumpableLog.i(LOG_TAG, "State pre revocation: ${group.allPermissions}")
+ }
+ anyPermsRevoked.compareAndSet(false, true)
+
+ val bgRevokedState =
+ KotlinUtils.revokeBackgroundRuntimePermissions(
+ context.application,
+ group,
+ userFixed = false,
+ oneTime = false,
+ filterPermissions = revocablePermissions
+ )
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(
+ LOG_TAG,
+ "Bg state post revocation: ${bgRevokedState.allPermissions}"
+ )
+ }
+ val fgRevokedState =
+ KotlinUtils.revokeForegroundRuntimePermissions(
+ context.application,
+ group,
+ userFixed = false,
+ oneTime = false,
+ filterPermissions = revocablePermissions
+ )
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(
+ LOG_TAG,
+ "Fg state post revocation: ${fgRevokedState.allPermissions}"
+ )
+ }
- for (permission in revocablePermissions) {
- context.packageManager.updatePermissionFlags(
- permission, packageName, user,
- FLAG_PERMISSION_AUTO_REVOKED to true,
- FLAG_PERMISSION_USER_SET to false)
+ for (permission in revocablePermissions) {
+ context.packageManager.updatePermissionFlags(
+ permission,
+ packageName,
+ user,
+ FLAG_PERMISSION_AUTO_REVOKED to true,
+ FLAG_PERMISSION_USER_SET to false
+ )
+ }
}
- }
if (anyPermsRevoked.get()) {
- synchronized(revokedApps) {
- revokedApps.add(packageName to user)
- }
+ synchronized(revokedApps) { revokedApps.add(packageName to user) }
}
}
if (DEBUG_AUTO_REVOKE) {
synchronized(revokedApps) {
- DumpableLog.i(LOG_TAG,
- "Done auto-revoke for user ${user.identifier} - revoked $revokedApps")
+ DumpableLog.i(
+ LOG_TAG,
+ "Done auto-revoke for user ${user.identifier} - revoked $revokedApps"
+ )
}
}
}
+ if (revokedApps.isNotEmpty()) {
+ AutoRevokedPackagesLiveData.update()
+ }
return revokedApps
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java b/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java
index 9082b6931..bea110f70 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java
@@ -136,8 +136,8 @@ public class BackupHelper {
case END_TAG:
numOpenTags--;
break;
- default:
- // ignore
+ case END_DOCUMENT:
+ return;
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/BasePermissionEventStorage.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/BasePermissionEventStorage.kt
index 840b7e483..8bbb41c56 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/BasePermissionEventStorage.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/BasePermissionEventStorage.kt
@@ -22,17 +22,14 @@ import android.util.AtomicFile
import android.util.Log
import com.android.permissioncontroller.DumpableLog
import com.android.permissioncontroller.permission.data.PermissionEvent
-import org.xmlpull.v1.XmlPullParserException
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
+import org.xmlpull.v1.XmlPullParserException
-/**
- * Thread-safe implementation of [PermissionEventStorage] using an XML file as the
- * database.
- */
+/** Thread-safe implementation of [PermissionEventStorage] using an XML file as the database. */
abstract class BasePermissionEventStorage<T : PermissionEvent>(
private val context: Context,
jobScheduler: JobScheduler = context.getSystemService(JobScheduler::class.java)!!
@@ -75,9 +72,7 @@ abstract class BasePermissionEventStorage<T : PermissionEvent>(
}
override suspend fun clearEvents() {
- synchronized(fileLock) {
- dbFile.delete()
- }
+ synchronized(fileLock) { dbFile.delete() }
}
override suspend fun removeOldData(): Boolean {
@@ -85,12 +80,15 @@ abstract class BasePermissionEventStorage<T : PermissionEvent>(
val existingEvents = readData()
val originalCount = existingEvents.size
- val newEvents = existingEvents.filter {
- (System.currentTimeMillis() - it.eventTime) <= getMaxDataAgeMs()
- }
+ val newEvents =
+ existingEvents.filter {
+ (System.currentTimeMillis() - it.eventTime) <= getMaxDataAgeMs()
+ }
- DumpableLog.d(LOG_TAG,
- "${originalCount - newEvents.size} old permission events removed")
+ DumpableLog.d(
+ LOG_TAG,
+ "${originalCount - newEvents.size} old permission events removed"
+ )
return writeData(newEvents)
}
@@ -109,20 +107,19 @@ abstract class BasePermissionEventStorage<T : PermissionEvent>(
synchronized(fileLock) {
val existingEvents = readData()
- val newEvents = existingEvents.map {
- it.copyWithTimeDelta(diffSystemTimeMillis)
- }
+ val newEvents = existingEvents.map { it.copyWithTimeDelta(diffSystemTimeMillis) }
return writeData(newEvents)
}
}
private fun writeData(events: List<T>): Boolean {
- val stream: FileOutputStream = try {
- dbFile.startWrite()
- } catch (e: IOException) {
- Log.e(LOG_TAG, "Failed to save db file", e)
- return false
- }
+ val stream: FileOutputStream =
+ try {
+ dbFile.startWrite()
+ } catch (e: IOException) {
+ Log.e(LOG_TAG, "Failed to save db file", e)
+ return false
+ }
try {
serialize(stream, events)
dbFile.finishWrite(stream)
@@ -167,23 +164,15 @@ abstract class BasePermissionEventStorage<T : PermissionEvent>(
@Throws(XmlPullParserException::class, IOException::class)
abstract fun parse(inputStream: InputStream): List<T>
- /**
- * Returns file name for database.
- */
+ /** Returns file name for database. */
abstract fun getDatabaseFileName(): String
- /**
- * Returns max time that data should be persisted before being removed.
- */
+ /** Returns max time that data should be persisted before being removed. */
abstract fun getMaxDataAgeMs(): Long
- /**
- * Returns true if the two events have the same primary key for the database store.
- */
+ /** Returns true if the two events have the same primary key for the database store. */
abstract fun hasTheSamePrimaryKey(first: T, second: T): Boolean
- /**
- * Copies the event with the time delta applied to the [PermissionEvent.eventTime].
- */
+ /** Copies the event with the time delta applied to the [PermissionEvent.eventTime]. */
abstract fun T.copyWithTimeDelta(timeDelta: Long): T
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/CheckLifecycleRegistry.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/CheckLifecycleRegistry.kt
index 506fa0ef0..678caa168 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/CheckLifecycleRegistry.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/CheckLifecycleRegistry.kt
@@ -29,17 +29,13 @@ class CheckLifecycleRegistry(provider: LifecycleOwner) : LifecycleRegistry(provi
if (Looper.myLooper() != Looper.getMainLooper()) {
throw IllegalStateException("Lifecycle running on non main thread")
}
- synchronized(observerLock) {
- super.addObserver(observer)
- }
+ synchronized(observerLock) { super.addObserver(observer) }
}
override fun removeObserver(observer: LifecycleObserver) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw IllegalStateException("Lifecycle running on non main thread")
}
- synchronized(observerLock) {
- super.removeObserver(observer)
- }
+ synchronized(observerLock) { super.removeObserver(observer) }
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/ExemptRestrictedPermission.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/ExemptRestrictedPermission.kt
index dbf844b27..33e579eef 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/ExemptRestrictedPermission.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/ExemptRestrictedPermission.kt
@@ -8,8 +8,8 @@ import android.os.Process
import android.os.UserHandle
/**
- * For manually exempting a restricted permission.
- * STOPSHIP This functionality should not be in the final release.
+ * For manually exempting a restricted permission. STOPSHIP This functionality should not be in the
+ * final release.
*/
class ExemptRestrictedPermission : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -21,7 +21,10 @@ class ExemptRestrictedPermission : BroadcastReceiver() {
// Use upgrade flag. If the permission needs to be manually exempted then it probably
// should have been done on upgrade.
- userContext.packageManager.addWhitelistedRestrictedPermission(packageName, permission,
- PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE)
+ userContext.packageManager.addWhitelistedRestrictedPermission(
+ packageName,
+ permission,
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
+ )
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java b/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java
index e21be6a05..57c828c20 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java
@@ -41,6 +41,9 @@ import static android.os.UserHandle.getUserHandleForUid;
import static android.os.UserHandle.myUserId;
import static android.provider.Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS;
import static android.provider.Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS;
+import static android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID;
+import static android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID;
+import static android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_USER_HANDLE;
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
@@ -1054,6 +1057,10 @@ public class LocationAccessCheck {
Intent clickIntent = null;
if (isSafetyCenterBgLocationReminderEnabled()) {
clickIntent = new Intent(ACTION_SAFETY_CENTER);
+ clickIntent.putExtra(EXTRA_SAFETY_SOURCE_ID, BG_LOCATION_SOURCE_ID);
+ clickIntent.putExtra(
+ EXTRA_SAFETY_SOURCE_ISSUE_ID, createSafetySourceIssueId(pkg, user));
+ clickIntent.putExtra(EXTRA_SAFETY_SOURCE_USER_HANDLE, user);
} else {
clickIntent = new Intent(ACTION_MANAGE_APP_PERMISSION);
clickIntent.putExtra(EXTRA_PERMISSION_GROUP_NAME, LOCATION);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt
index bdcf833fc..5a49b7ebe 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt
@@ -25,12 +25,6 @@ import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.hibernation.getUnusedThresholdMs
import com.android.permissioncontroller.permission.data.PermissionChange
import com.android.permissioncontroller.permission.utils.Utils
-import kotlinx.coroutines.DelicateCoroutinesApi
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import org.xmlpull.v1.XmlPullParser
-import org.xmlpull.v1.XmlPullParserException
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
@@ -39,6 +33,12 @@ import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserException
/**
* Implementation of [BasePermissionEventStorage] for storing [PermissionChange] events for long
@@ -52,14 +52,10 @@ class PermissionChangeStorageImpl(
// We don't use namespaces
private val ns: String? = null
- /**
- * The format for how dates are stored.
- */
+ /** The format for how dates are stored. */
private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US)
- /**
- * Exact format if [PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME] is true
- */
+ /** Exact format if [PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME] is true */
private val exactTimeFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
companion object {
@@ -67,9 +63,7 @@ class PermissionChangeStorageImpl(
private const val DB_VERSION = 1
- /**
- * Config store file name for general shared store file.
- */
+ /** Config store file name for general shared store file. */
private const val STORE_FILE_NAME = "permission_changes.xml"
private const val TAG_PERMISSION_CHANGES = "permission-changes"
@@ -79,13 +73,10 @@ class PermissionChangeStorageImpl(
private const val ATTR_PACKAGE_NAME = "package-name"
private const val ATTR_EVENT_TIME = "event-time"
- @Volatile
- private var INSTANCE: PermissionEventStorage<PermissionChange>? = null
+ @Volatile private var INSTANCE: PermissionEventStorage<PermissionChange>? = null
fun getInstance(): PermissionEventStorage<PermissionChange> =
- INSTANCE ?: synchronized(this) {
- INSTANCE ?: createInstance().also { INSTANCE = it }
- }
+ INSTANCE ?: synchronized(this) { INSTANCE ?: createInstance().also { INSTANCE = it } }
private fun createInstance(): PermissionEventStorage<PermissionChange> {
return PermissionChangeStorageImpl(PermissionControllerApplication.get())
@@ -142,9 +133,7 @@ class PermissionChangeStorageImpl(
val storesExactTime = storesExactTime()
val truncateToDay = didStoreExactTime != storesExactTime && !storesExactTime
while (parser.next() != XmlPullParser.END_TAG) {
- readPermissionChange(parser, format, truncateToDay)?.let {
- entries.add(it)
- }
+ readPermissionChange(parser, format, truncateToDay)?.let { entries.add(it) }
}
return entries
}
@@ -160,9 +149,11 @@ class PermissionChangeStorageImpl(
try {
val packageName = parser.getAttributeValueNullSafe(ns, ATTR_PACKAGE_NAME)
val changeDate = parser.getAttributeValueNullSafe(ns, ATTR_EVENT_TIME)
- var changeTime = format.parse(changeDate)?.time
- ?: throw IllegalArgumentException(
- "Could not parse date $changeDate on package $packageName")
+ var changeTime =
+ format.parse(changeDate)?.time
+ ?: throw IllegalArgumentException(
+ "Could not parse date $changeDate on package $packageName"
+ )
if (truncateToDay) {
changeTime = dateFormat.parse(dateFormat.format(Date(changeTime)))!!.time
}
@@ -184,7 +175,8 @@ class PermissionChangeStorageImpl(
private fun XmlPullParser.getAttributeValueNullSafe(namespace: String?, name: String): String {
return this.getAttributeValue(namespace, name)
?: throw XmlPullParserException(
- "Could not find attribute: namespace $namespace, name $name")
+ "Could not find attribute: namespace $namespace, name $name"
+ )
}
override fun getDatabaseFileName(): String {
@@ -204,11 +196,12 @@ class PermissionChangeStorageImpl(
return this.copy(eventTime = this.eventTime + timeDelta)
}
- /**
- * Should only be true in tests and never true in prod.
- */
+ /** Should only be true in tests and never true in prod. */
private fun storesExactTime(): Boolean {
- return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PERMISSIONS,
- Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME, /* defaultValue= */ false)
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PERMISSIONS,
+ Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME,
+ /* defaultValue= */ false
+ )
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt
index 49a465898..14a7f71f0 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt
@@ -25,10 +25,9 @@ import androidx.core.util.Consumer
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
-import androidx.lifecycle.Transformations
+import androidx.lifecycle.map
import com.android.permissioncontroller.DumpableLog
import com.android.permissioncontroller.PermissionControllerProto.PermissionControllerDumpProto
-import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData
import com.android.permissioncontroller.permission.data.HibernationSettingStateLiveData
import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
@@ -39,14 +38,15 @@ import com.android.permissioncontroller.permission.data.getUnusedPackages
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
+import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.Utils
+import java.util.function.IntConsumer
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
-import java.util.function.IntConsumer
/**
* A model for the PermissionControllerServiceImpl. Handles the data gathering for some methods of
@@ -57,10 +57,10 @@ class PermissionControllerServiceModel(private val service: PermissionController
private val observedLiveDatas = mutableListOf<LiveData<*>>()
/**
- * *Must* be used instead of LiveData.observe, in order to allow the lifecycle state to
- * be set to "started" correctly. If the liveData was inactive, create a no op observer, which
- * will survive until the service goes inactive. Will remove the provided observer after one
- * update (one non-stale update, in the case of a SmartUpdateMediatorLiveData).
+ * *Must* be used instead of LiveData.observe, in order to allow the lifecycle state to be set
+ * to "started" correctly. If the liveData was inactive, create a no op observer, which will
+ * survive until the service goes inactive. Will remove the provided observer after one update
+ * (one non-stale update, in the case of a SmartUpdateMediatorLiveData).
*
* @param liveData The livedata we wish to observe
* @param onChangedFun The function we wish to be called upon livedata updates
@@ -72,14 +72,13 @@ class PermissionControllerServiceModel(private val service: PermissionController
onChangedFun: (t: T?) -> Unit
) {
GlobalScope.launch(Main.immediate) {
-
if (service.lifecycle.currentState != Lifecycle.State.STARTED) {
service.setLifecycleToStarted()
}
if (!liveData.hasActiveObservers()) {
observedLiveDatas.add(liveData)
- liveData.observe(service, Observer { })
+ liveData.observe(service, Observer {})
}
if (forceUpdate && liveData is SmartUpdateMediatorLiveData<T>) {
@@ -88,13 +87,13 @@ class PermissionControllerServiceModel(private val service: PermissionController
var updated = false
val observer = object : Observer<T> {
- override fun onChanged(data: T) {
+ override fun onChanged(value: T) {
if (updated) {
return
}
if ((liveData is SmartUpdateMediatorLiveData<T> && !liveData.isStale) ||
liveData !is SmartUpdateMediatorLiveData<T>) {
- onChangedFun(data)
+ onChangedFun(value)
liveData.removeObserver(this)
updated = true
}
@@ -105,9 +104,7 @@ class PermissionControllerServiceModel(private val service: PermissionController
}
}
- /**
- * Stop observing all currently observed liveDatas
- */
+ /** Stop observing all currently observed liveDatas */
fun removeObservers() {
GlobalScope.launch(Main.immediate) {
for (liveData in observedLiveDatas) {
@@ -133,17 +130,16 @@ class PermissionControllerServiceModel(private val service: PermissionController
) {
val packageInfosLiveData = UserPackageInfosLiveData[Process.myUserHandle()]
observeAndCheckForLifecycleState(packageInfosLiveData) { packageInfos ->
- onPackagesLoadedForCountPermissionApps(permissionNames, flags, callback,
- packageInfos)
+ onPackagesLoadedForCountPermissionApps(permissionNames, flags, callback, packageInfos)
}
}
/**
- * Called upon receiving a list of packages which we want to filter by a list of permissions
- * and flags. Observes the AppPermGroupUiInfoLiveData for every app, and, upon receiving a
- * non-stale update, adds it to the count if it matches the permission list and flags. Will
- * only use the first non-stale update, so if an app is updated after this update, but before
- * execution is complete, the changes will not be reflected until the method is called again.
+ * Called upon receiving a list of packages which we want to filter by a list of permissions and
+ * flags. Observes the AppPermGroupUiInfoLiveData for every app, and, upon receiving a non-stale
+ * update, adds it to the count if it matches the permission list and flags. Will only use the
+ * first non-stale update, so if an app is updated after this update, but before execution is
+ * complete, the changes will not be reflected until the method is called again.
*
* @param permissionNames The list of permission names whose apps we want to count
* @param flags Flags specifying if we want to count system apps, and count only granted apps
@@ -167,11 +163,12 @@ class PermissionControllerServiceModel(private val service: PermissionController
// Store the group of all installed, runtime permissions in permissionNames
val permToGroup = mutableMapOf<String, String?>()
for (permName in permissionNames) {
- val permInfo = try {
- service.packageManager.getPermissionInfo(permName, 0)
- } catch (e: PackageManager.NameNotFoundException) {
- continue
- }
+ val permInfo =
+ try {
+ service.packageManager.getPermissionInfo(permName, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ continue
+ }
if (Utils.isPermissionDangerousInstalledNotRemoved(permInfo)) {
permToGroup[permName] = PermissionMapping.getGroupOfPermission(permInfo)
@@ -184,8 +181,10 @@ class PermissionControllerServiceModel(private val service: PermissionController
val packageUiLiveDatas = mutableSetOf<AppPermGroupUiInfoLiveData>()
for (permName in permToGroup.keys) {
if (requestedPermissions.contains(permName)) {
- packageUiLiveDatas.add(AppPermGroupUiInfoLiveData[packageName,
- permToGroup[permName]!!, Process.myUserHandle()])
+ packageUiLiveDatas.add(
+ AppPermGroupUiInfoLiveData[
+ packageName, permToGroup[permName]!!, Process.myUserHandle()]
+ )
}
}
if (packageUiLiveDatas.isNotEmpty()) {
@@ -211,8 +210,9 @@ class PermissionControllerServiceModel(private val service: PermissionController
numPermAppsChecked++
if (uiInfo != null && uiInfo.shouldShow && (!uiInfo.isSystem || countSystem)) {
- val granted = uiInfo.permGrantState != PermGrantState.PERMS_DENIED &&
- uiInfo.permGrantState != PermGrantState.PERMS_ASK
+ val granted =
+ uiInfo.permGrantState != PermGrantState.PERMS_DENIED &&
+ uiInfo.permGrantState != PermGrantState.PERMS_ASK
if (granted || !countOnlyGranted && !packageAdded) {
// The permission might not be granted, but some permissions of the
// group are granted. In this case the permission is granted silently
@@ -244,8 +244,7 @@ class PermissionControllerServiceModel(private val service: PermissionController
packageName: String,
callback: Consumer<List<Pair<String, AppPermGroupUiInfo>>>
) {
- val packageGroupsLiveData = PackagePermissionsLiveData[packageName,
- Process.myUserHandle()]
+ val packageGroupsLiveData = PackagePermissionsLiveData[packageName, Process.myUserHandle()]
observeAndCheckForLifecycleState(packageGroupsLiveData) { groups ->
val groupNames = groups?.keys?.toMutableList() ?: mutableListOf()
groupNames.remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS)
@@ -260,8 +259,8 @@ class PermissionControllerServiceModel(private val service: PermissionController
// live datas, because this method is used primarily for UI, and there is inherent
// delay when calling this method, due to binder calls, so some staleness is
// acceptable
- val uiInfoLiveData = AppPermGroupUiInfoLiveData[packageName, groupName,
- Process.myUserHandle()]
+ val uiInfoLiveData =
+ AppPermGroupUiInfoLiveData[packageName, groupName, Process.myUserHandle()]
observeAndCheckForLifecycleState(uiInfoLiveData, forceUpdate = true) { uiInfo ->
numLiveDatasUpdated++
@@ -288,28 +287,24 @@ class PermissionControllerServiceModel(private val service: PermissionController
fun onCountUnusedApps(
callback: IntConsumer
) {
- val unusedAppsCount = Transformations.map(getUnusedPackages()) {
- it?.size ?: 0
- }
+ val unusedAppsCount = getUnusedPackages().map { it?.size ?: 0 }
observeAndCheckForLifecycleState(unusedAppsCount) { count -> callback.accept(count ?: 0) }
}
/**
- * Gets whether the package is eligible for hibernation. The logic is the same logic used by
- * the app hibernation job when determining which apps to hibernate.
+ * Gets whether the package is eligible for hibernation. The logic is the same logic used by the
+ * app hibernation job when determining which apps to hibernate.
*
* @param packageName The package to check eligibility for
* @param callback The callback the result will be returned to
*/
- fun onGetHibernationEligibility(
- packageName: String,
- callback: IntConsumer
- ) {
+ fun onGetHibernationEligibility(packageName: String, callback: IntConsumer) {
val user = Process.myUserHandle()
val hibernationSettingLiveData = HibernationSettingStateLiveData[packageName, user]
observeAndCheckForLifecycleState(hibernationSettingLiveData) { hibernationSettingState ->
callback.accept(
- hibernationSettingState?.hibernationEligibility ?: HIBERNATION_ELIGIBILITY_UNKNOWN)
+ hibernationSettingState?.hibernationEligibility ?: HIBERNATION_ELIGIBILITY_UNKNOWN
+ )
}
}
@@ -323,9 +318,7 @@ class PermissionControllerServiceModel(private val service: PermissionController
return withTimeout(9000) {
val dumpedLogs = GlobalScope.async(IO) { DumpableLog.get() }
- PermissionControllerDumpProto.newBuilder()
- .addAllLogs(dumpedLogs.await())
- .build()
+ PermissionControllerDumpProto.newBuilder().addAllLogs(dumpedLogs.await()).build()
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventCleanupJobService.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventCleanupJobService.kt
index d22b63e9a..45501cd25 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventCleanupJobService.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventCleanupJobService.kt
@@ -26,15 +26,13 @@ import android.provider.DeviceConfig
import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.DumpableLog
import com.android.permissioncontroller.permission.utils.Utils
+import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-import java.util.concurrent.TimeUnit
-/**
- * A job to clean up old permission events.
- */
+/** A job to clean up old permission events. */
class PermissionEventCleanupJobService : JobService() {
companion object {
@@ -43,13 +41,15 @@ class PermissionEventCleanupJobService : JobService() {
fun scheduleOldDataCleanupIfNecessary(context: Context, jobScheduler: JobScheduler) {
if (isNewJobScheduleRequired(jobScheduler)) {
- val jobInfo = JobInfo.Builder(
- Constants.OLD_PERMISSION_EVENT_CLEANUP_JOB_ID,
- ComponentName(context, PermissionEventCleanupJobService::class.java))
- .setPeriodic(getClearOldEventsCheckFrequencyMs())
- // persist this job across boots
- .setPersisted(true)
- .build()
+ val jobInfo =
+ JobInfo.Builder(
+ Constants.OLD_PERMISSION_EVENT_CLEANUP_JOB_ID,
+ ComponentName(context, PermissionEventCleanupJobService::class.java)
+ )
+ .setPeriodic(getClearOldEventsCheckFrequencyMs())
+ // persist this job across boots
+ .setPersisted(true)
+ .build()
val status = jobScheduler.schedule(jobInfo)
if (status != JobScheduler.RESULT_SUCCESS) {
DumpableLog.e(LOG_TAG, "Could not schedule job: $status")
@@ -64,8 +64,8 @@ class PermissionEventCleanupJobService : JobService() {
*/
private fun isNewJobScheduleRequired(jobScheduler: JobScheduler): Boolean {
var scheduleNewJob = false
- val existingJob: JobInfo? = jobScheduler
- .getPendingJob(Constants.OLD_PERMISSION_EVENT_CLEANUP_JOB_ID)
+ val existingJob: JobInfo? =
+ jobScheduler.getPendingJob(Constants.OLD_PERMISSION_EVENT_CLEANUP_JOB_ID)
when {
existingJob == null -> {
DumpableLog.i(LOG_TAG, "No existing job, scheduling a new one")
@@ -83,9 +83,11 @@ class PermissionEventCleanupJobService : JobService() {
}
private fun getClearOldEventsCheckFrequencyMs() =
- DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
+ DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_PERMISSIONS,
Utils.PROPERTY_PERMISSION_EVENTS_CHECK_OLD_FREQUENCY_MILLIS,
- DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY)
+ DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY
+ )
}
var job: Job? = null
@@ -98,15 +100,16 @@ class PermissionEventCleanupJobService : JobService() {
return false
}
jobStartTime = System.currentTimeMillis()
- job = GlobalScope.launch(Dispatchers.IO) {
- for (storage in storages) {
- val success = storage.removeOldData()
- if (!success) {
- DumpableLog.e(LOG_TAG, "Failed to remove old data for $storage")
+ job =
+ GlobalScope.launch(Dispatchers.IO) {
+ for (storage in storages) {
+ val success = storage.removeOldData()
+ if (!success) {
+ DumpableLog.e(LOG_TAG, "Failed to remove old data for $storage")
+ }
}
+ jobFinished(params, false)
}
- jobFinished(params, false)
- }
return true
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorage.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorage.kt
index 67a1cb4a4..fb73bcf75 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorage.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorage.kt
@@ -18,9 +18,7 @@ package com.android.permissioncontroller.permission.service
import com.android.permissioncontroller.permission.data.PermissionEvent
-/**
- * Persistent storage for retrieving persisted permission event data.
- */
+/** Persistent storage for retrieving persisted permission event data. */
interface PermissionEventStorage<T : PermissionEvent> {
/**
* Persist a permission event for retrieval later.
@@ -36,9 +34,7 @@ interface PermissionEventStorage<T : PermissionEvent> {
*/
suspend fun loadEvents(): List<T>
- /**
- * Clear all events.
- */
+ /** Clear all events. */
suspend fun clearEvents()
/**
@@ -60,9 +56,9 @@ interface PermissionEventStorage<T : PermissionEvent> {
* Update event timestamps based on the delta in system time.
*
* @param diffSystemTimeMillis the difference between the current and old system times. Positive
- * values mean that the time has changed in the future and negative means the time was changed
- * into the past.
+ * values mean that the time has changed in the future and negative means the time was changed
+ * into the past.
* @return whether the storage was successful
*/
suspend fun updateEventsBySystemTimeDelta(diffSystemTimeMillis: Long): Boolean
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorageImpls.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorageImpls.kt
index de6a0d9e2..33dc128f6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorageImpls.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorageImpls.kt
@@ -21,18 +21,13 @@ import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.data.PermissionEvent
import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl
-/**
- * Singleton of all supported [PermissionEventStorage] on the device.
- */
+/** Singleton of all supported [PermissionEventStorage] on the device. */
class PermissionEventStorageImpls {
companion object {
- @Volatile
- private var INSTANCE: List<PermissionEventStorage<out PermissionEvent>>? = null
+ @Volatile private var INSTANCE: List<PermissionEventStorage<out PermissionEvent>>? = null
fun getInstance(): List<PermissionEventStorage<out PermissionEvent>> =
- INSTANCE ?: synchronized(this) {
- INSTANCE ?: createInstance().also { INSTANCE = it }
- }
+ INSTANCE ?: synchronized(this) { INSTANCE ?: createInstance().also { INSTANCE = it } }
@SuppressLint("NewApi")
private fun createInstance(): List<PermissionEventStorage<out PermissionEvent>> {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionStorageTimeChangeReceiver.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionStorageTimeChangeReceiver.kt
index 43970dd13..ccb3acbad 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionStorageTimeChangeReceiver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionStorageTimeChangeReceiver.kt
@@ -26,10 +26,10 @@ import com.android.permissioncontroller.DumpableLog
import com.android.permissioncontroller.permission.data.PermissionEvent
import com.android.permissioncontroller.permission.utils.SystemTimeSource
import com.android.permissioncontroller.permission.utils.TimeSource
+import kotlin.math.abs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
-import kotlin.math.abs
/**
* [BroadcastReceiver] to update the persisted timestamps when the date changes. Receives broadcasts
@@ -51,8 +51,7 @@ class PermissionStorageTimeChangeReceiver(
* Key for the last known system time from the system. First initialized after boot
* complete.
*/
- @VisibleForTesting
- const val PREF_KEY_SYSTEM_TIME_SNAPSHOT = "system_time_snapshot"
+ @VisibleForTesting const val PREF_KEY_SYSTEM_TIME_SNAPSHOT = "system_time_snapshot"
/**
* Key for the last known elapsed time since boot. First initialized after boot complete.
@@ -60,12 +59,9 @@ class PermissionStorageTimeChangeReceiver(
@VisibleForTesting
const val PREF_KEY_ELAPSED_REALTIME_SNAPSHOT = "elapsed_realtime_snapshot"
- @VisibleForTesting
- const val SNAPSHOT_UNINITIALIZED = -1L
+ @VisibleForTesting const val SNAPSHOT_UNINITIALIZED = -1L
- /**
- * The millisecond threshold for a time delta to be considered a time change.
- */
+ /** The millisecond threshold for a time delta to be considered a time change. */
private const val TIME_CHANGE_THRESHOLD_MILLIS = 60 * 1000L
}
@@ -75,8 +71,11 @@ class PermissionStorageTimeChangeReceiver(
}
when (val action = intent.action) {
Intent.ACTION_BOOT_COMPLETED -> {
- persistTimeSnapshots(context, timeSource.currentTimeMillis(),
- timeSource.elapsedRealtime())
+ persistTimeSnapshots(
+ context,
+ timeSource.currentTimeMillis(),
+ timeSource.elapsedRealtime()
+ )
}
Intent.ACTION_TIME_CHANGED -> {
checkForTimeChanged(context)
@@ -90,15 +89,16 @@ class PermissionStorageTimeChangeReceiver(
private fun checkForTimeChanged(context: Context) {
val systemTimeSnapshot = getSystemTimeSnapshot(context)
val realtimeSnapshot = getElapsedRealtimeSnapshot(context)
- if (realtimeSnapshot == SNAPSHOT_UNINITIALIZED ||
- systemTimeSnapshot == SNAPSHOT_UNINITIALIZED) {
+ if (
+ realtimeSnapshot == SNAPSHOT_UNINITIALIZED ||
+ systemTimeSnapshot == SNAPSHOT_UNINITIALIZED
+ ) {
DumpableLog.e(LOG_TAG, "Snapshots not initialized")
return
}
val actualSystemTime = timeSource.currentTimeMillis()
val actualRealtime = timeSource.elapsedRealtime()
- val expectedSystemTime = (actualRealtime - realtimeSnapshot) +
- systemTimeSnapshot
+ val expectedSystemTime = (actualRealtime - realtimeSnapshot) + systemTimeSnapshot
val diffSystemTime = actualSystemTime - expectedSystemTime
if (abs(diffSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) {
DumpableLog.d(LOG_TAG, "Time changed by ${diffSystemTime / 1000} seconds")
@@ -131,17 +131,21 @@ class PermissionStorageTimeChangeReceiver(
}
private fun getSystemTimeSnapshot(context: Context): Long {
- return context.sharedPreferences.getLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT,
- SNAPSHOT_UNINITIALIZED)
+ return context.sharedPreferences.getLong(
+ PREF_KEY_SYSTEM_TIME_SNAPSHOT,
+ SNAPSHOT_UNINITIALIZED
+ )
}
private fun getElapsedRealtimeSnapshot(context: Context): Long {
- return context.sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT,
- SNAPSHOT_UNINITIALIZED)
+ return context.sharedPreferences.getLong(
+ PREF_KEY_ELAPSED_REALTIME_SNAPSHOT,
+ SNAPSHOT_UNINITIALIZED
+ )
}
val Context.sharedPreferences: SharedPreferences
get() {
return PreferenceManager.getDefaultSharedPreferences(this)
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PersistedStoragePackageUninstalledReceiver.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PersistedStoragePackageUninstalledReceiver.kt
index 37fa2b36d..383e52dfc 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/PersistedStoragePackageUninstalledReceiver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PersistedStoragePackageUninstalledReceiver.kt
@@ -29,8 +29,8 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
/**
- * [BroadcastReceiver] to clear user decision information when a package has its data cleared or
- * is fully removed.
+ * [BroadcastReceiver] to clear user decision information when a package has its data cleared or is
+ * fully removed.
*/
class PersistedStoragePackageUninstalledReceiver(
@VisibleForTesting
@@ -48,8 +48,10 @@ class PersistedStoragePackageUninstalledReceiver(
return
}
val action = intent.action
- if (!(action == Intent.ACTION_PACKAGE_DATA_CLEARED ||
- action == Intent.ACTION_PACKAGE_FULLY_REMOVED)) {
+ if (
+ !(action == Intent.ACTION_PACKAGE_DATA_CLEARED ||
+ action == Intent.ACTION_PACKAGE_FULLY_REMOVED)
+ ) {
return
}
intent.data?.let {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
index 19b2b4803..630e86f65 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
@@ -42,24 +42,26 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightPerm
import com.android.permissioncontroller.permission.utils.IPC
import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions
import com.android.permissioncontroller.permission.utils.KotlinUtils.grantForegroundRuntimePermissions
+import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.PermissionMapping.getPlatformPermissionNamesOfGroup
import com.android.permissioncontroller.permission.utils.PermissionMapping.getRuntimePlatformPermissionNames
import com.android.permissioncontroller.permission.utils.application
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
-/**
- * This class handles upgrading the runtime permissions database
- */
+/** This class handles upgrading the runtime permissions database */
internal object RuntimePermissionsUpgradeController {
private val LOG_TAG = RuntimePermissionsUpgradeController::class.java.simpleName
// The latest version of the runtime permissions database
- private val LATEST_VERSION = if (SdkLevel.isAtLeastT()) {
- 10
- } else {
- 9
- }
+ private val LATEST_VERSION =
+ if (SdkLevel.isAtLeastU()) {
+ 11
+ } else if (SdkLevel.isAtLeastT()) {
+ 10
+ } else {
+ 9
+ }
fun upgradeIfNeeded(context: Context, onComplete: Runnable) {
val permissionManager = context.getSystemService(PermissionManager::class.java)
@@ -69,9 +71,13 @@ internal object RuntimePermissionsUpgradeController {
GlobalScope.launch(IPC) {
val upgradedVersion = onUpgradeLocked(context, currentVersion)
if (upgradedVersion != LATEST_VERSION) {
- Log.wtf("PermissionControllerService", "warning: upgrading permission database" +
- " to version $LATEST_VERSION left it at $currentVersion instead; this is " +
- "probably a bug. Did you update LATEST_VERSION?", Throwable())
+ Log.wtf(
+ "PermissionControllerService",
+ "warning: upgrading permission database" +
+ " to version $LATEST_VERSION left it at $currentVersion instead; this is " +
+ "probably a bug. Did you update LATEST_VERSION?",
+ Throwable()
+ )
throw RuntimeException("db upgrade error")
}
@@ -87,7 +93,6 @@ internal object RuntimePermissionsUpgradeController {
*
* @param permissionInfos permissions to exempt
* @param pkgs packages to exempt
- *
* @return the exemptions to apply
*/
private fun getExemptions(
@@ -107,9 +112,8 @@ internal object RuntimePermissionsUpgradeController {
}
/**
- * You must perform all necessary mutations to bring the runtime permissions
- * database from the old to the new version. When you add a new upgrade step
- * you *must* update LATEST_VERSION.
+ * You must perform all necessary mutations to bring the runtime permissions database from the
+ * old to the new version. When you add a new upgrade step you *must* update LATEST_VERSION.
*
* <p> NOTE: Relies upon the fact that the system will attempt to upgrade every version after
* currentVersion in order, without skipping any versions. Should this become the case, this
@@ -118,10 +122,7 @@ internal object RuntimePermissionsUpgradeController {
* @param context The current context
* @param currentVersion The current version of the permission database
*/
- private suspend fun onUpgradeLocked(
- context: Context,
- currentVersion: Int
- ): Int {
+ private suspend fun onUpgradeLocked(context: Context, currentVersion: Int): Int {
var sdkUpgradedFromP = false
var isNewUser = false
@@ -134,190 +135,238 @@ internal object RuntimePermissionsUpgradeController {
val needBackgroundAppPermGroups = sdkUpgradedFromP && currentVersion <= 6
val needAccessMediaAppPermGroups = !isNewUser && currentVersion <= 7
val needGrantedExternalStorage = currentVersion <= 9 && SdkLevel.isAtLeastT()
+ val needGrantedReadMediaVisual = currentVersion <= 10 && SdkLevel.isAtLeastU()
val isDeviceUpgrading = context.packageManager.isDeviceUpgrading
// All data needed by this method.
//
// All data is loaded once and then not updated.
- val upgradeDataProvider = object : SmartUpdateMediatorLiveData<UpgradeData>() {
- /** Provides all preinstalled packages in the system */
- private val preinstalledPkgInfoProvider =
+ val upgradeDataProvider =
+ object : SmartUpdateMediatorLiveData<UpgradeData>() {
+ /** Provides all preinstalled packages in the system */
+ private val preinstalledPkgInfoProvider =
PreinstalledUserPackageInfosLiveData[myUserHandle()]
- /** Provides all platform runtime permission infos */
- private val platformRuntimePermissionInfoProviders =
+ /** Provides all platform runtime permission infos */
+ private val platformRuntimePermissionInfoProviders =
mutableListOf<LightPermInfoLiveData>()
- /** {@link #platformRuntimePermissionInfoProvider} that already provided a result */
- private val platformRuntimePermissionInfoProvidersDone =
+ /** {@link #platformRuntimePermissionInfoProvider} that already provided a result */
+ private val platformRuntimePermissionInfoProvidersDone =
mutableSetOf<LightPermInfoLiveData>()
- /** Provides all packages in the system */
- private val pkgInfoProvider = UserPackageInfosLiveData[myUserHandle()]
+ /** Provides all packages in the system */
+ private val pkgInfoProvider = UserPackageInfosLiveData[myUserHandle()]
- /** Provides all {@link LightAppPermGroup} this upgrade needs */
- private var permGroupProviders: MutableList<LightAppPermGroupLiveData>? = null
+ /** Provides all {@link LightAppPermGroup} this upgrade needs */
+ private var permGroupProviders: MutableSet<LightAppPermGroupLiveData>? = null
- /** {@link #permGroupProviders} that already provided a result */
- private val permGroupProvidersDone = mutableSetOf<LightAppPermGroupLiveData>()
+ /** {@link #permGroupProviders} that already provided a result */
+ private val permGroupProvidersDone = mutableSetOf<LightAppPermGroupLiveData>()
- init {
- // First step: Load packages + perm infos
- // TODO ntmyren: remove once b/154796729 is fixed
- Log.i("RuntimePermissions", "observing UserPackageInfoLiveData for " +
- "${myUserHandle().identifier} in RuntimePermissionsUpgradeController")
- addSource(pkgInfoProvider) { pkgInfos ->
- if (pkgInfos != null) {
- removeSource(pkgInfoProvider)
+ init {
+ // First step: Load packages + perm infos
+ addSource(pkgInfoProvider) { pkgInfos ->
+ if (pkgInfos != null) {
+ removeSource(pkgInfoProvider)
- // TODO ntmyren: remove once b/154796729 is fixed
- Log.i("RuntimePermissions", "observing " +
- "PreinstalledUserPackageInfoLiveData for ${myUserHandle().identifier}" +
- " in RuntimePermissionsUpgradeController")
- addSource(preinstalledPkgInfoProvider) { preinstalledPkgInfos ->
- if (preinstalledPkgInfos != null) {
- removeSource(preinstalledPkgInfoProvider)
+ addSource(preinstalledPkgInfoProvider) { preinstalledPkgInfos ->
+ if (preinstalledPkgInfos != null) {
+ removeSource(preinstalledPkgInfoProvider)
- update()
+ update()
+ }
}
}
}
- }
- for (platformRuntimePermission in getRuntimePlatformPermissionNames()) {
- val permProvider = LightPermInfoLiveData[platformRuntimePermission]
- platformRuntimePermissionInfoProviders.add(permProvider)
+ for (platformRuntimePermission in getRuntimePlatformPermissionNames()) {
+ val permProvider = LightPermInfoLiveData[platformRuntimePermission]
+ platformRuntimePermissionInfoProviders.add(permProvider)
- addSource(permProvider) { permInfo ->
- if (permInfo != null) {
- platformRuntimePermissionInfoProvidersDone.add(permProvider)
- removeSource(permProvider)
+ addSource(permProvider) { permInfo ->
+ if (permInfo != null) {
+ platformRuntimePermissionInfoProvidersDone.add(permProvider)
+ removeSource(permProvider)
- update()
+ update()
+ }
}
}
}
- }
- override fun onUpdate() {
- if (permGroupProviders == null && pkgInfoProvider.value != null) {
- // Second step: Trigger load of app-perm-groups
+ override fun onUpdate() {
+ if (permGroupProviders == null && pkgInfoProvider.value != null) {
+ // Second step: Trigger load of app-perm-groups
- permGroupProviders = mutableListOf()
+ permGroupProviders = mutableSetOf()
- // Only load app-perm-groups needed for this upgrade
- if (needBackgroundAppPermGroups || needAccessMediaAppPermGroups ||
- needGrantedExternalStorage) {
- for ((pkgName, _, requestedPerms, requestedPermFlags) in
+ // Only load app-perm-groups needed for this upgrade
+ if (
+ needBackgroundAppPermGroups ||
+ needAccessMediaAppPermGroups ||
+ needGrantedExternalStorage ||
+ needGrantedReadMediaVisual
+ ) {
+ for ((pkgName, _, requestedPerms, requestedPermFlags) in
pkgInfoProvider.value!!) {
- var requestsAccessMediaLocation = false
- var hasGrantedExternalStorage = false
-
- for ((perm, flags) in requestedPerms.zip(requestedPermFlags)) {
- if (needBackgroundAppPermGroups &&
- perm == permission.ACCESS_BACKGROUND_LOCATION) {
- permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
- permission_group.LOCATION, myUserHandle()])
- }
-
- if (needAccessMediaAppPermGroups || needGrantedExternalStorage) {
- if (needAccessMediaAppPermGroups &&
- perm == permission.ACCESS_MEDIA_LOCATION) {
- requestsAccessMediaLocation = true
+ var requestsAccessMediaLocation = false
+ var hasGrantedExternalStorage = false
+ var hasGrantedReadMediaVisual = false
+
+ for ((perm, flags) in requestedPerms.zip(requestedPermFlags)) {
+ if (
+ needBackgroundAppPermGroups &&
+ perm == permission.ACCESS_BACKGROUND_LOCATION
+ ) {
+ permGroupProviders!!.add(
+ LightAppPermGroupLiveData[
+ pkgName, permission_group.LOCATION, myUserHandle()]
+ )
}
- if (perm == permission.READ_EXTERNAL_STORAGE &&
- flags and PackageInfo.REQUESTED_PERMISSION_GRANTED
- != 0) {
- hasGrantedExternalStorage = true
+ if (
+ needAccessMediaAppPermGroups ||
+ needGrantedExternalStorage ||
+ needGrantedReadMediaVisual
+ ) {
+ if (
+ needAccessMediaAppPermGroups &&
+ perm == permission.ACCESS_MEDIA_LOCATION
+ ) {
+ requestsAccessMediaLocation = true
+ }
+
+ val isGranted =
+ flags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0
+ if (perm == permission.READ_EXTERNAL_STORAGE && isGranted) {
+ hasGrantedExternalStorage = true
+ }
+ if (
+ PermissionMapping.getGroupOfPlatformPermission(perm) ==
+ permission_group.READ_MEDIA_VISUAL && isGranted
+ ) {
+ hasGrantedReadMediaVisual = true
+ }
}
}
- }
- val accessMediaLocationPermGroup =
- if (SdkLevel.isAtLeastT())
- permission_group.READ_MEDIA_VISUAL
- else
- permission_group.STORAGE
-
- if (hasGrantedExternalStorage) {
- if (needGrantedExternalStorage) {
- permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
- permission_group.STORAGE, myUserHandle()])
- if (SdkLevel.isAtLeastT()) {
- permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
- permission_group.READ_MEDIA_VISUAL, myUserHandle()])
- permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
- permission_group.READ_MEDIA_AURAL, myUserHandle()])
+ val accessMediaLocationPermGroup =
+ if (SdkLevel.isAtLeastT()) permission_group.READ_MEDIA_VISUAL
+ else permission_group.STORAGE
+
+ if (hasGrantedExternalStorage) {
+ if (needGrantedExternalStorage) {
+ permGroupProviders!!.add(
+ LightAppPermGroupLiveData[
+ pkgName, permission_group.STORAGE, myUserHandle()]
+ )
+ if (SdkLevel.isAtLeastT()) {
+ permGroupProviders!!.add(
+ LightAppPermGroupLiveData[
+ pkgName,
+ permission_group.READ_MEDIA_VISUAL,
+ myUserHandle()]
+ )
+ permGroupProviders!!.add(
+ LightAppPermGroupLiveData[
+ pkgName,
+ permission_group.READ_MEDIA_AURAL,
+ myUserHandle()]
+ )
+ }
+ } else if (requestsAccessMediaLocation) {
+ permGroupProviders!!.add(
+ LightAppPermGroupLiveData[
+ pkgName,
+ accessMediaLocationPermGroup,
+ myUserHandle()]
+ )
}
- } else if (requestsAccessMediaLocation) {
- permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
- accessMediaLocationPermGroup, myUserHandle()])
+ }
+ if (hasGrantedReadMediaVisual && needGrantedReadMediaVisual) {
+ permGroupProviders!!.add(
+ LightAppPermGroupLiveData[
+ pkgName,
+ permission_group.READ_MEDIA_VISUAL,
+ myUserHandle()]
+ )
}
}
}
- }
- // Wait until groups are loaded and then trigger third step
- for (permGroupProvider in permGroupProviders!!) {
- addSource(permGroupProvider) { group ->
- if (group != null) {
- permGroupProvidersDone.add(permGroupProvider)
- removeSource(permGroupProvider)
+ // Wait until groups are loaded and then trigger third step
+ for (permGroupProvider in permGroupProviders!!) {
+ addSource(permGroupProvider) { group ->
+ if (group != null) {
+ permGroupProvidersDone.add(permGroupProvider)
+ removeSource(permGroupProvider)
- update()
+ update()
+ }
}
}
- }
- // If no group need to be loaded, directly switch to third step
- if (permGroupProviders!!.isEmpty()) {
- update()
- }
- } else if (permGroupProviders != null &&
- permGroupProvidersDone.size == permGroupProviders!!.size &&
- preinstalledPkgInfoProvider.value != null &&
- platformRuntimePermissionInfoProviders.size
- == platformRuntimePermissionInfoProvidersDone.size) {
- // Third step: All packages, perm infos and perm groups are loaded, set value
-
- val bgGroups = mutableListOf<LightAppPermGroup>()
- val storageGroups = mutableListOf<LightAppPermGroup>()
-
- for (group in permGroupProviders!!.mapNotNull { it.value }) {
- when (group.permGroupName) {
- permission_group.LOCATION -> {
- bgGroups.add(group)
- }
- permission_group.STORAGE -> {
- storageGroups.add(group)
- }
- permission_group.READ_MEDIA_AURAL -> {
- storageGroups.add(group)
- }
- permission_group.READ_MEDIA_VISUAL -> {
- storageGroups.add(group)
+ // If no group need to be loaded, directly switch to third step
+ if (permGroupProviders!!.isEmpty()) {
+ update()
+ }
+ } else if (
+ permGroupProviders != null &&
+ permGroupProvidersDone.size == permGroupProviders!!.size &&
+ preinstalledPkgInfoProvider.value != null &&
+ platformRuntimePermissionInfoProviders.size ==
+ platformRuntimePermissionInfoProvidersDone.size
+ ) {
+ // Third step: All packages, perm infos and perm groups are loaded, set
+ // value
+
+ val bgGroups = mutableListOf<LightAppPermGroup>()
+ val storageGroups = mutableListOf<LightAppPermGroup>()
+
+ for (group in permGroupProviders!!.mapNotNull { it.value }) {
+ when (group.permGroupName) {
+ permission_group.LOCATION -> {
+ bgGroups.add(group)
+ }
+ permission_group.STORAGE -> {
+ storageGroups.add(group)
+ }
+ permission_group.READ_MEDIA_AURAL -> {
+ storageGroups.add(group)
+ }
+ permission_group.READ_MEDIA_VISUAL -> {
+ storageGroups.add(group)
+ }
}
}
- }
- val restrictedPermissions = mutableSetOf<String>()
- for (permInfoLiveDt in platformRuntimePermissionInfoProviders) {
- val permInfo = permInfoLiveDt.value!!
+ val restrictedPermissions = mutableSetOf<String>()
+ for (permInfoLiveDt in platformRuntimePermissionInfoProviders) {
+ val permInfo = permInfoLiveDt.value!!
+
+ if (
+ permInfo.flags and
+ (PermissionInfo.FLAG_HARD_RESTRICTED or
+ PermissionInfo.FLAG_SOFT_RESTRICTED) == 0
+ ) {
+ continue
+ }
- if (permInfo.flags and (PermissionInfo.FLAG_HARD_RESTRICTED or
- PermissionInfo.FLAG_SOFT_RESTRICTED) == 0) {
- continue
+ restrictedPermissions.add(permInfo.name)
}
- restrictedPermissions.add(permInfo.name)
+ value =
+ UpgradeData(
+ preinstalledPkgInfoProvider.value!!,
+ restrictedPermissions,
+ pkgInfoProvider.value!!,
+ bgGroups,
+ storageGroups
+ )
}
-
- value = UpgradeData(preinstalledPkgInfoProvider.value!!, restrictedPermissions,
- pkgInfoProvider.value!!, bgGroups, storageGroups)
}
}
- }
// Trigger loading of data and wait until data is loaded
val upgradeData = upgradeDataProvider.getInitializedValue(forceUpdate = true)
@@ -327,14 +376,18 @@ internal object RuntimePermissionsUpgradeController {
// always exempting them. For non-OTA updates the installer should do the exemption.
// If a restricted permission can't be exempted by the installer then it should be filtered
// out here.
- val preinstalledAppExemptions = getExemptions(
- upgradeData.restrictedPermissions,
- upgradeData.preinstalledPkgs)
+ val preinstalledAppExemptions =
+ getExemptions(upgradeData.restrictedPermissions, upgradeData.preinstalledPkgs)
- val (newVersion, upgradeExemptions, grants) = onUpgradeLockedDataLoaded(currentVersion,
- upgradeData.pkgs, upgradeData.restrictedPermissions,
- upgradeData.bgGroups, upgradeData.storageGroups,
- isDeviceUpgrading)
+ val (newVersion, upgradeExemptions, grants) =
+ onUpgradeLockedDataLoaded(
+ currentVersion,
+ upgradeData.pkgs,
+ upgradeData.restrictedPermissions,
+ upgradeData.bgGroups,
+ upgradeData.storageGroups,
+ isDeviceUpgrading
+ )
// Do not run in parallel. Measurements have shown that this is slower than sequential
for (exemption in (preinstalledAppExemptions union upgradeExemptions)) {
@@ -380,9 +433,10 @@ internal object RuntimePermissionsUpgradeController {
if (currentVersion == 0) {
Log.i(LOG_TAG, "Grandfathering SMS and CallLog permissions")
- val permissions = restrictedPermissions intersect
+ val permissions =
+ restrictedPermissions intersect
(getPlatformPermissionNamesOfGroup(permission_group.SMS) +
- getPlatformPermissionNamesOfGroup(permission_group.CALL_LOG))
+ getPlatformPermissionNamesOfGroup(permission_group.CALL_LOG))
exemptions.addAll(getExemptions(permissions, pkgs))
@@ -402,8 +456,7 @@ internal object RuntimePermissionsUpgradeController {
if (currentVersion == 3) {
Log.i(LOG_TAG, "Grandfathering location background permissions")
- val bgLocExemptions = getExemptions(setOf(permission.ACCESS_BACKGROUND_LOCATION),
- pkgs)
+ val bgLocExemptions = getExemptions(setOf(permission.ACCESS_BACKGROUND_LOCATION), pkgs)
// Adjust bgApps as if the exemption was applied
for ((pkgName, _) in bgLocExemptions) {
@@ -412,13 +465,22 @@ internal object RuntimePermissionsUpgradeController {
val allPermissionsWithxemption = bgApp.allPermissions.toMutableMap()
allPermissionsWithxemption[permission.ACCESS_BACKGROUND_LOCATION] =
- LightPermission(perm.pkgInfo, perm.permInfo, perm.isGranted,
+ LightPermission(
+ perm.pkgInfo,
+ perm.permInfo,
+ perm.isGranted,
perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
- perm.foregroundPerms)
-
- bgAppsWithExemption[pkgName] = LightAppPermGroup(bgApp.packageInfo,
- bgApp.permGroupInfo, allPermissionsWithxemption,
- bgApp.hasInstallToRuntimeSplit, bgApp.specialLocationGrant)
+ perm.foregroundPerms
+ )
+
+ bgAppsWithExemption[pkgName] =
+ LightAppPermGroup(
+ bgApp.packageInfo,
+ bgApp.permGroupInfo,
+ allPermissionsWithxemption,
+ bgApp.hasInstallToRuntimeSplit,
+ bgApp.specialLocationGrant
+ )
}
exemptions.addAll(bgLocExemptions)
@@ -434,7 +496,8 @@ internal object RuntimePermissionsUpgradeController {
if (currentVersion == 5) {
Log.i(LOG_TAG, "Grandfathering Storage permissions")
- val permissions = restrictedPermissions intersect
+ val permissions =
+ restrictedPermissions intersect
getPlatformPermissionNamesOfGroup(permission_group.STORAGE)
// We don't want to allow modification of storage post install, so put it
@@ -448,18 +511,23 @@ internal object RuntimePermissionsUpgradeController {
if (sdkUpgradedFromP) {
Log.i(LOG_TAG, "Expanding location permissions")
for (appPermGroup in bgAppsWithExemption.values) {
- if (appPermGroup.foreground.isGranted &&
- appPermGroup.hasBackgroundGroup &&
- !appPermGroup.background.isUserSet &&
- !appPermGroup.background.isSystemFixed &&
- !appPermGroup.background.isPolicyFixed &&
- !appPermGroup.background.isUserFixed) {
+ if (
+ appPermGroup.foreground.isGranted &&
+ appPermGroup.hasBackgroundGroup &&
+ !appPermGroup.background.isUserSet &&
+ !appPermGroup.background.isSystemFixed &&
+ !appPermGroup.background.isPolicyFixed &&
+ !appPermGroup.background.isUserFixed
+ ) {
grants.add(Grant(true, appPermGroup))
}
}
} else {
- Log.i(LOG_TAG, "Not expanding location permissions as this is not an upgrade " +
- "from Android P")
+ Log.i(
+ LOG_TAG,
+ "Not expanding location permissions as this is not an upgrade " +
+ "from Android P"
+ )
}
currentVersion = 7
@@ -470,18 +538,25 @@ internal object RuntimePermissionsUpgradeController {
Log.i(LOG_TAG, "Expanding read storage to access media location")
for (appPermGroup in storageAndMediaAppPermGroups) {
- val perm = appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION]
- ?: continue
-
- if (!perm.isUserSet && !perm.isSystemFixed && !perm.isPolicyFixed &&
- !perm.isGranted) {
- grants.add(Grant(false, appPermGroup,
- listOf(permission.ACCESS_MEDIA_LOCATION)))
+ val perm =
+ appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION] ?: continue
+
+ if (
+ !perm.isUserSet &&
+ !perm.isSystemFixed &&
+ !perm.isPolicyFixed &&
+ !perm.isGranted
+ ) {
+ grants.add(
+ Grant(false, appPermGroup, listOf(permission.ACCESS_MEDIA_LOCATION))
+ )
}
}
} else {
- Log.i(LOG_TAG, "Not expanding read storage to access media location as this is " +
- "a new user")
+ Log.i(
+ LOG_TAG,
+ "Not expanding read storage to access media location as this is " + "a new user"
+ )
}
currentVersion = 8
@@ -495,35 +570,47 @@ internal object RuntimePermissionsUpgradeController {
if (currentVersion == 9 && SdkLevel.isAtLeastT()) {
if (isNewUser) {
- Log.i(LOG_TAG, "Not migrating STORAGE permissions to READ_MEDIA permissions as" +
- " this is a new user")
+ Log.i(
+ LOG_TAG,
+ "Not migrating STORAGE permissions to READ_MEDIA permissions as" +
+ " this is a new user"
+ )
} else if (!isDeviceUpgrading) {
- Log.i(LOG_TAG, "Not migrating STORAGE permissions to READ_MEDIA permissions as" +
- " this device is not performing an upgrade")
+ Log.i(
+ LOG_TAG,
+ "Not migrating STORAGE permissions to READ_MEDIA permissions as" +
+ " this device is not performing an upgrade"
+ )
} else {
Log.i(LOG_TAG, "Migrating STORAGE permissions to READ_MEDIA permissions")
// 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 = storageAndMediaAppPermGroups.filter {
- it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
- it.permGroupInfo.name == permission_group.STORAGE &&
- it.isGranted && it.isUserSet
- }
+ val storageAppPermGroups =
+ storageAndMediaAppPermGroups.filter {
+ it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
+ it.permGroupInfo.name == permission_group.STORAGE &&
+ it.isGranted &&
+ it.isUserSet
+ }
for (storageAppPermGroup in storageAppPermGroups) {
val pkgName = storageAppPermGroup.packageInfo.packageName
- val auralAppPermGroup = storageAndMediaAppPermGroups.firstOrNull {
- it.packageInfo.packageName == pkgName &&
- it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL &&
- !it.isUserSet && !it.isUserFixed
- }
- val visualAppPermGroup = storageAndMediaAppPermGroups.firstOrNull {
- it.packageInfo.packageName == pkgName &&
- it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
- !it.permissions.filter { it.key != permission.ACCESS_MEDIA_LOCATION }
- .any { it.value.isUserSet || it.value.isUserFixed }
- }
+ val auralAppPermGroup =
+ storageAndMediaAppPermGroups.firstOrNull {
+ it.packageInfo.packageName == pkgName &&
+ it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL &&
+ !it.isUserSet &&
+ !it.isUserFixed
+ }
+ val visualAppPermGroup =
+ storageAndMediaAppPermGroups.firstOrNull {
+ it.packageInfo.packageName == pkgName &&
+ it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
+ !it.permissions
+ .filter { it.key != permission.ACCESS_MEDIA_LOCATION }
+ .any { it.value.isUserSet || it.value.isUserFixed }
+ }
if (auralAppPermGroup != null) {
grants.add(Grant(false, auralAppPermGroup))
@@ -536,14 +623,33 @@ internal object RuntimePermissionsUpgradeController {
currentVersion = 10
}
+ if (currentVersion == 10 && SdkLevel.isAtLeastU()) {
+ // On U, if the app is granted READ_MEDIA_VISUAL, expand the grant to
+ // READ_MEDIA_VISUAL_USER_SELECTED
+ if (isDeviceUpgrading && !isNewUser) {
+ Log.i(
+ LOG_TAG,
+ "Grandfathering READ_MEDIA_VISUAL_USER_SELECTED to apps already " +
+ "granted visual permissions"
+ )
+ val visualAppPermGroups =
+ storageAndMediaAppPermGroups.filter {
+ it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
+ it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
+ it.isGranted &&
+ it.isUserSet
+ }
+ visualAppPermGroups.forEach { grants.add(Grant(false, it)) }
+ }
+ currentVersion = 11
+ }
+
// XXX: Add new upgrade steps above this point.
return Triple(currentVersion, exemptions, grants)
}
- /**
- * All data needed by {@link #onUpgradeLocked}
- */
+ /** All data needed by {@link #onUpgradeLocked} */
private data class UpgradeData(
/** Preinstalled packages */
val preinstalledPkgs: List<LightPackageInfo>,
@@ -556,15 +662,11 @@ internal object RuntimePermissionsUpgradeController {
* {@link #onUpgradeLockedDataLoaded}
*/
val bgGroups: List<LightAppPermGroup>,
- /**
- * Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded}
- */
+ /** Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded} */
val storageGroups: List<LightAppPermGroup>,
)
- /**
- * A restricted permission of an app that should be exempted
- */
+ /** A restricted permission of an app that should be exempted */
private data class RestrictionExemption(
/** Name of package to exempt */
val pkgName: String,
@@ -583,15 +685,13 @@ internal object RuntimePermissionsUpgradeController {
}
}
- /**
- * A permission group of an app that should get granted
- */
+ /** A permission group of an app that should get granted */
private data class Grant(
/** Should the grant be for the foreground or background permissions */
private val isBackground: Boolean,
/** Group to be granted */
private val group: LightAppPermGroup,
- /** Which of th permissions in the group should be granted */
+ /** Which of the permissions in the group should be granted */
private val permissions: List<String> = group.permissions.keys.toList()
) {
/**
@@ -601,17 +701,21 @@ internal object RuntimePermissionsUpgradeController {
*/
fun applyToPlatform(context: Context) {
if (isBackground) {
- val newGroup = grantBackgroundRuntimePermissions(context.application, group,
- permissions)
+ val newGroup =
+ grantBackgroundRuntimePermissions(context.application, group, permissions)
- logRuntimePermissionUpgradeResult(newGroup,
- permissions intersect newGroup.backgroundPermNames)
+ logRuntimePermissionUpgradeResult(
+ newGroup,
+ permissions intersect newGroup.backgroundPermNames
+ )
} else {
- val newGroup = grantForegroundRuntimePermissions(context.application, group,
- permissions)
+ val newGroup =
+ grantForegroundRuntimePermissions(context.application, group, permissions)
- logRuntimePermissionUpgradeResult(newGroup,
- permissions intersect newGroup.foregroundPermNames)
+ logRuntimePermissionUpgradeResult(
+ newGroup,
+ permissions intersect newGroup.foregroundPermNames
+ )
}
}
@@ -629,10 +733,21 @@ internal object RuntimePermissionsUpgradeController {
val packageName = permissionGroup.packageName
for (permName in filterPermissions) {
val permission = permissionGroup.permissions[permName] ?: continue
- PermissionControllerStatsLog.write(RUNTIME_PERMISSIONS_UPGRADE_RESULT,
- permission.name, uid, packageName)
- Log.v(LOG_TAG, "Runtime permission upgrade logged for permissionName=" +
- permission.name + " uid=" + uid + " packageName=" + packageName)
+ PermissionControllerStatsLog.write(
+ RUNTIME_PERMISSIONS_UPGRADE_RESULT,
+ permission.name,
+ uid,
+ packageName
+ )
+ Log.v(
+ LOG_TAG,
+ "Runtime permission upgrade logged for permissionName=" +
+ permission.name +
+ " uid=" +
+ uid +
+ " packageName=" +
+ packageName
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt
index 115200b2f..eb3f2e9af 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt
@@ -40,12 +40,14 @@ class SplitPermissionIndex() {
val oldPermGroup = PermissionMapping.getGroupOfPlatformPermission(oldPerm)
val newPermGroup = PermissionMapping.getGroupOfPlatformPermission(newPerm)
if (newPermGroup != null) {
- permToGroupSplits.add(SplitPermissionIndexEntry(
- oldPerm, splitPerm.targetSdk, newPermGroup))
+ permToGroupSplits.add(
+ SplitPermissionIndexEntry(oldPerm, splitPerm.targetSdk, newPermGroup)
+ )
}
if (oldPermGroup != null && newPermGroup != null) {
- groupToGroupSplits.add(SplitPermissionIndexEntry(
- oldPermGroup, splitPerm.targetSdk, newPermGroup))
+ groupToGroupSplits.add(
+ SplitPermissionIndexEntry(oldPermGroup, splitPerm.targetSdk, newPermGroup)
+ )
}
}
}
@@ -53,9 +55,7 @@ class SplitPermissionIndex() {
this.groupToGroupSplits = groupToGroupSplits
}
- /**
- * Given a permission, return which groups split *from* it for the given targetSdk.
- */
+ /** Given a permission, return which groups split *from* it for the given targetSdk. */
fun getPermToGroupSplitsFrom(oldPermission: String, targetSdk: Int): List<String> {
return permToGroupSplits
.filter { it.oldPerm == oldPermission && it.targetSdk < targetSdk }
@@ -63,9 +63,7 @@ class SplitPermissionIndex() {
.toList()
}
- /**
- * Given a permission group, return which groups split *from* it for the given targetSdk.
- */
+ /** Given a permission group, return which groups split *from* it for the given targetSdk. */
fun getGroupToGroupSplitsFrom(oldPermissionGroup: String, targetSdk: Int): List<String> {
return groupToGroupSplits
.filter { it.oldPerm == oldPermissionGroup && it.targetSdk < targetSdk }
@@ -73,9 +71,7 @@ class SplitPermissionIndex() {
.toList()
}
- /**
- * Given a permission group, return which permissions split *to* it for the given targetSdk.
- */
+ /** Given a permission group, return which permissions split *to* it for the given targetSdk. */
fun getGroupToGroupSplitsTo(newPermissionGroup: String, targetSdk: Int): List<String> {
return groupToGroupSplits
.filter { it.newPerm == newPermissionGroup && it.targetSdk < targetSdk }
@@ -88,4 +84,4 @@ class SplitPermissionIndex() {
val targetSdk: Int,
val newPerm: String
)
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt
index 578b74783..18c40e0e4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt
@@ -29,11 +29,6 @@ import com.android.permissioncontroller.permission.data.v33.PermissionDecision
import com.android.permissioncontroller.permission.service.BasePermissionEventStorage
import com.android.permissioncontroller.permission.service.PermissionEventStorage
import com.android.permissioncontroller.permission.utils.Utils
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import org.xmlpull.v1.XmlPullParser
-import org.xmlpull.v1.XmlPullParserException
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
@@ -43,10 +38,13 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserException
-/**
- * Implementation of [BasePermissionEventStorage] for storing [PermissionDecision] events.
- */
+/** Implementation of [BasePermissionEventStorage] for storing [PermissionDecision] events. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class PermissionDecisionStorageImpl(
context: Context,
@@ -56,9 +54,7 @@ class PermissionDecisionStorageImpl(
// We don't use namespaces
private val ns: String? = null
- /**
- * The format for how dates are stored.
- */
+ /** The format for how dates are stored. */
private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US)
companion object {
@@ -66,9 +62,7 @@ class PermissionDecisionStorageImpl(
private const val DB_VERSION = 1
- /**
- * Config store file name for general shared store file.
- */
+ /** Config store file name for general shared store file. */
private const val STORE_FILE_NAME = "recent_permission_decisions.xml"
private const val TAG_RECENT_PERMISSION_DECISIONS = "recent-permission-decisions"
@@ -81,13 +75,10 @@ class PermissionDecisionStorageImpl(
private val DEFAULT_MAX_DATA_AGE_MS = TimeUnit.DAYS.toMillis(7)
- @Volatile
- private var INSTANCE: PermissionEventStorage<PermissionDecision>? = null
+ @Volatile private var INSTANCE: PermissionEventStorage<PermissionDecision>? = null
fun getInstance(): PermissionEventStorage<PermissionDecision> =
- INSTANCE ?: synchronized(this) {
- INSTANCE ?: createInstance().also { INSTANCE = it }
- }
+ INSTANCE ?: synchronized(this) { INSTANCE ?: createInstance().also { INSTANCE = it } }
private fun createInstance(): PermissionEventStorage<PermissionDecision> {
return PermissionDecisionStorageImpl(PermissionControllerApplication.get())
@@ -101,9 +92,15 @@ class PermissionDecisionStorageImpl(
) {
if (isRecordPermissionsSupported(context)) {
GlobalScope.launch(Dispatchers.IO) {
- getInstance().storeEvent(
- PermissionDecision(packageName, System.currentTimeMillis(), permGroupName,
- isGranted))
+ getInstance()
+ .storeEvent(
+ PermissionDecision(
+ packageName,
+ System.currentTimeMillis(),
+ permGroupName,
+ isGranted
+ )
+ )
}
}
}
@@ -148,9 +145,7 @@ class PermissionDecisionStorageImpl(
parser.require(XmlPullParser.START_TAG, ns, TAG_RECENT_PERMISSION_DECISIONS)
while (parser.next() != XmlPullParser.END_TAG) {
- readPermissionDecision(parser)?.let {
- entries.add(it)
- }
+ readPermissionDecision(parser)?.let { entries.add(it) }
}
return entries
}
@@ -163,9 +158,11 @@ class PermissionDecisionStorageImpl(
val packageName = parser.getAttributeValueNullSafe(ns, ATTR_PACKAGE_NAME)
val permissionGroup = parser.getAttributeValueNullSafe(ns, ATTR_PERMISSION_GROUP)
val decisionDate = parser.getAttributeValueNullSafe(ns, ATTR_DECISION_TIME)
- val decisionTime = dateFormat.parse(decisionDate)?.time
- ?: throw IllegalArgumentException(
- "Could not parse date $decisionDate on package $packageName")
+ val decisionTime =
+ dateFormat.parse(decisionDate)?.time
+ ?: throw IllegalArgumentException(
+ "Could not parse date $decisionDate on package $packageName"
+ )
val isGranted = parser.getAttributeValueNullSafe(ns, ATTR_IS_GRANTED).toBoolean()
decision = PermissionDecision(packageName, decisionTime, permissionGroup, isGranted)
} catch (e: XmlPullParserException) {
@@ -185,7 +182,8 @@ class PermissionDecisionStorageImpl(
private fun XmlPullParser.getAttributeValueNullSafe(namespace: String?, name: String): String {
return this.getAttributeValue(namespace, name)
?: throw XmlPullParserException(
- "Could not find attribute: namespace $namespace, name $name")
+ "Could not find attribute: namespace $namespace, name $name"
+ )
}
override fun getDatabaseFileName(): String {
@@ -193,9 +191,11 @@ class PermissionDecisionStorageImpl(
}
override fun getMaxDataAgeMs(): Long {
- return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
+ return DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_PERMISSIONS,
Utils.PROPERTY_PERMISSION_DECISIONS_MAX_DATA_AGE_MILLIS,
- DEFAULT_MAX_DATA_AGE_MS)
+ DEFAULT_MAX_DATA_AGE_MS
+ )
}
override fun hasTheSamePrimaryKey(
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt
index 9231dc17b..b627664d4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt
@@ -56,10 +56,10 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.APP_DATA_SH
import com.android.permissioncontroller.PermissionControllerStatsLog.APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION__ACTION__DISMISSED
import com.android.permissioncontroller.PermissionControllerStatsLog.APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION__ACTION__NOTIFICATION_SHOWN
import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.data.v34.LightInstallSourceInfoLiveData
import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
import com.android.permissioncontroller.permission.data.SinglePermGroupPackagesUiInfoLiveData
import com.android.permissioncontroller.permission.data.v34.AppDataSharingUpdatesLiveData
+import com.android.permissioncontroller.permission.data.v34.LightInstallSourceInfoLiveData
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState.PERMS_ALLOWED_ALWAYS
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY
@@ -419,9 +419,7 @@ class SafetyLabelChangesJobService : JobService() {
// preinstalled apps.
private suspend fun getAllStoreInstalledPackagesRequestingLocation():
Set<Pair<String, UserHandle>> =
- getAllPackagesRequestingLocation()
- .filter { isSafetyLabelSupported(it) }
- .toSet()
+ getAllPackagesRequestingLocation().filter { isSafetyLabelSupported(it) }.toSet()
private suspend fun getAllPackagesRequestingLocation(): Set<Pair<String, UserHandle>> =
SinglePermGroupPackagesUiInfoLiveData[Manifest.permission_group.LOCATION]
@@ -439,7 +437,7 @@ class SafetyLabelChangesJobService : JobService() {
private suspend fun isSafetyLabelSupported(packageUser: Pair<String, UserHandle>): Boolean {
val lightInstallSourceInfo =
- LightInstallSourceInfoLiveData[packageUser].getInitializedValue()
+ LightInstallSourceInfoLiveData[packageUser].getInitializedValue()
return lightInstallSourceInfo.supportsSafetyLabel
}
@@ -531,8 +529,9 @@ class SafetyLabelChangesJobService : JobService() {
createNotificationChannel(context, notificationManager)
val (appLabel, smallIcon, color) = KotlinUtils.getSafetyCenterNotificationResources(this)
- val smallIconCompat = IconCompat.createFromIcon(smallIcon)
- ?: IconCompat.createWithResource(this, R.drawable.ic_info)
+ val smallIconCompat =
+ IconCompat.createFromIcon(smallIcon)
+ ?: IconCompat.createWithResource(this, R.drawable.ic_info)
val title = context.getString(R.string.safety_label_changes_notification_title)
val text = context.getString(R.string.safety_label_changes_notification_desc)
var notificationBuilder =
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt
index 61336cdce..5daf26883 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt
@@ -24,5 +24,6 @@ enum class Category(val categoryName: String) {
ALLOWED("allowed"),
ALLOWED_FOREGROUND("allowed_foreground"),
ASK("ask"),
- DENIED("denied")
+ DENIED("denied"),
+ STORAGE_FOOTER("storage_footer_category"),
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
index 963c922d5..2a900105c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
@@ -55,7 +55,6 @@ import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.util.Log;
import android.util.Pair;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.Window;
@@ -66,7 +65,6 @@ import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
-import androidx.core.util.Consumer;
import androidx.core.util.Preconditions;
import com.android.modules.utils.build.SdkLevel;
@@ -177,7 +175,6 @@ public class GrantPermissionsActivity extends SettingsActivity
private String mTargetPackage;
/** A key representing this activity, defined by the target package and task ID */
private Pair<String, Integer> mKey;
- private int mCurrentRequestIdx = 0;
private float mOriginalDimAmount;
private View mRootView;
private int mStoragePermGroupIcon = R.drawable.ic_empty_icon;
@@ -457,7 +454,7 @@ public class GrantPermissionsActivity extends SettingsActivity
mViewModel.sendDirectlyToSettings(this, info.getGroupName());
return;
} else if (info.getOpenPhotoPicker()) {
- mViewModel.openPhotoPicker(this, GRANTED_USER_SELECTED);
+ mViewModel.openPhotoPicker(this);
return;
}
@@ -546,8 +543,6 @@ public class GrantPermissionsActivity extends SettingsActivity
Log.e(LOG_TAG, "Cannot load icon for group" + info.getGroupName(), e);
}
- boolean showingNewGroup = message == null || !message.equals(getTitle());
-
// Set the permission message as the title so it can be announced. Skip on Wear
// because the dialog title is already announced, as is the default selection which
// is a text view containing the title.
@@ -581,40 +576,21 @@ public class GrantPermissionsActivity extends SettingsActivity
mRequestCounts = mRequestInfos.size();
}
- mViewHandler.updateUi(info.getGroupName(), mRequestCounts, mCurrentRequestIdx, icon,
+ int pageIdx = mRequestCounts - mRequestInfos.size();
+ mViewHandler.updateUi(info.getGroupName(), mRequestCounts, pageIdx, icon,
message, detailMessage, permissionRationaleMessage, mButtonVisibilities,
locationVisibilities);
- if (showingNewGroup) {
- mCurrentRequestIdx++;
- }
getWindow().setDimAmount(mOriginalDimAmount);
if (mRootView.getVisibility() == View.GONE) {
- InputMethodManager manager = getSystemService(InputMethodManager.class);
- manager.hideSoftInputFromWindow(mRootView.getWindowToken(), 0);
- mRootView.setVisibility(View.VISIBLE);
- }
- }
-
- // LINT.IfChange(dispatchTouchEvent)
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- View rootView = getWindow().getDecorView();
- if (rootView.getTop() != 0) {
- // We are animating the top view, need to compensate for that in motion events.
- ev.setLocation(ev.getX(), ev.getY() - rootView.getTop());
- }
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
- if ((x < 0) || (y < 0) || (x > (rootView.getWidth())) || (y > (rootView.getHeight()))) {
- if (MotionEvent.ACTION_DOWN == ev.getAction()) {
- mViewHandler.onCancelled();
+ if (mIsSystemTriggered) {
+ // We don't want the keyboard obscuring system-triggered dialogs
+ InputMethodManager manager = getSystemService(InputMethodManager.class);
+ manager.hideSoftInputFromWindow(mRootView.getWindowToken(), 0);
}
- finishAfterTransition();
+ mRootView.setVisibility(View.VISIBLE);
}
- return super.dispatchTouchEvent(ev);
}
- // LINT.ThenChange(PermissionRationaleActivity.java:dispatchTouchEvent)
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
@@ -645,16 +621,14 @@ public class GrantPermissionsActivity extends SettingsActivity
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- Consumer<Intent> callback = mViewModel.getActivityResultCallback();
- if (callback == null || (requestCode != APP_PERMISSION_REQUEST_CODE
- && requestCode != PHOTO_PICKER_REQUEST_CODE)) {
+ if (requestCode != APP_PERMISSION_REQUEST_CODE
+ && requestCode != PHOTO_PICKER_REQUEST_CODE) {
return;
}
if (requestCode == PHOTO_PICKER_REQUEST_CODE) {
data = new Intent("").putExtra(INTENT_PHOTOS_SELECTED, resultCode == RESULT_OK);
}
- callback.accept(data);
- mViewModel.setActivityResultCallback(null);
+ mViewModel.handleCallback(data, requestCode);
}
@Override
@@ -674,11 +648,8 @@ public class GrantPermissionsActivity extends SettingsActivity
mPreMergeShownGroupName = null;
}
- if (Objects.equals(READ_MEDIA_VISUAL, name)
- && result == GrantPermissionsViewHandler.GRANTED_USER_SELECTED) {
- // Only the top activity can receive activity results
- Activity top = mFollowerActivities.isEmpty() ? this : mFollowerActivities.get(0);
- mViewModel.openPhotoPicker(this, result);
+ if (Objects.equals(READ_MEDIA_VISUAL, name) && result == GRANTED_USER_SELECTED) {
+ mViewModel.openPhotoPicker(this);
logGrantPermissionActivityButtons(name, affectedForegroundPermissions, result);
return;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
index afeb19aa9..81af33294 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
@@ -31,7 +31,6 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.PERM
import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__OPEN;
import android.Manifest;
-import android.app.ActionBar;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -549,15 +548,6 @@ public final class ManagePermissionsActivity extends SettingsActivity {
}
@Override
- public ActionBar getActionBar() {
- ActionBar ab = super.getActionBar();
- if (ab != null) {
- ab.setHomeActionContentDescription(R.string.back);
- }
- return ab;
- }
-
- @Override
public boolean onOptionsItemSelected(MenuItem item) {
// in automotive mode, there's no system wide back button, so need to add that
if (DeviceUtils.isAuto(this)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt
index 2525c191c..df4e7947a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt
@@ -16,17 +16,11 @@
package com.android.permissioncontroller.permission.ui
-/**
- * Preference with a clickable UI component for removal.
- */
+/** Preference with a clickable UI component for removal. */
interface RemovablePref {
- /**
- * Sets the action to run when the remove UI component is clicked.
- */
+ /** Sets the action to run when the remove UI component is clicked. */
fun setRemoveClickRunnable(runnable: Runnable)
- /**
- * Set whether the UI component for removal should be enabled or not.
- */
+ /** Set whether the UI component for removal should be enabled or not. */
fun setRemoveComponentEnabled(enabled: Boolean)
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
index 68fb493eb..69aef1a4f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
@@ -52,12 +52,14 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils
import java.text.Collator
/**
- * A fragment displaying all applications that are unused as well as the option to remove them
- * and to open them.
+ * A fragment displaying all applications that are unused as well as the option to remove them and
+ * to open them.
*/
-class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
- where PF : PreferenceFragmentCompat, PF : UnusedAppsFragment.Parent<UnusedAppPref>,
- UnusedAppPref : Preference, UnusedAppPref : RemovablePref {
+class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() where
+PF : PreferenceFragmentCompat,
+PF : UnusedAppsFragment.Parent<UnusedAppPref>,
+UnusedAppPref : Preference,
+UnusedAppPref : RemovablePref {
private lateinit var viewModel: UnusedAppsViewModel
private lateinit var collator: Collator
@@ -72,9 +74,11 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
private val LOG_TAG = UnusedAppsFragment::class.java.simpleName
@JvmStatic
- fun <PF, UnusedAppPref> newInstance(): UnusedAppsFragment<PF, UnusedAppPref>
- where PF : PreferenceFragmentCompat, PF : UnusedAppsFragment.Parent<UnusedAppPref>,
- UnusedAppPref : Preference, UnusedAppPref : RemovablePref {
+ fun <PF, UnusedAppPref> newInstance(): UnusedAppsFragment<PF, UnusedAppPref> where
+ PF : PreferenceFragmentCompat,
+ PF : UnusedAppsFragment.Parent<UnusedAppPref>,
+ UnusedAppPref : Preference,
+ UnusedAppPref : RemovablePref {
return UnusedAppsFragment()
}
@@ -82,7 +86,6 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
* Create the args needed for this fragment
*
* @param sessionId The current session Id
- *
* @return A bundle containing the session Id
*/
@JvmStatic
@@ -101,29 +104,35 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
val preferenceFragment: PF = requirePreferenceFragment()
isFirstLoad = true
- collator = Collator.getInstance(
- context!!.getResources().getConfiguration().getLocales().get(0))
+ collator =
+ Collator.getInstance(context!!.getResources().getConfiguration().getLocales().get(0))
sessionId = arguments!!.getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID)
val factory = UnusedAppsViewModelFactory(activity!!.application, sessionId)
viewModel = ViewModelProvider(this, factory).get(UnusedAppsViewModel::class.java)
- viewModel.unusedPackageCategoriesLiveData.observe(this, Observer {
- it?.let { pkgs ->
- updatePackages(pkgs)
- preferenceFragment.setLoadingState(loading = false, animate = true)
+ viewModel.unusedPackageCategoriesLiveData.observe(
+ this,
+ Observer {
+ it?.let { pkgs ->
+ updatePackages(pkgs)
+ preferenceFragment.setLoadingState(loading = false, animate = true)
+ }
}
- })
+ )
activity?.getActionBar()?.setDisplayHomeAsUpEnabled(true)
if (!viewModel.unusedPackageCategoriesLiveData.isInitialized) {
val handler = Handler(Looper.getMainLooper())
- handler.postDelayed({
- if (!viewModel.unusedPackageCategoriesLiveData.isInitialized) {
- preferenceFragment.setLoadingState(loading = true, animate = true)
- } else {
- updatePackages(viewModel.unusedPackageCategoriesLiveData.value!!)
- }
- }, SHOW_LOAD_DELAY_MS)
+ handler.postDelayed(
+ {
+ if (!viewModel.unusedPackageCategoriesLiveData.isInitialized) {
+ preferenceFragment.setLoadingState(loading = true, animate = true)
+ } else {
+ updatePackages(viewModel.unusedPackageCategoriesLiveData.value!!)
+ }
+ },
+ SHOW_LOAD_DELAY_MS
+ )
} else {
updatePackages(viewModel.unusedPackageCategoriesLiveData.value!!)
}
@@ -150,15 +159,15 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
return requireParentFragment() as PF
}
- /**
- * Create [PreferenceScreen] in the parent fragment.
- */
+ /** Create [PreferenceScreen] in the parent fragment. */
private fun createPreferenceScreen() {
val preferenceFragment: PF = requirePreferenceFragment()
- val preferenceScreen = preferenceFragment.preferenceManager.inflateFromResource(
- context!!,
- R.xml.unused_app_categories,
- /* rootPreferences= */ null)
+ val preferenceScreen =
+ preferenceFragment.preferenceManager.inflateFromResource(
+ context!!,
+ R.xml.unused_app_categories,
+ /* rootPreferences= */ null
+ )
for (period in allPeriods) {
val periodCat = PreferenceCategory(context!!)
@@ -208,8 +217,10 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
val category = preferenceScreen.findPreference<PreferenceCategory>(period.name)!!
val months = period.months
category.title =
- MessageFormat.format(getString(R.string.last_opened_category_title),
- mapOf("count" to months))
+ MessageFormat.format(
+ getString(R.string.last_opened_category_title),
+ mapOf("count" to months)
+ )
category.isVisible = packages.isNotEmpty()
if (packages.isNotEmpty()) {
allCategoriesEmpty = false
@@ -221,39 +232,53 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
var pref = category.findPreference<UnusedAppPref>(key)
if (pref == null) {
- pref = removedPrefs[key] ?: preferenceFragment.createUnusedAppPref(
- activity!!.application, pkgName, user)
+ pref =
+ removedPrefs[key]
+ ?: preferenceFragment.createUnusedAppPref(
+ activity!!.application,
+ pkgName,
+ user
+ )
pref.key = key
pref.title = KotlinUtils.getPackageLabel(activity!!.application, pkgName, user)
}
- pref.setRemoveClickRunnable {
- viewModel.requestUninstallApp(this, pkgName, user)
- }
+ pref.setRemoveClickRunnable { viewModel.requestUninstallApp(this, pkgName, user) }
pref.setRemoveComponentEnabled(!isSystemApp)
- pref.onPreferenceClickListener = Preference.OnPreferenceClickListener { _ ->
- viewModel.navigateToAppInfo(pkgName, user, sessionId)
- true
- }
+ pref.onPreferenceClickListener =
+ Preference.OnPreferenceClickListener { _ ->
+ viewModel.navigateToAppInfo(pkgName, user, sessionId)
+ true
+ }
val mostImportant = getMostImportantGroup(revokedPerms)
val importantLabel = KotlinUtils.getPermGroupLabel(context!!, mostImportant)
- pref.summary = when {
- revokedPerms.isEmpty() -> null
- revokedPerms.size == 1 -> getString(R.string.auto_revoked_app_summary_one,
- importantLabel)
- revokedPerms.size == 2 -> {
- val otherLabel = if (revokedPerms[0] == mostImportant) {
- KotlinUtils.getPermGroupLabel(context!!, revokedPerms[1])
- } else {
- KotlinUtils.getPermGroupLabel(context!!, revokedPerms[0])
+ pref.summary =
+ when {
+ revokedPerms.isEmpty() -> null
+ revokedPerms.size == 1 ->
+ getString(R.string.auto_revoked_app_summary_one, importantLabel)
+ revokedPerms.size == 2 -> {
+ val otherLabel =
+ if (revokedPerms[0] == mostImportant) {
+ KotlinUtils.getPermGroupLabel(context!!, revokedPerms[1])
+ } else {
+ KotlinUtils.getPermGroupLabel(context!!, revokedPerms[0])
+ }
+ getString(
+ R.string.auto_revoked_app_summary_two,
+ importantLabel,
+ otherLabel
+ )
}
- getString(R.string.auto_revoked_app_summary_two, importantLabel, otherLabel)
+ else ->
+ getString(
+ R.string.auto_revoked_app_summary_many,
+ importantLabel,
+ "${revokedPerms.size - 1}"
+ )
}
- else -> getString(R.string.auto_revoked_app_summary_many, importantLabel,
- "${revokedPerms.size - 1}")
- }
category.addPreference(pref)
KotlinUtils.sortPreferenceGroup(category, this::comparePreference, false)
}
@@ -267,13 +292,19 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
}
Log.i(LOG_TAG, "sessionId: $sessionId Showed Auto Revoke Page")
for (period in allPeriods) {
- Log.i(LOG_TAG, "sessionId: $sessionId $period unused: " +
- "${categorizedPackages[period]}")
+ Log.i(
+ LOG_TAG,
+ "sessionId: $sessionId $period unused: " + "${categorizedPackages[period]}"
+ )
for (revokedPackageInfo in categorizedPackages[period]!!) {
for (groupName in revokedPackageInfo.revokedGroups) {
val isNewlyRevoked = period.isNewlyUnused()
- viewModel.logAppView(revokedPackageInfo.packageName,
- revokedPackageInfo.user, groupName, isNewlyRevoked)
+ viewModel.logAppView(
+ revokedPackageInfo.packageName,
+ revokedPackageInfo.user,
+ groupName,
+ isNewlyRevoked
+ )
}
}
}
@@ -281,8 +312,7 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
}
private fun comparePreference(lhs: Preference, rhs: Preference): Int {
- var result = collator.compare(lhs.title.toString(),
- rhs.title.toString())
+ var result = collator.compare(lhs.title.toString(), rhs.title.toString())
if (result == 0) {
result = lhs.key.compareTo(rhs.key)
}
@@ -324,23 +354,23 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
val fragment = parentFragment as UnusedAppsFragment<*, *>
val packageName = arguments!!.getString(Intent.EXTRA_PACKAGE_NAME)!!
val user = arguments!!.getParcelable<UserHandle>(Intent.EXTRA_USER)!!
- val b = AlertDialog.Builder(context!!)
- .setMessage(R.string.app_disable_dlg_text)
- .setPositiveButton(R.string.app_disable_dlg_positive) { _, _ ->
- fragment.viewModel.disableApp(packageName, user)
- }
- .setNegativeButton(R.string.cancel, null)
+ val b =
+ AlertDialog.Builder(context!!)
+ .setMessage(R.string.app_disable_dlg_text)
+ .setPositiveButton(R.string.app_disable_dlg_positive) { _, _ ->
+ fragment.viewModel.disableApp(packageName, user)
+ }
+ .setNegativeButton(R.string.cancel, null)
val d: Dialog = b.create()
d.setCanceledOnTouchOutside(true)
return d
}
}
- /**
- * Interface that the parent fragment must implement.
- */
- interface Parent<UnusedAppPref> where UnusedAppPref : Preference,
- UnusedAppPref : RemovablePref {
+ /** Interface that the parent fragment must implement. */
+ interface Parent<UnusedAppPref> where
+ UnusedAppPref : Preference,
+ UnusedAppPref : RemovablePref {
/**
* Set the title of the current settings page.
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
index 2de936469..d3a89c3ed 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
@@ -418,7 +418,7 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment
// TODO(b/229024576): This code is duplicated, refactor ConfirmDialog for easier
// NFF sharing
boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST)
- == ChangeRequest.GRANT_All_FILE_ACCESS;
+ == ChangeRequest.GRANT_ALL_FILE_ACCESS;
boolean isGrantStorageSupergroup = getArguments().getSerializable(CHANGE_REQUEST)
== ChangeRequest.GRANT_STORAGE_SUPERGROUP;
int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoDividerPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoDividerPreference.kt
index aa3c7ffd7..7bb4ccb05 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoDividerPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoDividerPreference.kt
@@ -20,9 +20,7 @@ import android.util.AttributeSet
import androidx.preference.Preference
import com.android.permissioncontroller.R
-/**
- * Non-interactive preference that displays a horizontal divider.
- */
+/** Non-interactive preference that displays a horizontal divider. */
class AutoDividerPreference : Preference {
constructor(
context: Context?,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
index 29fcb43b2..3b8419bcf 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
@@ -22,6 +22,7 @@ import static com.android.permissioncontroller.permission.ui.Category.ALLOWED;
import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND;
import static com.android.permissioncontroller.permission.ui.Category.ASK;
import static com.android.permissioncontroller.permission.ui.Category.DENIED;
+import static com.android.permissioncontroller.permission.ui.Category.STORAGE_FOOTER;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
import android.content.Context;
@@ -52,15 +53,15 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.Utils;
import com.android.settingslib.utils.applications.AppUtils;
+import kotlin.Pair;
+import kotlin.Triple;
+
import java.text.Collator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
-import kotlin.Pair;
-import kotlin.Triple;
-
/** Shows the list of applications which have (or do not have) the given permission. */
public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implements
PermissionUsages.PermissionsUsagesChangeCallback {
@@ -172,6 +173,10 @@ public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implem
}
// Hide allowed foreground label by default, to avoid briefly showing it before updating
findPreference(ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);
+
+ // Hide storage footer category
+ findPreference(STORAGE_FOOTER.getCategoryName()).setVisible(false);
+
Context context = getPreferenceManager().getContext();
if (context == null || getActivity() == null || categories == null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt
index 92917a342..99f5c85e4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt
@@ -53,20 +53,19 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() {
private const val LOG_TAG = "AutoReviewPermissionDecisionsFragment"
private const val MAX_DECISIONS = 3
- /**
- * Creates a new instance of [AutoReviewPermissionDecisionsFragment].
- */
+ /** Creates a new instance of [AutoReviewPermissionDecisionsFragment]. */
fun newInstance(
sessionId: Long,
userHandle: UserHandle,
source: String?
): AutoReviewPermissionDecisionsFragment {
return AutoReviewPermissionDecisionsFragment().apply {
- arguments = Bundle().apply {
- putLong(Constants.EXTRA_SESSION_ID, sessionId)
- putParcelable(Intent.EXTRA_USER, userHandle)
- putString(EXTRA_SOURCE, source)
- }
+ arguments =
+ Bundle().apply {
+ putLong(Constants.EXTRA_SESSION_ID, sessionId)
+ putParcelable(Intent.EXTRA_USER, userHandle)
+ putString(EXTRA_SOURCE, source)
+ }
}
}
}
@@ -95,21 +94,23 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() {
}
user = requireArguments().getParcelable<UserHandle>(Intent.EXTRA_USER)!!
sessionId = requireArguments().getLong(Constants.EXTRA_SESSION_ID)
- if (requireArguments().containsKey(EXTRA_SOURCE) &&
- (requireArguments().getString(EXTRA_SOURCE) == EXTRA_SOURCE_NOTIFICATION)) {
+ if (
+ requireArguments().containsKey(EXTRA_SOURCE) &&
+ (requireArguments().getString(EXTRA_SOURCE) == EXTRA_SOURCE_NOTIFICATION)
+ ) {
logDecisionReminderNotificationClicked()
}
- val factory = ReviewPermissionDecisionsViewModelFactory(
- requireActivity().getApplication()!!, user)
- viewModel = ViewModelProvider(this,
- factory)[ReviewPermissionDecisionsViewModel::class.java]
+ val factory =
+ ReviewPermissionDecisionsViewModelFactory(requireActivity().getApplication()!!, user)
+ viewModel = ViewModelProvider(this, factory)[ReviewPermissionDecisionsViewModel::class.java]
addPrivacyDashboardPreference()
addPermissionManagerPreference()
preferenceScreen.addPreference(AutoDividerPreference(context))
- recentPermissionsGroup = PreferenceCategory(context!!).apply {
- title = getString(R.string.review_permission_decisions)
- }
+ recentPermissionsGroup =
+ PreferenceCategory(context!!).apply {
+ title = getString(R.string.review_permission_decisions)
+ }
preferenceScreen.addPreference(recentPermissionsGroup)
viewModel.recentPermissionDecisionsLiveData.observe(this) { recentDecisions ->
@@ -138,34 +139,43 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() {
}
private fun addPrivacyDashboardPreference() {
- val preference = CarUiPreference(context).apply {
- title = getString(R.string.permission_usage_title)
- summary = getString(R.string.auto_permission_usage_summary)
- onPreferenceClickListener = Preference.OnPreferenceClickListener { _ ->
- val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply {
- putExtra(Constants.EXTRA_SESSION_ID, sessionId)
- }
- startActivity(intent)
- true
+ val preference =
+ CarUiPreference(context).apply {
+ title = getString(R.string.permission_usage_title)
+ summary = getString(R.string.auto_permission_usage_summary)
+ onPreferenceClickListener =
+ Preference.OnPreferenceClickListener { _ ->
+ val intent =
+ Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply {
+ putExtra(Constants.EXTRA_SESSION_ID, sessionId)
+ }
+ startActivity(intent)
+ true
+ }
}
- }
preferenceScreen.addPreference(preference)
}
private fun addPermissionManagerPreference() {
- val preference = CarUiPreference(context).apply {
- title = getString(R.string.app_permission_manager)
- summary = getString(R.string.auto_permission_manager_summary)
- onPreferenceClickListener = Preference.OnPreferenceClickListener { _ ->
- val intent = Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
- putExtra(Intent.EXTRA_USER, user)
- putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME, javaClass.name)
- putExtra(Constants.EXTRA_SESSION_ID, sessionId)
- }
- startActivity(intent)
- true
+ val preference =
+ CarUiPreference(context).apply {
+ title = getString(R.string.app_permission_manager)
+ summary = getString(R.string.auto_permission_manager_summary)
+ onPreferenceClickListener =
+ Preference.OnPreferenceClickListener { _ ->
+ val intent =
+ Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
+ putExtra(Intent.EXTRA_USER, user)
+ putExtra(
+ ManagePermissionsActivity.EXTRA_CALLER_NAME,
+ javaClass.name
+ )
+ putExtra(Constants.EXTRA_SESSION_ID, sessionId)
+ }
+ startActivity(intent)
+ true
+ }
}
- }
preferenceScreen.addPreference(preference)
}
@@ -175,46 +185,57 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() {
) {
for (i in 0 until min(recentDecisions.size, MAX_DECISIONS)) {
val recentDecision = recentDecisions[i]
- val decisionPreference = CarUiPreference(context).apply {
- icon = viewModel.getAppIcon(recentDecision.packageName)
- title = viewModel.createPreferenceTitle(recentDecision)
- summary = viewModel.createSummaryText(recentDecision)
- onPreferenceClickListener = Preference.OnPreferenceClickListener {
- viewModel.createManageAppPermissionIntent(recentDecision).also {
- startActivity(it)
- }
- logPermissionDecisionClicked(recentDecision.packageName,
- recentDecision.permissionGroupName)
- true
+ val decisionPreference =
+ CarUiPreference(context).apply {
+ icon = viewModel.getAppIcon(recentDecision.packageName)
+ title = viewModel.createPreferenceTitle(recentDecision)
+ summary = viewModel.createSummaryText(recentDecision)
+ onPreferenceClickListener =
+ Preference.OnPreferenceClickListener {
+ viewModel.createManageAppPermissionIntent(recentDecision).also {
+ startActivity(it)
+ }
+ logPermissionDecisionClicked(
+ recentDecision.packageName,
+ recentDecision.permissionGroupName
+ )
+ true
+ }
}
- }
preferenceGroup.addPreference(decisionPreference)
}
}
private fun addViewAllPreference(preferenceGroup: PreferenceGroup) {
val viewAllIcon = requireContext().getDrawable(R.drawable.car_ic_apps)
- val preference = CarUiPreference(context).apply {
- icon = Utils.applyTint(context, viewAllIcon, android.R.attr.colorControlNormal)
- title = getString(R.string.review_permission_decisions_view_all)
- onPreferenceClickListener = Preference.OnPreferenceClickListener {
- val frag = AutoReviewPermissionDecisionsViewAllFragment.newInstance(sessionId,
- user)
- getParentFragmentManager().beginTransaction()
- .replace(android.R.id.content, frag)
- .addToBackStack(null)
- .commit()
- logViewAllClicked()
- true
+ val preference =
+ CarUiPreference(context).apply {
+ icon = Utils.applyTint(context, viewAllIcon, android.R.attr.colorControlNormal)
+ title = getString(R.string.review_permission_decisions_view_all)
+ onPreferenceClickListener =
+ Preference.OnPreferenceClickListener {
+ val frag =
+ AutoReviewPermissionDecisionsViewAllFragment.newInstance(
+ sessionId,
+ user
+ )
+ getParentFragmentManager()
+ .beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack(null)
+ .commit()
+ logViewAllClicked()
+ true
+ }
}
- }
preferenceGroup.addPreference(preference)
}
private fun addNoRecentDecisionsPreference(preferenceGroup: PreferenceGroup) {
- val preference = CarUiPreference(context).apply {
- title = getString(R.string.review_permission_decisions_empty)
- }
+ val preference =
+ CarUiPreference(context).apply {
+ title = getString(R.string.review_permission_decisions_empty)
+ }
preferenceGroup.addPreference(preference)
}
@@ -224,7 +245,8 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() {
sessionId,
RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__SCREEN_VIEWED,
0,
- null)
+ null
+ )
}
private fun logViewAllClicked() {
@@ -233,7 +255,8 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() {
sessionId,
RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__VIEW_ALL_CLICKED,
0,
- null)
+ null
+ )
}
private fun logPermissionDecisionClicked(packageName: String, permissionGroupName: String) {
@@ -243,12 +266,15 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() {
sessionId,
RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__REVIEW_DECISION,
uid,
- permissionGroupName)
+ permissionGroupName
+ )
}
private fun logDecisionReminderNotificationClicked() {
PermissionControllerStatsLog.write(
PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED,
- sessionId, PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_CLICKED)
+ sessionId,
+ PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_CLICKED
+ )
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsViewAllFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsViewAllFragment.kt
index 9f9471fdf..11cca4693 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsViewAllFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsViewAllFragment.kt
@@ -39,18 +39,17 @@ class AutoReviewPermissionDecisionsViewAllFragment : AutoSettingsFrameFragment()
companion object {
private const val LOG_TAG = "AutoReviewPermissionDecisionsViewAllFragment"
- /**
- * Creates a new instance of [AutoReviewPermissionDecisionsViewAllFragment].
- */
+ /** Creates a new instance of [AutoReviewPermissionDecisionsViewAllFragment]. */
fun newInstance(
sessionId: Long,
userHandle: UserHandle
): AutoReviewPermissionDecisionsViewAllFragment {
return AutoReviewPermissionDecisionsViewAllFragment().apply {
- arguments = Bundle().apply {
- putLong(Constants.EXTRA_SESSION_ID, sessionId)
- putParcelable(Intent.EXTRA_USER, userHandle)
- }
+ arguments =
+ Bundle().apply {
+ putLong(Constants.EXTRA_SESSION_ID, sessionId)
+ putParcelable(Intent.EXTRA_USER, userHandle)
+ }
}
}
}
@@ -71,10 +70,9 @@ class AutoReviewPermissionDecisionsViewAllFragment : AutoSettingsFrameFragment()
return
}
user = requireArguments().getParcelable<UserHandle>(Intent.EXTRA_USER)!!
- val factory = ReviewPermissionDecisionsViewModelFactory(
- requireActivity().getApplication()!!, user)
- viewModel = ViewModelProvider(this,
- factory)[ReviewPermissionDecisionsViewModel::class.java]
+ val factory =
+ ReviewPermissionDecisionsViewModelFactory(requireActivity().getApplication()!!, user)
+ viewModel = ViewModelProvider(this, factory)[ReviewPermissionDecisionsViewModel::class.java]
viewModel.recentPermissionDecisionsLiveData.observe(this) { recentDecisions ->
onRecentDecisionsChanged(recentDecisions)
}
@@ -88,17 +86,19 @@ class AutoReviewPermissionDecisionsViewAllFragment : AutoSettingsFrameFragment()
private fun onRecentDecisionsChanged(recentDecisions: List<PermissionDecision>) {
preferenceScreen.removeAll()
for (recentDecision in recentDecisions) {
- val decisionPreference = CarUiPreference(context).apply {
- icon = viewModel.getAppIcon(recentDecision.packageName)
- title = viewModel.createPreferenceTitle(recentDecision)
- summary = viewModel.createSummaryText(recentDecision)
- onPreferenceClickListener = Preference.OnPreferenceClickListener {
- viewModel.createManageAppPermissionIntent(recentDecision).also {
- startActivity(it)
- }
- false
+ val decisionPreference =
+ CarUiPreference(context).apply {
+ icon = viewModel.getAppIcon(recentDecision.packageName)
+ title = viewModel.createPreferenceTitle(recentDecision)
+ summary = viewModel.createSummaryText(recentDecision)
+ onPreferenceClickListener =
+ Preference.OnPreferenceClickListener {
+ viewModel.createManageAppPermissionIntent(recentDecision).also {
+ startActivity(it)
+ }
+ false
+ }
}
- }
preferenceScreen.addPreference(decisionPreference)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
index 61f77c81a..d798291e0 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
@@ -30,16 +30,14 @@ import com.android.permissioncontroller.hibernation.isHibernationEnabled
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY
-/**
- * Auto wrapper, with customizations, around [UnusedAppsFragment].
- */
-class AutoUnusedAppsFragment : AutoSettingsFrameFragment(),
- UnusedAppsFragment.Parent<AutoUnusedAppsPreference> {
+/** Auto wrapper, with customizations, around [UnusedAppsFragment]. */
+class AutoUnusedAppsFragment :
+ AutoSettingsFrameFragment(), UnusedAppsFragment.Parent<AutoUnusedAppsPreference> {
companion object {
private const val UNUSED_PREFERENCE_KEY = "unused_pref_row_key"
- /** Create a new instance of this fragment. */
+ /** Create a new instance of this fragment. */
@JvmStatic
fun newInstance(): AutoUnusedAppsFragment {
return AutoUnusedAppsFragment()
@@ -53,15 +51,12 @@ class AutoUnusedAppsFragment : AutoSettingsFrameFragment(),
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null) {
- val fragment:
- UnusedAppsFragment<AutoUnusedAppsFragment, AutoUnusedAppsPreference> =
+ val fragment: UnusedAppsFragment<AutoUnusedAppsFragment, AutoUnusedAppsPreference> =
UnusedAppsFragment.newInstance()
fragment.arguments = arguments
// child fragment does not have its own UI - it will add to the preferences of this
// parent fragment
- childFragmentManager.beginTransaction()
- .add(fragment, null)
- .commit()
+ childFragmentManager.beginTransaction().add(fragment, null).commit()
}
// initially focus on focus parking view and then shift focus to recyclerview once it has
@@ -76,10 +71,12 @@ class AutoUnusedAppsFragment : AutoSettingsFrameFragment(),
if (isHibernationEnabled()) {
preference.summary = getString(R.string.unused_apps_page_summary)
} else {
- preference.summary = """
+ preference.summary =
+ """
${getString(R.string.auto_revoked_apps_page_summary)}
${getString(R.string.auto_revoke_open_app_message)}
- """.trimIndent()
+ """
+ .trimIndent()
}
preference.setIcon(R.drawable.ic_info_outline)
preference.isSelectable = false
@@ -104,9 +101,9 @@ class AutoUnusedAppsFragment : AutoSettingsFrameFragment(),
override fun setEmptyState(empty: Boolean) {
val infoMsgCategory =
- preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
+ preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
val noUnusedAppsPreference: Preference? =
- infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY)
+ infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY)
if (empty && noUnusedAppsPreference == null) {
infoMsgCategory.addPreference(createNoUnusedAppsPreference())
} else if (noUnusedAppsPreference != null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsPreference.kt
index 9835a90f4..f1b65b38f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsPreference.kt
@@ -52,4 +52,4 @@ class AutoUnusedAppsPreference(
override fun setRemoveComponentEnabled(enabled: Boolean) {
setSecondaryActionEnabled(enabled)
}
-} \ No newline at end of file
+}
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 7ea400127..08460178c 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,8 +23,8 @@ 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.model.v31.PermissionUsageDetailsViewModel
import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelLegacy
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel
/** Preference that displays a permission usage for an app. */
@RequiresApi(Build.VERSION_CODES.S)
@@ -40,7 +40,8 @@ class AutoPermissionHistoryPreference(
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)
}
@@ -60,7 +61,9 @@ class AutoPermissionHistoryPreference(
accessEndTime = historyPreferenceData.accessEndTime,
accessStartTime = historyPreferenceData.accessStartTime,
showingAttribution = historyPreferenceData.showingAttribution,
- attributionTags = historyPreferenceData.attributionTags))
+ attributionTags = historyPreferenceData.attributionTags
+ )
+ )
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 a16bc1ce4..481543eb6 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
@@ -112,8 +112,10 @@ class AutoPermissionUsageDetailsFragment :
activity?.finish()
return
}
- if (!requireArguments().containsKey(Intent.EXTRA_PERMISSION_GROUP_NAME) or
- (requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME) == null)) {
+ if (
+ !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}")
activity?.finish()
return
@@ -128,14 +130,19 @@ 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(
- PermissionControllerApplication.get(), roleManager, filterGroup, sessionId)
+ PermissionControllerApplication.get(),
+ roleManager,
+ filterGroup,
+ sessionId
+ )
usageViewModel =
ViewModelProvider(this, usageViewModelFactory)[
PermissionUsageDetailsViewModelLegacy::class.java]
@@ -157,7 +164,11 @@ class AutoPermissionUsageDetailsFragment :
/** Reloads the data to show. */
private fun reloadData() {
usageViewModel.loadPermissionUsages(
- requireActivity().getLoaderManager(), permissionUsages, this, FILTER_24_HOURS)
+ requireActivity().getLoaderManager(),
+ permissionUsages,
+ this,
+ FILTER_24_HOURS
+ )
if (finishedInitialLoad) {
setLoading(true)
}
@@ -176,7 +187,8 @@ class AutoPermissionUsageDetailsFragment :
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()
@@ -206,19 +218,25 @@ class AutoPermissionUsageDetailsFragment :
val uiData =
usageViewModel.buildPermissionUsageDetailsUiData(
- appPermissionUsages, showSystem, SHOW_7_DAYS)
+ appPermissionUsages,
+ showSystem,
+ SHOW_7_DAYS
+ )
if (hasSystemApps != uiData.shouldDisplayShowSystemToggle) {
hasSystemApps = uiData.shouldDisplayShowSystemToggle
updateAction()
}
- val category = AtomicReference(PreferenceCategory(context))
+ val category = AtomicReference(PreferenceCategory(requireContext()))
preferenceScreen.addPreference(category.get())
AppDataLoader(context) {
renderHistoryPreferences(
- uiData.getHistoryPreferenceDataList(), category, preferenceScreen)
+ uiData.getHistoryPreferenceDataList(),
+ category,
+ preferenceScreen
+ )
setLoading(false)
finishedInitialLoad = true
@@ -239,7 +257,8 @@ class AutoPermissionUsageDetailsFragment :
summary =
getString(
R.string.permission_group_usage_subtitle_24h,
- getPermGroupLabel(requireContext(), filterGroup))
+ getPermGroupLabel(requireContext(), filterGroup)
+ )
isSelectable = false
}
preferenceScreen.addPreference(preference)
@@ -252,7 +271,8 @@ class AutoPermissionUsageDetailsFragment :
summary =
getString(
R.string.manage_permission_summary,
- getPermGroupLabel(requireContext(), filterGroup))
+ getPermGroupLabel(requireContext(), filterGroup)
+ )
onPreferenceClickListener =
Preference.OnPreferenceClickListener {
val intent =
@@ -279,12 +299,13 @@ class AutoPermissionUsageDetailsFragment :
val currentDateMs =
ZonedDateTime.ofInstant(
Instant.ofEpochMilli(usageTimestamp),
- Clock.system(ZoneId.systemDefault()).zone)
+ Clock.system(ZoneId.systemDefault()).zone
+ )
.truncatedTo(ChronoUnit.DAYS)
.toEpochSecond() * 1000L
if (currentDateMs != previousDateMs) {
if (previousDateMs != 0L) {
- category.set(PreferenceCategory(context))
+ category.set(PreferenceCategory(requireContext()))
preferenceScreen.addPreference(category.get())
}
if (usageTimestamp > MIDNIGHT_TODAY) {
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 d4a2a073e..c11129514 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
@@ -38,11 +38,11 @@ import com.android.permissioncontroller.permission.model.livedatatypes.PermGroup
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.model.ManagePermissionsViewModel
-import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageControlPreferenceUtils
import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelFactoryLegacy
import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelLegacy
import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelLegacy.PermissionGroupWithUsageCount
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageControlPreferenceUtils
import com.android.permissioncontroller.permission.utils.Utils
@RequiresApi(Build.VERSION_CODES.S)
@@ -95,7 +95,9 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag
PermissionUsageViewModelLegacy::class.java]
managePermissionsViewModel.standardPermGroupsLiveData.observe(
- this, this::onPermissionGroupsChanged)
+ this,
+ this::onPermissionGroupsChanged
+ )
setLoading(true)
reloadData()
}
@@ -119,7 +121,8 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag
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()
@@ -143,7 +146,10 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag
/** Reloads the data to show. */
private fun reloadData() {
usageViewModel.loadPermissionUsages(
- requireActivity().getLoaderManager(), permissionUsages, this)
+ requireActivity().getLoaderManager(),
+ permissionUsages,
+ this
+ )
if (finishedInitialLoad) {
setLoading(false)
}
@@ -165,7 +171,11 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag
val permissionUsagesUiData =
usageViewModel.buildPermissionUsagesUiData(
- appPermissionUsages, show7Days, showSystem, requireContext())
+ appPermissionUsages,
+ show7Days,
+ showSystem,
+ requireContext()
+ )
val permissionApps = permissionUsagesUiData.permissionApps
val displayShowSystemToggle = permissionUsagesUiData.displayShowSystemToggle
@@ -213,7 +223,8 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag
count,
showSystem,
sessionId,
- show7Days)
+ show7Days
+ )
getPreferenceScreen().addPreference(permissionUsagePreference)
}
finishedInitialLoad = true
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java
index cab0de15e..7fa51dd8a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java
@@ -103,6 +103,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
implements AppPermissionViewModel.ConfirmDialogShowingFragment {
private static final String LOG_TAG = "AppPermissionFragment";
private static final long POST_DELAY_MS = 20;
+ private static final long EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS = 200L;
static final String GRANT_CATEGORY = "grant_category";
@@ -117,6 +118,9 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
private @NonNull RadioButton mSelectPhotosButton;
private @NonNull RadioButton mDenyButton;
private @NonNull RadioButton mDenyForegroundButton;
+ private @NonNull ImageView mEditPhotosButton;
+ private @NonNull View mSelectPhotosLayout;
+ private @NonNull View mEditPhotosDivider;
private @NonNull View mLocationAccuracy;
private @NonNull Switch mLocationAccuracySwitch;
private @NonNull View mDivider;
@@ -128,6 +132,8 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
private @NonNull UserHandle mUser;
private boolean mIsStorageGroup;
private boolean mIsInitialLoad;
+ // This prevents the user from clicking the photo picker button multiple times in succession
+ private boolean mPhotoPickerTriggered;
private long mSessionId;
private @NonNull String mPackageLabel;
@@ -209,7 +215,6 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
if (mIsStorageGroup) {
mViewModel.getFullStorageStateLiveData().observe(this, this::setSpecialStorageState);
}
- mViewModel.registerPhotoPickerResultIfNeeded(this);
mRoleManager = Utils.getSystemServiceSafe(getContext(), RoleManager.class);
}
@@ -261,12 +266,15 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
mSelectPhotosButton = root.requireViewById(R.id.select_radio_button);
mDenyButton = root.requireViewById(R.id.deny_radio_button);
mDenyForegroundButton = root.requireViewById(R.id.deny_foreground_radio_button);
+
mDivider = root.requireViewById(R.id.two_target_divider);
mWidgetFrame = root.requireViewById(R.id.widget_frame);
mPermissionDetails = root.requireViewById(R.id.permission_details);
mLocationAccuracy = root.requireViewById(R.id.location_accuracy);
mLocationAccuracySwitch = root.requireViewById(R.id.location_accuracy_switch);
-
+ mSelectPhotosLayout = root.requireViewById(R.id.radio_select_layout);
+ mEditPhotosButton = root.requireViewById(R.id.edit_selected_button);
+ mEditPhotosDivider = root.requireViewById(R.id.edit_photos_divider);
mNestedScrollView = root.requireViewById(R.id.nested_scroll_view);
if (mViewModel.getButtonStateLiveData().getValue() != null) {
@@ -280,6 +288,9 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
mDenyButton.setVisibility(View.GONE);
mDenyForegroundButton.setVisibility(View.GONE);
mLocationAccuracy.setVisibility(View.GONE);
+ mSelectPhotosLayout.setVisibility(View.GONE);
+ mEditPhotosDivider.setAlpha(0f);
+ mEditPhotosButton.setAlpha(0f);
}
if (mViewModel.getFullStorageStateLiveData().isInitialized() && mIsStorageGroup) {
@@ -302,6 +313,12 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
return root;
}
+ public void onResume() {
+ super.onResume();
+ // If we're returning to the fragment, photo picker hasn't been triggered
+ mPhotoPickerTriggered = false;
+ }
+
private void showPermissionRationaleDialog(boolean showPermissionRationale) {
if (!showPermissionRationale) {
mAppPermissionRationaleContainer.setVisibility(View.GONE);
@@ -380,7 +397,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
});
mAllowAlwaysButton.setOnClickListener((v) -> {
if (mIsStorageGroup) {
- showConfirmDialog(ChangeRequest.GRANT_All_FILE_ACCESS,
+ showConfirmDialog(ChangeRequest.GRANT_ALL_FILE_ACCESS,
R.string.special_file_access_dialog, -1, false);
} else {
mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH,
@@ -412,6 +429,13 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
mViewModel.requestChange(false, this, this, ChangeRequest.PHOTOS_SELECTED,
buttonPressed);
});
+ mEditPhotosButton.setOnClickListener((v) -> {
+ ButtonState selectState = states.get(ButtonType.SELECT_PHOTOS);
+ if (selectState != null && selectState.isChecked() && !mPhotoPickerTriggered) {
+ mPhotoPickerTriggered = true;
+ mViewModel.openPhotoPicker(this);
+ }
+ });
mDenyButton.setOnClickListener((v) -> {
if (mViewModel.getFullStorageStateLiveData().getValue() != null
&& !mViewModel.getFullStorageStateLiveData().getValue().isLegacy()) {
@@ -485,6 +509,21 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
if (mIsInitialLoad) {
button.jumpDrawablesToCurrentState();
}
+
+ if (button == mSelectPhotosButton) {
+ mSelectPhotosLayout.setVisibility(visible);
+ float endOpacity = state.isChecked() ? 1f : 0f;
+ // On initial load, do not show the fade in/out animation
+ if (mIsInitialLoad) {
+ mEditPhotosDivider.setAlpha(endOpacity);
+ mEditPhotosButton.setAlpha(endOpacity);
+ return;
+ }
+ mEditPhotosButton.animate().alpha(endOpacity)
+ .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS);
+ mEditPhotosDivider.animate().alpha(endOpacity)
+ .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS);
+ }
}
private void setSpecialStorageState(FullStoragePackageState storageState, View v) {
@@ -630,7 +669,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
// NFF sharing
AppPermissionFragment fragment = (AppPermissionFragment) getParentFragment();
boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST)
- == ChangeRequest.GRANT_All_FILE_ACCESS;
+ == ChangeRequest.GRANT_ALL_FILE_ACCESS;
int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway;
if (isGrantFileAccess) {
positiveButtonStringResId = R.string.grant_dialog_button_allow;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
index a8b79bfde..5b24b52ec 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
@@ -23,6 +23,7 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_
import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED;
import static com.android.permissioncontroller.hibernation.HibernationPolicyKt.isHibernationEnabled;
+import static com.android.permissioncontroller.permission.ui.Category.STORAGE_FOOTER;
import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
import static java.util.concurrent.TimeUnit.DAYS;
@@ -318,6 +319,9 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
findPreference(Category.ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);
+ // Hide storage footer category
+ findPreference(STORAGE_FOOTER.getCategoryName()).setVisible(false);
+
long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
for (Category grantCategory : groupMap.keySet()) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt
index 7864abbb2..278243f09 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt
@@ -28,10 +28,10 @@ import com.android.permissioncontroller.R
* placement.
*/
class FooterPreference : Preference {
- constructor(c: Context): super(c)
- constructor(c: Context, a: AttributeSet): super(c, a)
- constructor(c: Context, a: AttributeSet, attr: Int): super(c, a, attr)
- constructor(c: Context, a: AttributeSet, attr: Int, res: Int): super(c, a, attr, res)
+ constructor(c: Context) : super(c)
+ constructor(c: Context, a: AttributeSet) : super(c, a)
+ constructor(c: Context, a: AttributeSet, attr: Int) : super(c, a, attr)
+ constructor(c: Context, a: AttributeSet, attr: Int, res: Int) : super(c, a, attr, res)
init {
layoutResource = R.layout.footer_preference
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt
index decbfe590..394c2d113 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt
@@ -83,10 +83,14 @@ class GrantPermissionsViewHandlerImpl(
private val resultListener: ResultListener
) : GrantPermissionsViewHandler, OnClickListener {
- private val LOCATION_ACCURACY_DIALOGS = listOf(DIALOG_WITH_BOTH_LOCATIONS,
- DIALOG_WITH_FINE_LOCATION_ONLY, DIALOG_WITH_COARSE_LOCATION_ONLY)
- private val LOCATION_ACCURACY_IMAGE_DIAMETER = mActivity.resources.getDimension(
- R.dimen.location_accuracy_image_size)
+ private val LOCATION_ACCURACY_DIALOGS =
+ listOf(
+ DIALOG_WITH_BOTH_LOCATIONS,
+ DIALOG_WITH_FINE_LOCATION_ONLY,
+ DIALOG_WITH_COARSE_LOCATION_ONLY
+ )
+ private val LOCATION_ACCURACY_IMAGE_DIAMETER =
+ mActivity.resources.getDimension(R.dimen.location_accuracy_image_size)
// Configuration of the current dialog
private var groupName: String? = null
@@ -124,8 +128,10 @@ class GrantPermissionsViewHandlerImpl(
arguments.putParcelable(ARG_GROUP_ICON, groupIcon)
arguments.putCharSequence(ARG_GROUP_MESSAGE, groupMessage)
arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, detailMessage)
- arguments.putCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE,
- permissionRationaleMessage)
+ arguments.putCharSequence(
+ ARG_GROUP_PERMISSION_RATIONALE_MESSAGE,
+ permissionRationaleMessage
+ )
arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, buttonVisibilities)
arguments.putBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES, locationVisibilities)
arguments.putInt(ARG_DIALOG_SELECTED_PRECISION, selectedPrecision)
@@ -141,8 +147,9 @@ class GrantPermissionsViewHandlerImpl(
permissionRationaleMessage =
savedInstanceState.getCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE)
setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES))
- setLocationVisibilities(savedInstanceState.getBooleanArray(
- ARG_DIALOG_LOCATION_VISIBILITIES))
+ setLocationVisibilities(
+ savedInstanceState.getBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES)
+ )
selectedPrecision = savedInstanceState.getInt(ARG_DIALOG_SELECTED_PRECISION)
updateAll()
@@ -187,21 +194,22 @@ class GrantPermissionsViewHandlerImpl(
// Grow or shrink the content container to size of new content
val growShrinkToNewContentSize = ChangeBounds()
growShrinkToNewContentSize.duration = ANIMATION_DURATION_MILLIS
- growShrinkToNewContentSize.interpolator = AnimationUtils.loadInterpolator(mActivity,
- android.R.interpolator.fast_out_slow_in)
+ growShrinkToNewContentSize.interpolator =
+ AnimationUtils.loadInterpolator(mActivity, android.R.interpolator.fast_out_slow_in)
TransitionManager.beginDelayedTransition(rootView, growShrinkToNewContentSize)
}
override fun createView(): View {
- val useMaterial3PermissionGrantDialog = mActivity.resources
- .getBoolean(R.bool.config_useMaterial3PermissionGrantDialog)
- val rootView = if (useMaterial3PermissionGrantDialog || SdkLevel.isAtLeastT()) {
- LayoutInflater.from(mActivity)
- .inflate(R.layout.grant_permissions_material3, null) as ViewGroup
- } else {
- LayoutInflater.from(mActivity)
- .inflate(R.layout.grant_permissions, null) as ViewGroup
- }
+ val useMaterial3PermissionGrantDialog =
+ mActivity.resources.getBoolean(R.bool.config_useMaterial3PermissionGrantDialog)
+ val rootView =
+ if (useMaterial3PermissionGrantDialog || SdkLevel.isAtLeastT()) {
+ LayoutInflater.from(mActivity).inflate(R.layout.grant_permissions_material3, null)
+ as ViewGroup
+ } else {
+ LayoutInflater.from(mActivity).inflate(R.layout.grant_permissions, null)
+ as ViewGroup
+ }
this.rootView = rootView
// Uses the vertical gravity of the PermissionGrantSingleton style to position the window
@@ -257,14 +265,15 @@ class GrantPermissionsViewHandlerImpl(
private fun getLottieDrawable(@RawRes rawResId: Int): LottieDrawable {
val composition = LottieCompositionFactory.fromRawResSync(mActivity, rawResId).value!!
val scale = LOCATION_ACCURACY_IMAGE_DIAMETER / composition.bounds.width()
- val drawable = object : LottieDrawable() {
- override fun getIntrinsicHeight(): Int {
- return (super.getIntrinsicHeight() * scale).toInt()
- }
- override fun getIntrinsicWidth(): Int {
- return (super.getIntrinsicWidth() * scale).toInt()
+ val drawable =
+ object : LottieDrawable() {
+ override fun getIntrinsicHeight(): Int {
+ return (super.getIntrinsicHeight() * scale).toInt()
+ }
+ override fun getIntrinsicWidth(): Int {
+ return (super.getIntrinsicWidth() * scale).toInt()
+ }
}
- }
drawable.composition = composition
return drawable
}
@@ -282,21 +291,23 @@ class GrantPermissionsViewHandlerImpl(
private fun setButtonVisibilities(visibilities: BooleanArray?) {
for (i in buttonVisibilities.indices) {
- buttonVisibilities[i] = if (visibilities != null && i < visibilities.size) {
- visibilities[i]
- } else {
- false
- }
+ buttonVisibilities[i] =
+ if (visibilities != null && i < visibilities.size) {
+ visibilities[i]
+ } else {
+ false
+ }
}
}
private fun setLocationVisibilities(visibilities: BooleanArray?) {
for (i in locationVisibilities.indices) {
- locationVisibilities[i] = if (visibilities != null && i < visibilities.size) {
- visibilities[i]
- } else {
- false
- }
+ locationVisibilities[i] =
+ if (visibilities != null && i < visibilities.size) {
+ visibilities[i]
+ } else {
+ false
+ }
}
}
@@ -329,29 +340,38 @@ class GrantPermissionsViewHandlerImpl(
private fun updateButtons() {
for (i in 0 until BUTTON_RES_ID_TO_NUM.size()) {
val pos = BUTTON_RES_ID_TO_NUM.valueAt(i)
- buttons[pos]?.visibility = if (buttonVisibilities[pos]) {
- View.VISIBLE
- } else {
- View.GONE
- }
+ buttons[pos]?.visibility =
+ if (buttonVisibilities[pos]) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
if (pos == ALLOW_FOREGROUND_BUTTON && buttonVisibilities[pos]) {
- if (locationVisibilities[LOCATION_ACCURACY_LAYOUT] &&
- locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) {
- buttons[pos]?.text = mActivity.resources.getString(
- R.string.grant_dialog_button_change_to_precise_location)
+ if (
+ locationVisibilities[LOCATION_ACCURACY_LAYOUT] &&
+ locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]
+ ) {
+ buttons[pos]?.text =
+ mActivity.resources.getString(
+ R.string.grant_dialog_button_change_to_precise_location
+ )
} else {
- buttons[pos]?.text = mActivity.resources.getString(
- R.string.grant_dialog_button_allow_foreground)
+ buttons[pos]?.text =
+ mActivity.resources.getString(R.string.grant_dialog_button_allow_foreground)
}
}
if ((pos == DENY_BUTTON || pos == DENY_AND_DONT_ASK_AGAIN_BUTTON)) {
- if (locationVisibilities[LOCATION_ACCURACY_LAYOUT] &&
- locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) {
- buttons[pos]?.text = mActivity.resources.getString(
- R.string.grant_dialog_button_keey_approximate_location)
+ if (
+ locationVisibilities[LOCATION_ACCURACY_LAYOUT] &&
+ locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]
+ ) {
+ buttons[pos]?.text =
+ mActivity.resources.getString(
+ R.string.grant_dialog_button_keey_approximate_location
+ )
} else {
- buttons[pos]?.text = mActivity.resources.getString(
- R.string.grant_dialog_button_deny)
+ buttons[pos]?.text =
+ mActivity.resources.getString(R.string.grant_dialog_button_deny)
}
}
buttons[pos]?.requestLayout()
@@ -365,11 +385,12 @@ class GrantPermissionsViewHandlerImpl(
}
locationViews[LOCATION_ACCURACY_LAYOUT]?.visibility = View.VISIBLE
for (i in LOCATION_ACCURACY_DIALOGS) {
- locationViews[i]?.visibility = if (locationVisibilities[i]) {
- View.VISIBLE
- } else {
- View.GONE
- }
+ locationViews[i]?.visibility =
+ if (locationVisibilities[i]) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
}
if (locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) {
coarseRadioButton?.visibility = View.VISIBLE
@@ -388,11 +409,13 @@ class GrantPermissionsViewHandlerImpl(
}
} else if (locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) {
(locationViews[DIALOG_WITH_COARSE_LOCATION_ONLY] as ImageView).setImageDrawable(
- coarseOnDrawable)
+ coarseOnDrawable
+ )
coarseOnDrawable?.start()
} else if (locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) {
(locationViews[DIALOG_WITH_FINE_LOCATION_ONLY] as ImageView).setImageDrawable(
- fineOnDrawable)
+ fineOnDrawable
+ )
fineOnDrawable?.start()
}
} else {
@@ -408,10 +431,18 @@ class GrantPermissionsViewHandlerImpl(
if (isFineSelected) {
coarseOnDrawable?.stop()
fineOffDrawable?.stop()
- coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, coarseOffDrawable,
- null, null)
- fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, fineOnDrawable,
- null, null)
+ coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds(
+ null,
+ coarseOffDrawable,
+ null,
+ null
+ )
+ fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds(
+ null,
+ fineOnDrawable,
+ null,
+ null
+ )
coarseOffDrawable?.start()
fineOnDrawable?.start()
fineRadioButton?.setTypeface(null, Typeface.BOLD)
@@ -419,10 +450,18 @@ class GrantPermissionsViewHandlerImpl(
} else {
coarseOffDrawable?.stop()
fineOnDrawable?.stop()
- coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, coarseOnDrawable,
- null, null)
- fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, fineOffDrawable,
- null, null)
+ coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds(
+ null,
+ coarseOnDrawable,
+ null,
+ null
+ )
+ fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds(
+ null,
+ fineOffDrawable,
+ null,
+ null
+ )
fineOffDrawable?.start()
coarseOnDrawable?.start()
coarseRadioButton?.setTypeface(null, Typeface.BOLD)
@@ -471,8 +510,8 @@ class GrantPermissionsViewHandlerImpl(
R.id.permission_location_accuracy_radio_coarse ->
affectedForegroundPermissions = listOf(ACCESS_COARSE_LOCATION)
R.id.permission_location_accuracy_radio_fine ->
- affectedForegroundPermissions = listOf(ACCESS_FINE_LOCATION,
- ACCESS_COARSE_LOCATION)
+ affectedForegroundPermissions =
+ listOf(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION)
}
} else if (locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) {
affectedForegroundPermissions = listOf(ACCESS_FINE_LOCATION)
@@ -481,52 +520,94 @@ class GrantPermissionsViewHandlerImpl(
}
when (BUTTON_RES_ID_TO_NUM.get(id, -1)) {
- ALLOW_ALL_BUTTON, ALLOW_BUTTON -> {
+ ALLOW_ALL_BUTTON,
+ ALLOW_BUTTON -> {
view.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
- resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
- GRANTED_ALWAYS)
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+ null
+ )
+ resultListener.onPermissionGrantResult(
+ groupName,
+ affectedForegroundPermissions,
+ GRANTED_ALWAYS
+ )
}
ALLOW_FOREGROUND_BUTTON -> {
view.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
- resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
- GRANTED_FOREGROUND_ONLY)
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+ null
+ )
+ resultListener.onPermissionGrantResult(
+ groupName,
+ affectedForegroundPermissions,
+ GRANTED_FOREGROUND_ONLY
+ )
}
ALLOW_ALWAYS_BUTTON -> {
view.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
- resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
- GRANTED_ALWAYS)
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+ null
+ )
+ resultListener.onPermissionGrantResult(
+ groupName,
+ affectedForegroundPermissions,
+ GRANTED_ALWAYS
+ )
}
ALLOW_ONE_TIME_BUTTON -> {
view.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
- resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
- GRANTED_ONE_TIME)
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+ null
+ )
+ resultListener.onPermissionGrantResult(
+ groupName,
+ affectedForegroundPermissions,
+ GRANTED_ONE_TIME
+ )
}
ALLOW_SELECTED_BUTTON -> {
view.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
- resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
- GRANTED_USER_SELECTED)
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+ null
+ )
+ resultListener.onPermissionGrantResult(
+ groupName,
+ affectedForegroundPermissions,
+ GRANTED_USER_SELECTED
+ )
}
DONT_ALLOW_MORE_SELECTED_BUTTON -> {
- resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
- DENIED_MORE)
+ resultListener.onPermissionGrantResult(
+ groupName,
+ affectedForegroundPermissions,
+ DENIED_MORE
+ )
}
- DENY_BUTTON, NO_UPGRADE_BUTTON, NO_UPGRADE_OT_BUTTON -> {
+ DENY_BUTTON,
+ NO_UPGRADE_BUTTON,
+ NO_UPGRADE_OT_BUTTON -> {
view.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
- resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
- DENIED)
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+ null
+ )
+ resultListener.onPermissionGrantResult(
+ groupName,
+ affectedForegroundPermissions,
+ DENIED
+ )
}
- DENY_AND_DONT_ASK_AGAIN_BUTTON, NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON,
+ DENY_AND_DONT_ASK_AGAIN_BUTTON,
+ NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON,
NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON -> {
view.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
- resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
- DENIED_DO_NOT_ASK_AGAIN)
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+ null
+ )
+ resultListener.onPermissionGrantResult(
+ groupName,
+ affectedForegroundPermissions,
+ DENIED_DO_NOT_ASK_AGAIN
+ )
}
}
}
@@ -567,39 +648,57 @@ class GrantPermissionsViewHandlerImpl(
init {
BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_button, ALLOW_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_foreground_only_button,
- ALLOW_FOREGROUND_BUTTON)
+ BUTTON_RES_ID_TO_NUM.put(
+ R.id.permission_allow_foreground_only_button,
+ ALLOW_FOREGROUND_BUTTON
+ )
BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_button, DENY_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_and_dont_ask_again_button,
- DENY_AND_DONT_ASK_AGAIN_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_one_time_button,
- ALLOW_ONE_TIME_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_button,
- NO_UPGRADE_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_and_dont_ask_again_button,
- NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_button,
- NO_UPGRADE_OT_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_and_dont_ask_again_button,
- NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_all_button,
- ALLOW_ALL_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_selected_button,
- ALLOW_SELECTED_BUTTON)
- BUTTON_RES_ID_TO_NUM.put(R.id.permission_dont_allow_more_selected_button,
- DONT_ALLOW_MORE_SELECTED_BUTTON)
+ BUTTON_RES_ID_TO_NUM.put(
+ R.id.permission_deny_and_dont_ask_again_button,
+ DENY_AND_DONT_ASK_AGAIN_BUTTON
+ )
+ BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_one_time_button, ALLOW_ONE_TIME_BUTTON)
+ BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_button, NO_UPGRADE_BUTTON)
+ BUTTON_RES_ID_TO_NUM.put(
+ R.id.permission_no_upgrade_and_dont_ask_again_button,
+ NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON
+ )
+ BUTTON_RES_ID_TO_NUM.put(
+ R.id.permission_no_upgrade_one_time_button,
+ NO_UPGRADE_OT_BUTTON
+ )
+ BUTTON_RES_ID_TO_NUM.put(
+ R.id.permission_no_upgrade_one_time_and_dont_ask_again_button,
+ NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON
+ )
+ BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_all_button, ALLOW_ALL_BUTTON)
+ BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_selected_button, ALLOW_SELECTED_BUTTON)
+ BUTTON_RES_ID_TO_NUM.put(
+ R.id.permission_dont_allow_more_selected_button,
+ DONT_ALLOW_MORE_SELECTED_BUTTON
+ )
LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy, LOCATION_ACCURACY_LAYOUT)
- LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_fine,
- FINE_RADIO_BUTTON)
- LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_coarse,
- COARSE_RADIO_BUTTON)
- LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_group,
- DIALOG_WITH_BOTH_LOCATIONS)
- LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_fine_only,
- DIALOG_WITH_FINE_LOCATION_ONLY)
- LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_coarse_only,
- DIALOG_WITH_COARSE_LOCATION_ONLY)
+ LOCATION_RES_ID_TO_NUM.put(
+ R.id.permission_location_accuracy_radio_fine,
+ FINE_RADIO_BUTTON
+ )
+ LOCATION_RES_ID_TO_NUM.put(
+ R.id.permission_location_accuracy_radio_coarse,
+ COARSE_RADIO_BUTTON
+ )
+ LOCATION_RES_ID_TO_NUM.put(
+ R.id.permission_location_accuracy_radio_group,
+ DIALOG_WITH_BOTH_LOCATIONS
+ )
+ LOCATION_RES_ID_TO_NUM.put(
+ R.id.permission_location_accuracy_fine_only,
+ DIALOG_WITH_FINE_LOCATION_ONLY
+ )
+ LOCATION_RES_ID_TO_NUM.put(
+ R.id.permission_location_accuracy_coarse_only,
+ DIALOG_WITH_COARSE_LOCATION_ONLY
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
index e2fdfc86e..37ac50bb8 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
@@ -28,14 +28,12 @@ import com.android.permissioncontroller.hibernation.isHibernationEnabled
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY
-/**
- * Handheld wrapper, with customizations, around [UnusedAppsFragment].
- */
-class HandheldUnusedAppsFragment : PermissionsFrameFragment(),
- UnusedAppsFragment.Parent<UnusedAppPreference> {
+/** Handheld wrapper, with customizations, around [UnusedAppsFragment]. */
+class HandheldUnusedAppsFragment :
+ PermissionsFrameFragment(), UnusedAppsFragment.Parent<UnusedAppPreference> {
companion object {
- /** Create a new instance of this fragment. */
+ /** Create a new instance of this fragment. */
@JvmStatic
fun newInstance(): HandheldUnusedAppsFragment {
return HandheldUnusedAppsFragment()
@@ -55,15 +53,12 @@ class HandheldUnusedAppsFragment : PermissionsFrameFragment(),
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null) {
- val fragment:
- UnusedAppsFragment<HandheldUnusedAppsFragment, UnusedAppPreference> =
+ val fragment: UnusedAppsFragment<HandheldUnusedAppsFragment, UnusedAppPreference> =
UnusedAppsFragment.newInstance()
fragment.arguments = arguments
// child fragment does not have its own UI - it will add to the preferences of this
// parent fragment
- childFragmentManager.beginTransaction()
- .add(fragment, null)
- .commit()
+ childFragmentManager.beginTransaction().add(fragment, null).commit()
}
}
@@ -113,7 +108,7 @@ class HandheldUnusedAppsFragment : PermissionsFrameFragment(),
override fun setEmptyState(empty: Boolean) {
val infoMsgCategory =
- preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
+ preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
infoMsgCategory.isVisible = !empty
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsWrapperFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsWrapperFragment.kt
index 44a9f3d08..7565e6d17 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsWrapperFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsWrapperFragment.kt
@@ -24,12 +24,10 @@ import android.view.ViewGroup
import com.android.permissioncontroller.R
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseFragment
-/**
- * Wrapper over HandheldUnusedAppsFragment
- */
+/** Wrapper over HandheldUnusedAppsFragment */
class HandheldUnusedAppsWrapperFragment : CollapsingToolbarBaseFragment() {
companion object {
- /** Create a new instance of this fragment. */
+ /** Create a new instance of this fragment. */
@JvmStatic
fun newInstance(): HandheldUnusedAppsWrapperFragment {
return HandheldUnusedAppsWrapperFragment()
@@ -48,15 +46,16 @@ class HandheldUnusedAppsWrapperFragment : CollapsingToolbarBaseFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
- var preferenceFragment = childFragmentManager
- .findFragmentById(R.id.preference_fragment_container)
+ var preferenceFragment =
+ childFragmentManager.findFragmentById(R.id.preference_fragment_container)
as HandheldUnusedAppsFragment?
if (preferenceFragment == null) {
preferenceFragment = HandheldUnusedAppsFragment.newInstance()
preferenceFragment.arguments = arguments
- childFragmentManager.beginTransaction()
- .add(R.id.preference_fragment_container, preferenceFragment)
- .commit()
+ childFragmentManager
+ .beginTransaction()
+ .add(R.id.preference_fragment_container, preferenceFragment)
+ .commit()
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
index 220507426..ba839ee0e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
@@ -21,6 +21,7 @@ import static com.android.permissioncontroller.permission.ui.Category.ALLOWED;
import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND;
import static com.android.permissioncontroller.permission.ui.Category.ASK;
import static com.android.permissioncontroller.permission.ui.Category.DENIED;
+import static com.android.permissioncontroller.permission.ui.Category.STORAGE_FOOTER;
import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
import android.Manifest;
@@ -34,6 +35,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
+import android.safetycenter.SafetyCenterManager;
import android.util.ArrayMap;
import android.view.Menu;
import android.view.MenuInflater;
@@ -62,15 +64,15 @@ import com.android.settingslib.HelpUtils;
import com.android.settingslib.utils.applications.AppUtils;
import com.android.settingslib.widget.FooterPreference;
+import kotlin.Pair;
+import kotlin.Triple;
+
import java.text.Collator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
-import kotlin.Pair;
-import kotlin.Triple;
-
/**
* Show and manage apps which request a single permission group.
*
@@ -87,12 +89,13 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
private static final String STORAGE_ALLOWED_FULL = "allowed_storage_full";
private static final String STORAGE_ALLOWED_SCOPED = "allowed_storage_scoped";
private static final String BLOCKED_SENSOR_PREF_KEY = "sensor_card";
- private static final String STORAGE_FOOTER_CATEGORY_KEY = "storage_footer_category";
private static final String STORAGE_FOOTER_PREFERENCE_KEY = "storage_footer_preference";
private static final int SHOW_LOAD_DELAY_MS = 200;
private static final int MENU_PERMISSION_USAGE = MENU_HIDE_SYSTEM + 1;
+ private static final String PRIVACY_CONTROLS_ACTION = "android.settings.PRIVACY_CONTROLS";
+
/**
* Create a bundle with the arguments needed by this fragment
*
@@ -281,12 +284,30 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
findPreference(ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);
}
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private String getPrivacyControlsIntent() {
+ SafetyCenterManager safetyCenterManager =
+ getContext().getSystemService(SafetyCenterManager.class);
+ if (safetyCenterManager.isSafetyCenterEnabled()) {
+ return PRIVACY_CONTROLS_ACTION;
+ } else {
+ return Settings.ACTION_PRIVACY_SETTINGS;
+ }
+ }
+
@RequiresApi(Build.VERSION_CODES.S)
private CardViewPreference createSensorCard() {
boolean isLocation = Manifest.permission_group.LOCATION.equals(mPermGroupName);
Context context = getPreferenceManager().getContext();
- String action = isLocation ? Settings.ACTION_LOCATION_SOURCE_SETTINGS
- : Settings.ACTION_PRIVACY_SETTINGS;
+
+ String action;
+ if (isLocation) {
+ action = Settings.ACTION_LOCATION_SOURCE_SETTINGS;
+ } else if (SdkLevel.isAtLeastT()) {
+ action = getPrivacyControlsIntent();
+ } else {
+ action = Settings.ACTION_PRIVACY_SETTINGS;
+ }
CardViewPreference sensorCard = new CardViewPreference(context, action);
sensorCard.setKey(BLOCKED_SENSOR_PREF_KEY);
sensorCard.setIcon(Utils.getBlockedIcon(mPermGroupName));
@@ -303,7 +324,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
private void addStorageFooterSeeAllFilesAccess() {
PreferenceScreen screen = getPreferenceScreen();
Context context = screen.getPreferenceManager().getContext();
- PreferenceCategory preferenceCategory = findPreference(STORAGE_FOOTER_CATEGORY_KEY);
+ PreferenceCategory preferenceCategory = findPreference(STORAGE_FOOTER.getCategoryName());
Preference existingPreference = findPreference(STORAGE_FOOTER_PREFERENCE_KEY);
if (preferenceCategory == null || existingPreference != null) {
@@ -502,6 +523,13 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
if (SdkLevel.isAtLeastT() && Manifest.permission_group.STORAGE.equals(mPermGroupName)) {
addStorageFooterSeeAllFilesAccess();
+ } else {
+ // Hide storage footer category
+ PreferenceCategory storageFooterPreferenceCategory =
+ findPreference(STORAGE_FOOTER.getCategoryName());
+ if (storageFooterPreferenceCategory != null) {
+ storageFooterPreferenceCategory.setVisible(false);
+ }
}
mViewModel.setCreationLogged(true);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt
index 27cbd8c15..f7178bdbb 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt
@@ -30,16 +30,17 @@ import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.utils.KotlinUtils
/**
- * A Preference representing a package for a user, which loads and displays its icon only upon
- * being bound to a viewHolder. This lets us synchronously load package icons and labels, while
- * still displaying the PermissionAppsFragment instantly.
+ * A Preference representing a package for a user, which loads and displays its icon only upon being
+ * bound to a viewHolder. This lets us synchronously load package icons and labels, while still
+ * displaying the PermissionAppsFragment instantly.
*
* @param app The current application
* @param packageName The name of the package whose icon this preference will retrieve
* @param user The user whose package icon will be retrieved
* @param context The current context
*/
-open class SmartIconLoadPackagePermissionPreference constructor(
+open class SmartIconLoadPackagePermissionPreference
+constructor(
private val app: Application,
private val packageName: String,
private val user: UserHandle,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppPreference.kt
index dfab55ed7..02eb6c090 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppPreference.kt
@@ -50,9 +50,7 @@ class UnusedAppPreference(
super.onBindViewHolder(holder)
val removeButton = holder.findViewById(R.id.uninstall_button) as ImageButton
- removeButton.setOnClickListener {
- removeRunnable?.run()
- }
+ removeButton.setOnClickListener { removeRunnable?.run() }
removeButton.isEnabled = removeButtonEnabled
}
@@ -63,4 +61,4 @@ class UnusedAppPreference(
override fun setRemoveComponentEnabled(enabled: Boolean) {
removeButtonEnabled = enabled
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/Utils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/Utils.kt
index e2acb498c..f6a387e9d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/Utils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/Utils.kt
@@ -19,12 +19,10 @@ package com.android.permissioncontroller.permission.ui.handheld
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
-/**
- * Press back and close the activity if this is the last fragment.
- */
+/** Press back and close the activity if this is the last fragment. */
fun Fragment.pressBack() {
val wasBackExecuted = findNavController().popBackStack()
if (!wasBackExecuted) {
activity?.let { it.finishAfterTransition() }
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt
index 5b92dd36d..b36d5174c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt
@@ -49,7 +49,6 @@ fun shouldShowSubattributionInPermissionsDashboard(): Boolean {
*
* @param context the context.
* @param lastAccessTime the time in milliseconds.
- *
* @return a string representing the time or date of the given time or null if the time is 0.
*/
fun getAbsoluteTimeString(context: Context, lastAccessTime: Long): String? {
@@ -64,14 +63,13 @@ fun getAbsoluteTimeString(context: Context, lastAccessTime: Long): String? {
}
/**
- * Build a string representing the time of the most recent permission usage if it happened on
- * the current day and the date otherwise.
+ * Build a string representing the time of the most recent permission usage if it happened on the
+ * current day and the date otherwise.
*
* @param context the context.
* @param groupUsage the permission usage.
- *
- * @return a string representing the time or date of the most recent usage or null if there are
- * no usages.
+ * @return a string representing the time or date of the most recent usage or null if there are no
+ * usages.
*/
@RequiresApi(Build.VERSION_CODES.S)
fun getAbsoluteLastUsageString(context: Context, groupUsage: GroupUsage?): String? {
@@ -83,8 +81,7 @@ fun getAbsoluteLastUsageString(context: Context, groupUsage: GroupUsage?): Strin
/**
* Build a string representing the duration of a permission usage.
*
- * @return a string representing the duration of this app's usage or null if there are no
- * usages.
+ * @return a string representing the duration of this app's usage or null if there are no usages.
*/
@RequiresApi(Build.VERSION_CODES.S)
fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? {
@@ -94,9 +91,9 @@ fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? {
}
/**
- * Build a string representing the number of milliseconds passed in. It rounds to the nearest
- * unit. For example, given a duration of 3500 and an English locale, this can return
- * "3 seconds".
+ * Build a string representing the number of milliseconds passed in. It rounds to the nearest unit.
+ * For example, given a duration of 3500 and an English locale, this can return "3 seconds".
+ *
* @param context The context.
* @param duration The number of milliseconds.
* @return a string representing the given number of milliseconds.
@@ -104,37 +101,63 @@ fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? {
fun getTimeDiffStr(context: Context, duration: Long): String {
val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
return when (timeDiffAndUnit.second) {
- SECONDS -> StringUtils.getIcuPluralsString(context,
- R.string.seconds, timeDiffAndUnit.first.toInt())
- MINUTES -> StringUtils.getIcuPluralsString(context,
- R.string.minutes, timeDiffAndUnit.first.toInt())
- HOURS -> StringUtils.getIcuPluralsString(context,
- R.string.hours, timeDiffAndUnit.first.toInt())
- else -> StringUtils.getIcuPluralsString(context,
- R.string.days, timeDiffAndUnit.first.toInt())
+ SECONDS ->
+ StringUtils.getIcuPluralsString(
+ context,
+ R.string.seconds,
+ timeDiffAndUnit.first.toInt()
+ )
+ MINUTES ->
+ StringUtils.getIcuPluralsString(
+ context,
+ R.string.minutes,
+ timeDiffAndUnit.first.toInt()
+ )
+ HOURS ->
+ StringUtils.getIcuPluralsString(context, R.string.hours, timeDiffAndUnit.first.toInt())
+ else ->
+ StringUtils.getIcuPluralsString(context, R.string.days, timeDiffAndUnit.first.toInt())
}
}
/**
* Build a string representing the duration used of milliseconds passed in.
+ *
* @return a string representing the duration used in the nearest unit. ex: Used for 3 mins
*/
fun getDurationUsedStr(context: Context, duration: Long): String {
val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
return when (timeDiffAndUnit.second) {
- SECONDS -> StringUtils.getIcuPluralsString(context,
- R.string.duration_used_seconds, timeDiffAndUnit.first.toInt())
- MINUTES -> StringUtils.getIcuPluralsString(context,
- R.string.duration_used_minutes, timeDiffAndUnit.first.toInt())
- HOURS -> StringUtils.getIcuPluralsString(context,
- R.string.duration_used_hours, timeDiffAndUnit.first.toInt())
- else -> StringUtils.getIcuPluralsString(context,
- R.string.duration_used_days, timeDiffAndUnit.first.toInt())
+ SECONDS ->
+ StringUtils.getIcuPluralsString(
+ context,
+ R.string.duration_used_seconds,
+ timeDiffAndUnit.first.toInt()
+ )
+ MINUTES ->
+ StringUtils.getIcuPluralsString(
+ context,
+ R.string.duration_used_minutes,
+ timeDiffAndUnit.first.toInt()
+ )
+ HOURS ->
+ StringUtils.getIcuPluralsString(
+ context,
+ R.string.duration_used_hours,
+ timeDiffAndUnit.first.toInt()
+ )
+ else ->
+ StringUtils.getIcuPluralsString(
+ context,
+ R.string.duration_used_days,
+ timeDiffAndUnit.first.toInt()
+ )
}
}
/**
* Given the duration in milliseconds, calculate the time of that duration in the nearest unit.
+ *
* @return a Pair of the <duration in the nearest unit, the nearest unit>
*/
fun calculateTimeDiffAndUnit(duration: Long): Pair<Long, Int> {
@@ -159,7 +182,6 @@ fun calculateTimeDiffAndUnit(duration: Long): Pair<Long, Int> {
* Check whether the given time (in milliseconds) is in the current day.
*
* @param time the time in milliseconds
- *
* @return whether the given time is in the current day.
*/
private fun isToday(time: Long): Boolean {
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 bcca75fcc..5c20ef9df 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
@@ -77,7 +77,7 @@ public class PermissionHistoryPreference extends Preference {
public PermissionHistoryPreference(@NonNull Context context,
@NonNull UserHandle userHandle, @NonNull String pkgName,
- @NonNull Drawable appIcon,
+ @Nullable Drawable appIcon,
@NonNull String preferenceTitle,
@NonNull String permissionGroup,
@NonNull long accessStartTime,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java
index 15ee31a54..5d4343639 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java
@@ -368,15 +368,9 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader {
new PermissionHistoryPreference(
getContext(),
appPermissionAccessUiInfo.getUserHandle(),
- appPermissionAccessUiInfo.getPkgName(),
- KotlinUtils.INSTANCE.getBadgedPackageIcon(
- mViewModel.getApplication(),
- appPermissionAccessUiInfo.getPkgName(),
- appPermissionAccessUiInfo.getUserHandle()),
- KotlinUtils.INSTANCE.getPackageLabel(
- mViewModel.getApplication(),
- appPermissionAccessUiInfo.getPkgName(),
- appPermissionAccessUiInfo.getUserHandle()),
+ appPermissionAccessUiInfo.getPackageName(),
+ appPermissionAccessUiInfo.getBadgedPackageIcon(),
+ appPermissionAccessUiInfo.getPackageLabel(),
appPermissionAccessUiInfo.getPermissionGroup(),
appPermissionAccessUiInfo.getAccessStartTime(),
appPermissionAccessUiInfo.getAccessEndTime(),
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingDetailsPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingDetailsPreference.kt
index 014627c3c..70d0632f9 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingDetailsPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingDetailsPreference.kt
@@ -45,7 +45,7 @@ class AppDataSharingDetailsPreference : Preference {
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
- val noUpdatesMessage = holder.findViewById(R.id.no_updates_message)
+ val noUpdatesMessage = holder.findViewById(R.id.no_updates_message)!!
noUpdatesMessage.isVisible = showNoUpdates
super.onBindViewHolder(holder)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt
index 88b5ebe87..e74022291 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt
@@ -76,7 +76,8 @@ class AppDataSharingUpdatesFooterPreference : Preference {
},
0,
footerLink.length,
- 0)
+ 0
+ )
footerLinkView?.let {
it.visibility = if (onFooterLinkClick == null) View.GONE else View.VISIBLE
it.text = footerLinkText
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt
index 2db9bc4b4..b58de90a5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt
@@ -98,7 +98,8 @@ class AppDataSharingUpdatesFragment : PermissionsFrameFragment() {
val updatesCategory =
preferenceScreen.findPreference<PreferenceCategory>(
LAST_PERIOD_UPDATES_PREFERENCE_CATEGORY_ID
- ) ?: return
+ )
+ ?: return
val preferencesToRemove = mutableSetOf<Preference>()
for (i in 0 until (updatesCategory.preferenceCount)) {
@@ -192,11 +193,12 @@ class AppDataSharingUpdatesFragment : PermissionsFrameFragment() {
it.isVisible = true
}
- val onFooterLinkClick = if (viewModel.canLinkToHelpCenter(requireActivity())) {
- View.OnClickListener { viewModel.openSafetyLabelsHelpCenterPage(requireActivity()) }
- } else {
- null
- }
+ val onFooterLinkClick =
+ if (viewModel.canLinkToHelpCenter(requireActivity())) {
+ View.OnClickListener { viewModel.openSafetyLabelsHelpCenterPage(requireActivity()) }
+ } else {
+ null
+ }
footerPreference?.let {
it.footerMessage = getString(R.string.data_sharing_updates_footer_message)
it.footerLink = getString(R.string.learn_about_data_sharing)
@@ -231,11 +233,14 @@ class AppDataSharingUpdatesFragment : PermissionsFrameFragment() {
footerPreference?.let {
it.footerMessage = getString(R.string.data_sharing_updates_footer_message)
it.footerLink = getString(R.string.learn_about_data_sharing)
- it.onFooterLinkClick = if (viewModel.canLinkToHelpCenter(requireActivity())) {
- View.OnClickListener { viewModel.openSafetyLabelsHelpCenterPage(requireActivity()) }
- } else {
- null
- }
+ it.onFooterLinkClick =
+ if (viewModel.canLinkToHelpCenter(requireActivity())) {
+ View.OnClickListener {
+ viewModel.openSafetyLabelsHelpCenterPage(requireActivity())
+ }
+ } else {
+ null
+ }
it.isVisible = true
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt
index 3998ca141..93e6b6336 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt
@@ -122,14 +122,14 @@ class PermissionRationaleViewHandlerImpl(
// Grow or shrink the content container to size of new content
val growShrinkToNewContentSize = ChangeBounds()
growShrinkToNewContentSize.duration = ANIMATION_DURATION_MILLIS
- growShrinkToNewContentSize.interpolator = AnimationUtils.loadInterpolator(mActivity,
- android.R.interpolator.fast_out_slow_in)
+ growShrinkToNewContentSize.interpolator =
+ AnimationUtils.loadInterpolator(mActivity, android.R.interpolator.fast_out_slow_in)
TransitionManager.beginDelayedTransition(rootView, growShrinkToNewContentSize)
}
override fun createView(): View {
- val rootView = LayoutInflater.from(mActivity)
- .inflate(R.layout.permission_rationale, null) as ViewGroup
+ val rootView =
+ LayoutInflater.from(mActivity).inflate(R.layout.permission_rationale, null) as ViewGroup
// Uses the vertical gravity of the PermissionGrantSingleton style to position the window
val gravity =
@@ -160,13 +160,15 @@ class PermissionRationaleViewHandlerImpl(
val settingsSectionView: ViewGroup? = rootView.findViewById(R.id.settings_section)
settingsSectionView?.visibility = View.GONE
}
- backButton = rootView.findViewById<Button>(R.id.back_button)!!.apply {
- setOnClickListener(this@PermissionRationaleViewHandlerImpl)
-
- // Load the text color from the activity theme rather than the Material Design theme
- val textColor = getColorStateListForAttr(mActivity, android.R.attr.textColorPrimary)!!
- setTextColor(textColor)
- }
+ backButton =
+ rootView.findViewById<Button>(R.id.back_button)!!.apply {
+ setOnClickListener(this@PermissionRationaleViewHandlerImpl)
+
+ // Load the text color from the activity theme rather than the Material Design theme
+ val textColor =
+ getColorStateListForAttr(mActivity, android.R.attr.textColorPrimary)!!
+ setTextColor(textColor)
+ }
this.rootView = rootView
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt
index e219153f3..14aff67ba 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt
@@ -44,8 +44,8 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils
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.v31.SubattributionUtils
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
@@ -93,7 +93,8 @@ class PermissionUsageDetailsViewModelLegacy(
/* getUiInfo= */ false,
/* getNonPlatformPermissions= */ false,
/* callback= */ callback,
- /* sync= */ false)
+ /* sync= */ false
+ )
}
/**
@@ -116,19 +117,26 @@ class PermissionUsageDetailsViewModelLegacy(
}
val startTime =
(System.currentTimeMillis() - showPermissionUsagesDuration).coerceAtLeast(
- Instant.EPOCH.toEpochMilli())
+ Instant.EPOCH.toEpochMilli()
+ )
val appPermissionTimelineUsages: List<AppPermissionTimelineUsage> =
extractAppPermissionTimelineUsagesForGroup(appPermissionUsages, permissionGroup)
val shouldDisplayShowSystemToggle =
shouldDisplayShowSystemToggle(appPermissionTimelineUsages)
val permissionApps: List<PermissionApp> =
getPermissionAppsWithRecentDiscreteUsage(
- appPermissionTimelineUsages, showSystem, startTime)
+ appPermissionTimelineUsages,
+ showSystem,
+ startTime
+ )
val appPermissionUsageEntries =
buildDiscreteAccessClusterData(appPermissionTimelineUsages, showSystem, startTime)
return PermissionUsageDetailsUiData(
- permissionApps, shouldDisplayShowSystemToggle, appPermissionUsageEntries)
+ permissionApps,
+ shouldDisplayShowSystemToggle,
+ appPermissionUsageEntries
+ )
}
private fun getHistoryPreferenceData(
@@ -141,14 +149,14 @@ class PermissionUsageDetailsViewModelLegacy(
getDurationSummary(discreteAccessClusterData, accessTimeList, context)
val proxyLabel = getProxyPackageLabel(discreteAccessClusterData)
val subattributionLabel = getSubattributionLabel(discreteAccessClusterData)
- val showingSubattribution =
- subattributionLabel != null && subattributionLabel.isNotEmpty()
+ val showingSubattribution = subattributionLabel != null && subattributionLabel.isNotEmpty()
val summary =
buildUsageSummary(durationSummaryLabel, proxyLabel, subattributionLabel, context)
return HistoryPreferenceData(
UserHandle.getUserHandleForUid(
- discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.uid),
+ discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.uid
+ ),
discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.packageName,
discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.icon,
discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.label,
@@ -158,7 +166,8 @@ class PermissionUsageDetailsViewModelLegacy(
summary,
showingSubattribution,
discreteAccessClusterData.appPermissionTimelineUsage.attributionTags,
- sessionId)
+ sessionId
+ )
}
/**
@@ -180,7 +189,8 @@ class PermissionUsageDetailsViewModelLegacy(
.map { appPermissionUsage ->
getAppPermissionTimelineUsages(
appPermissionUsage.app,
- appPermissionUsage.groupUsages.firstOrNull { it.group.name == group })
+ appPermissionUsage.groupUsages.firstOrNull { it.group.name == group }
+ )
}
.flatten()
@@ -221,7 +231,10 @@ class PermissionUsageDetailsViewModelLegacy(
.map { appPermissionTimelineUsages ->
val accessDataList =
extractRecentDiscreteAccessData(
- appPermissionTimelineUsages.timelineUsage, showSystem, startTime)
+ appPermissionTimelineUsages.timelineUsage,
+ showSystem,
+ startTime
+ )
if (accessDataList.size <= 1) {
return@map accessDataList.map {
@@ -235,7 +248,9 @@ class PermissionUsageDetailsViewModelLegacy(
.sortedWith(
compareBy(
{ -it.discreteAccessDataList.first().accessTimeMs },
- { it.appPermissionTimelineUsage.permissionApp.label }))
+ { it.appPermissionTimelineUsage.permissionApp.label }
+ )
+ )
.toList()
/**
@@ -253,11 +268,15 @@ class PermissionUsageDetailsViewModelLegacy(
for (discreteAccessData in discreteAccessDataList) {
if (currentDiscreteAccessDataList.isEmpty()) {
currentDiscreteAccessDataList.add(discreteAccessData)
- } else if (!canAccessBeAddedToCluster(
- discreteAccessData, currentDiscreteAccessDataList)) {
+ } else if (
+ !canAccessBeAddedToCluster(discreteAccessData, currentDiscreteAccessDataList)
+ ) {
clusterDataList.add(
DiscreteAccessClusterData(
- appPermissionTimelineUsage, currentDiscreteAccessDataList.toMutableList()))
+ appPermissionTimelineUsage,
+ currentDiscreteAccessDataList.toMutableList()
+ )
+ )
currentDiscreteAccessDataList.clear()
currentDiscreteAccessDataList.add(discreteAccessData)
} else {
@@ -266,8 +285,8 @@ class PermissionUsageDetailsViewModelLegacy(
}
if (currentDiscreteAccessDataList.isNotEmpty()) {
clusterDataList.add(
- DiscreteAccessClusterData(
- appPermissionTimelineUsage, currentDiscreteAccessDataList))
+ DiscreteAccessClusterData(appPermissionTimelineUsage, currentDiscreteAccessDataList)
+ )
}
return clusterDataList
}
@@ -282,8 +301,9 @@ class PermissionUsageDetailsViewModelLegacy(
showSystem: Boolean,
startTime: Long
): List<DiscreteAccessData> {
- return if (timelineUsages.hasDiscreteData() &&
- (showSystem || !timelineUsages.group.isSystem())) {
+ return if (
+ timelineUsages.hasDiscreteData() && (showSystem || !timelineUsages.group.isSystem())
+ ) {
getRecentDiscreteAccessData(timelineUsages, startTime)
.sortedWith(compareBy { -it.accessTimeMs })
.toList()
@@ -377,7 +397,8 @@ class PermissionUsageDetailsViewModelLegacy(
getPackageLabel(
PermissionControllerApplication.get(),
it.proxy!!.packageName!!,
- UserHandle.getUserHandleForUid(it.proxy.uid))
+ UserHandle.getUserHandleForUid(it.proxy.uid)
+ )
}
/** Returns the attribution label for the permission access, if any. */
@@ -406,10 +427,14 @@ class PermissionUsageDetailsViewModelLegacy(
R.string.history_preference_subtext_3,
subTextStrings[0],
subTextStrings[1],
- subTextStrings[2])
+ subTextStrings[2]
+ )
2 ->
context.getString(
- R.string.history_preference_subtext_2, subTextStrings[0], subTextStrings[1])
+ R.string.history_preference_subtext_2,
+ subTextStrings[0],
+ subTextStrings[1]
+ )
1 -> subTextStrings[0]
else -> null
}
@@ -434,7 +459,8 @@ class PermissionUsageDetailsViewModelLegacy(
}
return listOf(
- AppPermissionTimelineUsage(permissionGroup, app, groupUsage, Resources.ID_NULL))
+ AppPermissionTimelineUsage(permissionGroup, app, groupUsage, Resources.ID_NULL)
+ )
}
/** Extracts to a set all the permission groups declared by the platform. */
@@ -448,15 +474,20 @@ class PermissionUsageDetailsViewModelLegacy(
/** Initialize all relevant [TimeFilterItemMs] values. */
private fun initializeTimeFilterItems(context: Context) {
mTimeFilterItemMs.add(
- TimeFilterItemMs(Long.MAX_VALUE, context.getString(R.string.permission_usage_any_time)))
+ 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)))
+ 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)))
+ StringUtils.getIcuPluralsString(context, R.string.permission_usage_last_n_days, 1)
+ )
+ )
// TODO: theianchen add code for filtering by time here.
}
@@ -554,7 +585,11 @@ class PermissionUsageDetailsViewModelFactoryLegacy(
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return PermissionUsageDetailsViewModelLegacy(
- application, roleManager, filterGroup, sessionId)
+ application,
+ roleManager,
+ filterGroup,
+ sessionId
+ )
as T
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt
index d0e751f7d..3032dece5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt
@@ -57,7 +57,8 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel()
mapOf(
Manifest.permission_group.LOCATION to 0,
Manifest.permission_group.CAMERA to 1,
- Manifest.permission_group.MICROPHONE to 2)
+ Manifest.permission_group.MICROPHONE to 2
+ )
private const val DEFAULT_ORDER = 3
}
@@ -79,7 +80,8 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel()
false /*getUiInfo*/,
false /*getNonPlatformPermissions*/,
callback /*callback*/,
- false /*sync*/)
+ false /*sync*/
+ )
}
/**
@@ -108,10 +110,16 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel()
val permissionApps = filteredAppPermissionUsages.getRecentPermissionApps(startTime)
val orderedPermissionGroupsWithUsage =
filteredAppPermissionUsages.buildOrderedPermissionGroupsWithUsageCount(
- context, startTime, showSystem)
+ context,
+ startTime,
+ showSystem
+ )
return PermissionUsagesUiData(
- permissionApps, displayShowSystemToggle, orderedPermissionGroupsWithUsage)
+ permissionApps,
+ displayShowSystemToggle,
+ orderedPermissionGroupsWithUsage
+ )
}
/**
@@ -150,7 +158,9 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel()
.sortedWith(
compareBy(
{ PERMISSION_GROUP_ORDER.getOrDefault(it.permGroup, DEFAULT_ORDER) },
- { getPermGroupLabel(context, it.permGroup).toString() }))
+ { getPermGroupLabel(context, it.permGroup).toString() }
+ )
+ )
}
/** Extracts [PermissionApp] where there has been recent permission usage. */
@@ -163,7 +173,8 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel()
.filter { !EXEMPTED_PERMISSION_GROUPS.contains(it.group.name) }
.any { it.lastAccessTime >= startTime || it.lastAccessTime == 0L }
}
- .map { it.app })
+ .map { it.app }
+ )
}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AllAppPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AllAppPermissionsViewModel.kt
index d789f4e1e..3e651dd9f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AllAppPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AllAppPermissionsViewModel.kt
@@ -33,41 +33,34 @@ import com.android.permissioncontroller.permission.utils.Utils
*
* @param packageName The name of the package this viewModel is representing
* @param user The user of the package this viewModel is representing
- * @param filterGroup An optional single group that should be shown, no other groups will be
- * shown
+ * @param filterGroup An optional single group that should be shown, no other groups will be shown
*/
-class AllAppPermissionsViewModel(
- packageName: String,
- user: UserHandle,
- filterGroup: String?
-) : ViewModel() {
+class AllAppPermissionsViewModel(packageName: String, user: UserHandle, filterGroup: String?) :
+ ViewModel() {
- val allPackagePermissionsLiveData = AllPackagePermissionsLiveData(packageName, user,
- filterGroup)
+ val allPackagePermissionsLiveData =
+ AllPackagePermissionsLiveData(packageName, user, filterGroup)
class AllPackagePermissionsLiveData(
packageName: String,
user: UserHandle,
private val filterGroup: String?
- ) : SmartUpdateMediatorLiveData<@kotlin.jvm.JvmSuppressWildcards
- Map<String, List<String>>>() {
+ ) : SmartUpdateMediatorLiveData<@kotlin.jvm.JvmSuppressWildcards Map<String, List<String>>>() {
- private val packagePermsLiveData =
- PackagePermissionsLiveData[packageName, user]
+ private val packagePermsLiveData = PackagePermissionsLiveData[packageName, user]
private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
init {
- addSource(packagePermsLiveData) {
- update()
- }
- addSource(packageInfoLiveData) {
- update()
- }
+ addSource(packagePermsLiveData) { update() }
+ addSource(packageInfoLiveData) { update() }
}
override fun onUpdate() {
- if (!packagePermsLiveData.isInitialized || packagePermsLiveData.isStale ||
- !packageInfoLiveData.isInitialized) {
+ if (
+ !packagePermsLiveData.isInitialized ||
+ packagePermsLiveData.isStale ||
+ !packageInfoLiveData.isInitialized
+ ) {
return
}
val permissions = packagePermsLiveData.value
@@ -77,10 +70,17 @@ class AllAppPermissionsViewModel(
return
}
- value = permissions
- .filter { filterGroup == null || it.key == filterGroup }
- .filter { (it.key != Manifest.permission_group.STORAGE ||
- Utils.shouldShowStorage(packageInfo)) }
+ value =
+ permissions
+ .filter { filterGroup == null || it.key == filterGroup }
+ .filter {
+ (it.key != Manifest.permission_group.STORAGE ||
+ Utils.shouldShowStorage(packageInfo))
+ }
+ .filter {
+ (!Utils.isHealthPermissionGroup(it.key) ||
+ Utils.shouldShowHealthPermission(packageInfo, it.key))
+ }
}
}
}
@@ -91,8 +91,7 @@ class AllAppPermissionsViewModel(
* @param app The current application
* @param packageName The name of the package this viewModel is representing
* @param user The user of the package this viewModel is representing
- * @param filterGroup An optional single group that should be shown, no other groups will be
- * shown
+ * @param filterGroup An optional single group that should be shown, no other groups will be shown
*/
class AllAppPermissionsViewModelFactory(
private val packageName: String,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
index 741c93aab..5ecab1527 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
@@ -95,135 +95,165 @@ class AppPermissionGroupsViewModel(
val isSystem: Boolean = false,
val subtitle: PermSubtitle
) {
- constructor(groupName: String, isSystem: Boolean) :
- this(groupName, isSystem, PermSubtitle.NONE)
+ constructor(
+ groupName: String,
+ isSystem: Boolean
+ ) : this(groupName, isSystem, PermSubtitle.NONE)
}
// Auto-revoke and hibernation share the same settings
val autoRevokeLiveData = HibernationSettingStateLiveData[packageName, user]
- private val packagePermsLiveData =
- PackagePermissionsLiveData[packageName, user]
+ private val packagePermsLiveData = PackagePermissionsLiveData[packageName, user]
private val appPermGroupUiInfoLiveDatas = mutableMapOf<String, AppPermGroupUiInfoLiveData>()
private val fullStoragePermsLiveData = FullStoragePermissionAppsLiveData
/**
- * LiveData whose data is a map of grant category (either allowed or denied) to a list
- * of permission group names that match the key, and two booleans representing if this is a
- * system group, and a subtitle resource ID, if applicable.
+ * LiveData whose data is a map of grant category (either allowed or denied) to a list of
+ * permission group names that match the key, and two booleans representing if this is a system
+ * group, and a subtitle resource ID, if applicable.
*/
- val packagePermGroupsLiveData = object : SmartUpdateMediatorLiveData<@JvmSuppressWildcards
- Map<Category, List<GroupUiInfo>>>() {
-
- init {
- addSource(packagePermsLiveData) {
- update()
- }
- addSource(fullStoragePermsLiveData) {
- update()
- }
- addSource(autoRevokeLiveData) {
- removeSource(autoRevokeLiveData)
+ val packagePermGroupsLiveData =
+ object :
+ SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<Category, List<GroupUiInfo>>>() {
+
+ init {
+ addSource(packagePermsLiveData) { update() }
+ addSource(fullStoragePermsLiveData) { update() }
+ addSource(autoRevokeLiveData) {
+ removeSource(autoRevokeLiveData)
+ update()
+ }
update()
}
- update()
- }
- override fun onUpdate() {
- val groups = packagePermsLiveData.value?.keys?.filter { it != NON_RUNTIME_NORMAL_PERMS }
- if (groups == null && packagePermsLiveData.isInitialized) {
- value = null
- return
- } else if (groups == null || (Manifest.permission_group.STORAGE in groups &&
- !fullStoragePermsLiveData.isInitialized) || !autoRevokeLiveData.isInitialized) {
- return
- }
-
- val getLiveData = { groupName: String ->
- AppPermGroupUiInfoLiveData[packageName, groupName, user]
- }
- setSourcesToDifference(groups, appPermGroupUiInfoLiveDatas, getLiveData)
+ override fun onUpdate() {
+ val groups =
+ packagePermsLiveData.value?.keys?.filter { it != NON_RUNTIME_NORMAL_PERMS }
+ if (groups == null && packagePermsLiveData.isInitialized) {
+ value = null
+ return
+ } else if (
+ groups == null ||
+ (Manifest.permission_group.STORAGE in groups &&
+ !fullStoragePermsLiveData.isInitialized) ||
+ !autoRevokeLiveData.isInitialized
+ ) {
+ return
+ }
- if (!appPermGroupUiInfoLiveDatas.all { it.value.isInitialized }) {
- return
- }
+ val getLiveData = { groupName: String ->
+ AppPermGroupUiInfoLiveData[packageName, groupName, user]
+ }
+ setSourcesToDifference(groups, appPermGroupUiInfoLiveDatas, getLiveData)
- val groupGrantStates = mutableMapOf<Category,
- MutableList<GroupUiInfo>>()
- groupGrantStates[Category.ALLOWED] = mutableListOf()
- groupGrantStates[Category.ASK] = mutableListOf()
- groupGrantStates[Category.DENIED] = mutableListOf()
+ if (!appPermGroupUiInfoLiveDatas.all { it.value.isInitialized }) {
+ return
+ }
- val fullStorageState = fullStoragePermsLiveData.value?.find { pkg ->
- pkg.packageName == packageName && pkg.user == user
- }
+ val groupGrantStates = mutableMapOf<Category, MutableList<GroupUiInfo>>()
+ groupGrantStates[Category.ALLOWED] = mutableListOf()
+ groupGrantStates[Category.ASK] = mutableListOf()
+ groupGrantStates[Category.DENIED] = mutableListOf()
- for (groupName in groups) {
- val isSystem = PermissionMapping.getPlatformPermissionGroups().contains(groupName)
- appPermGroupUiInfoLiveDatas[groupName]?.value?.let { uiInfo ->
- if (SdkLevel.isAtLeastT() && !uiInfo.shouldShow) {
- return@let
+ val fullStorageState =
+ fullStoragePermsLiveData.value?.find { pkg ->
+ pkg.packageName == packageName && pkg.user == user
}
- if (groupName == Manifest.permission_group.STORAGE &&
- (fullStorageState?.isGranted == true && !fullStorageState.isLegacy)) {
- groupGrantStates[Category.ALLOWED]!!.add(
- GroupUiInfo(groupName, isSystem, PermSubtitle.ALL_FILES))
- return@let
- }
- when (uiInfo.permGrantState) {
- PermGrantState.PERMS_ALLOWED -> {
- val subtitle = if (groupName == Manifest.permission_group.STORAGE) {
- if (SdkLevel.isAtLeastT()) {
- PermSubtitle.NONE
- } else {
- if (fullStorageState?.isLegacy == true) {
- PermSubtitle.ALL_FILES
+
+ for (groupName in groups) {
+ val isSystem =
+ PermissionMapping.getPlatformPermissionGroups().contains(groupName)
+ appPermGroupUiInfoLiveDatas[groupName]?.value?.let { uiInfo ->
+ if (SdkLevel.isAtLeastT() && !uiInfo.shouldShow) {
+ return@let
+ }
+ if (
+ groupName == Manifest.permission_group.STORAGE &&
+ (fullStorageState?.isGranted == true && !fullStorageState.isLegacy)
+ ) {
+ groupGrantStates[Category.ALLOWED]!!.add(
+ GroupUiInfo(groupName, isSystem, PermSubtitle.ALL_FILES)
+ )
+ return@let
+ }
+ when (uiInfo.permGrantState) {
+ PermGrantState.PERMS_ALLOWED -> {
+ val subtitle =
+ if (groupName == Manifest.permission_group.STORAGE) {
+ if (SdkLevel.isAtLeastT()) {
+ PermSubtitle.NONE
+ } else {
+ if (fullStorageState?.isLegacy == true) {
+ PermSubtitle.ALL_FILES
+ } else {
+ PermSubtitle.MEDIA_ONLY
+ }
+ }
} else {
- PermSubtitle.MEDIA_ONLY
+ PermSubtitle.NONE
}
- }
- } else {
- PermSubtitle.NONE
+ groupGrantStates[Category.ALLOWED]!!.add(
+ GroupUiInfo(groupName, isSystem, subtitle)
+ )
}
- groupGrantStates[Category.ALLOWED]!!.add(
- GroupUiInfo(groupName, isSystem, subtitle))
+ PermGrantState.PERMS_ALLOWED_ALWAYS ->
+ groupGrantStates[Category.ALLOWED]!!.add(
+ GroupUiInfo(groupName, isSystem, PermSubtitle.BACKGROUND)
+ )
+ PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY ->
+ groupGrantStates[Category.ALLOWED]!!.add(
+ GroupUiInfo(groupName, isSystem, PermSubtitle.FOREGROUND_ONLY)
+ )
+ PermGrantState.PERMS_DENIED ->
+ groupGrantStates[Category.DENIED]!!.add(
+ GroupUiInfo(groupName, isSystem)
+ )
+ PermGrantState.PERMS_ASK ->
+ groupGrantStates[Category.ASK]!!.add(
+ GroupUiInfo(groupName, isSystem)
+ )
}
- PermGrantState.PERMS_ALLOWED_ALWAYS -> groupGrantStates[
- Category.ALLOWED]!!.add(GroupUiInfo(groupName, isSystem,
- PermSubtitle.BACKGROUND))
- PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY -> groupGrantStates[
- Category.ALLOWED]!!.add(GroupUiInfo(groupName, isSystem,
- PermSubtitle.FOREGROUND_ONLY))
- PermGrantState.PERMS_DENIED -> groupGrantStates[Category.DENIED]!!.add(
- GroupUiInfo(groupName, isSystem))
- PermGrantState.PERMS_ASK -> groupGrantStates[Category.ASK]!!.add(
- GroupUiInfo(groupName, isSystem))
}
}
- }
- value = groupGrantStates
+ value = groupGrantStates
+ }
}
- }
// TODO 206455664: remove once issue is identified
fun logLiveDataState() {
- Log.i(LOG_TAG, "Overall liveData isStale: ${packagePermGroupsLiveData.isStale}, " +
+ Log.i(
+ LOG_TAG,
+ "Overall liveData isStale: ${packagePermGroupsLiveData.isStale}, " +
"isInitialized: ${packagePermGroupsLiveData.isInitialized}, " +
- "value: ${packagePermGroupsLiveData.value}")
- Log.i(LOG_TAG, "AutoRevoke liveData isStale: ${autoRevokeLiveData.isStale}, " +
+ "value: ${packagePermGroupsLiveData.value}"
+ )
+ Log.i(
+ LOG_TAG,
+ "AutoRevoke liveData isStale: ${autoRevokeLiveData.isStale}, " +
"isInitialized: ${autoRevokeLiveData.isInitialized}, " +
- "value: ${autoRevokeLiveData.value}")
- Log.i(LOG_TAG, "PackagePerms liveData isStale: ${packagePermsLiveData.isStale}, " +
+ "value: ${autoRevokeLiveData.value}"
+ )
+ Log.i(
+ LOG_TAG,
+ "PackagePerms liveData isStale: ${packagePermsLiveData.isStale}, " +
"isInitialized: ${packagePermsLiveData.isInitialized}, " +
- "value: ${packagePermsLiveData.value}")
- Log.i(LOG_TAG, "FullStorage liveData isStale: ${fullStoragePermsLiveData.isStale}, " +
+ "value: ${packagePermsLiveData.value}"
+ )
+ Log.i(
+ LOG_TAG,
+ "FullStorage liveData isStale: ${fullStoragePermsLiveData.isStale}, " +
"isInitialized: ${fullStoragePermsLiveData.isInitialized}, " +
- "value size: ${fullStoragePermsLiveData.value?.size}")
+ "value size: ${fullStoragePermsLiveData.value?.size}"
+ )
for ((group, liveData) in appPermGroupUiInfoLiveDatas) {
- Log.i(LOG_TAG, "$group ui liveData isStale: ${liveData.isStale}, " +
+ Log.i(
+ LOG_TAG,
+ "$group ui liveData isStale: ${liveData.isStale}, " +
"isInitialized: ${liveData.isInitialized}, " +
- "value size: ${liveData.value}")
+ "value size: ${liveData.value}"
+ )
}
}
@@ -233,26 +263,33 @@ class AppPermissionGroupsViewModel(
val lightPackageInfo = LightPackageInfoLiveData[packageName, user].getInitializedValue()
if (lightPackageInfo != null) {
- Log.i(LOG_TAG, "sessionId $sessionId setting auto revoke enabled to $enabled for" +
- "$packageName $user")
- val tag = if (enabled) {
- APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
- } else {
- APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED
- }
+ Log.i(
+ LOG_TAG,
+ "sessionId $sessionId setting auto revoke enabled to $enabled for" +
+ "$packageName $user"
+ )
+ val tag =
+ if (enabled) {
+ APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
+ } else {
+ APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED
+ }
PermissionControllerStatsLog.write(
- APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION, sessionId,
- lightPackageInfo.uid, packageName, tag)
-
- val mode = if (enabled) {
- MODE_ALLOWED
- } else {
- MODE_IGNORED
- }
+ APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION,
+ sessionId,
+ lightPackageInfo.uid,
+ packageName,
+ tag
+ )
+
+ val mode =
+ if (enabled) {
+ MODE_ALLOWED
+ } else {
+ MODE_IGNORED
+ }
aom.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, lightPackageInfo.uid, mode)
- if (isHibernationEnabled() &&
- SdkLevel.isAtLeastSv2() &&
- !enabled) {
+ if (isHibernationEnabled() && SdkLevel.isAtLeastSv2() && !enabled) {
// Only unhibernate on S_V2+ to have consistent toggle behavior w/ Settings
val ahm = app.getSystemService(AppHibernationManager::class.java)!!
ahm.setHibernatingForUser(packageName, false)
@@ -281,13 +318,17 @@ class AppPermissionGroupsViewModel(
return
}
- val aggregateDataFilterBeginDays = if (KotlinUtils.is7DayToggleEnabled())
- AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1
+ val aggregateDataFilterBeginDays =
+ if (KotlinUtils.is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7
+ else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1
accessTime.clear()
- val filterTimeBeginMillis = max(System.currentTimeMillis() -
- TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays.toLong()),
- Instant.EPOCH.toEpochMilli())
+ val filterTimeBeginMillis =
+ max(
+ System.currentTimeMillis() -
+ TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays.toLong()),
+ Instant.EPOCH.toEpochMilli()
+ )
val numApps: Int = appPermissionUsages.size
for (appIndex in 0 until numApps) {
val appUsage: AppPermissionUsage = appPermissionUsages[appIndex]
@@ -307,96 +348,128 @@ class AppPermissionGroupsViewModel(
// We might have another AppPermissionUsage entry that's of the same packageName
// but with a different uid. In that case, we want to grab the max lastAccessTime
// as the last usage to show.
- lastAccessTime = Math.max(
+ lastAccessTime =
+ Math.max(
accessTime.getOrDefault(groupName, Instant.EPOCH.toEpochMilli()),
- lastAccessTime)
+ lastAccessTime
+ )
accessTime[groupName] = lastAccessTime
}
}
}
- fun getPreferenceSummary(groupInfo: GroupUiInfo, context: Context, lastAccessTime: Long?):
- String {
- val summaryTimestamp = Utils
- .getPermissionLastAccessSummaryTimestamp(
- lastAccessTime, context, groupInfo.groupName)
+ fun getPreferenceSummary(
+ groupInfo: GroupUiInfo,
+ context: Context,
+ lastAccessTime: Long?
+ ): String {
+ val summaryTimestamp =
+ Utils.getPermissionLastAccessSummaryTimestamp(
+ lastAccessTime,
+ context,
+ groupInfo.groupName
+ )
@AppPermsLastAccessType val lastAccessType: Int = summaryTimestamp.second
return when (groupInfo.subtitle) {
PermSubtitle.BACKGROUND ->
when (lastAccessType) {
- Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
- R.string.app_perms_content_provider_24h_background)
- Utils.LAST_7D_CONTENT_PROVIDER -> context.getString(
- R.string.app_perms_content_provider_7d_background)
- Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ Utils.LAST_24H_CONTENT_PROVIDER ->
+ context.getString(R.string.app_perms_content_provider_24h_background)
+ Utils.LAST_7D_CONTENT_PROVIDER ->
+ context.getString(R.string.app_perms_content_provider_7d_background)
+ Utils.LAST_24H_SENSOR_TODAY ->
+ context.getString(
R.string.app_perms_24h_access_background,
- summaryTimestamp.first)
- Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ summaryTimestamp.first
+ )
+ Utils.LAST_24H_SENSOR_YESTERDAY ->
+ context.getString(
R.string.app_perms_24h_access_yest_background,
- summaryTimestamp.first)
- Utils.LAST_7D_SENSOR -> context.getString(
+ summaryTimestamp.first
+ )
+ Utils.LAST_7D_SENSOR ->
+ context.getString(
R.string.app_perms_7d_access_background,
- summaryTimestamp.third, summaryTimestamp.first)
- Utils.NOT_IN_LAST_7D -> context.getString(
- R.string.permission_subtitle_background)
- else -> context.getString(
- R.string.permission_subtitle_background)
+ summaryTimestamp.third,
+ summaryTimestamp.first
+ )
+ Utils.NOT_IN_LAST_7D ->
+ context.getString(R.string.permission_subtitle_background)
+ else -> context.getString(R.string.permission_subtitle_background)
}
PermSubtitle.MEDIA_ONLY ->
when (lastAccessType) {
- Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
- R.string.app_perms_content_provider_24h_media_only)
- Utils.LAST_7D_CONTENT_PROVIDER -> context.getString(
- R.string.app_perms_content_provider_7d_media_only)
- Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ Utils.LAST_24H_CONTENT_PROVIDER ->
+ context.getString(R.string.app_perms_content_provider_24h_media_only)
+ Utils.LAST_7D_CONTENT_PROVIDER ->
+ context.getString(R.string.app_perms_content_provider_7d_media_only)
+ Utils.LAST_24H_SENSOR_TODAY ->
+ context.getString(
R.string.app_perms_24h_access_media_only,
- summaryTimestamp.first)
- Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ summaryTimestamp.first
+ )
+ Utils.LAST_24H_SENSOR_YESTERDAY ->
+ context.getString(
R.string.app_perms_24h_access_yest_media_only,
- summaryTimestamp.first)
- Utils.LAST_7D_SENSOR -> context.getString(
+ summaryTimestamp.first
+ )
+ Utils.LAST_7D_SENSOR ->
+ context.getString(
R.string.app_perms_7d_access_media_only,
- summaryTimestamp.third, summaryTimestamp.first)
- Utils.NOT_IN_LAST_7D -> context.getString(
- R.string.permission_subtitle_media_only)
+ summaryTimestamp.third,
+ summaryTimestamp.first
+ )
+ Utils.NOT_IN_LAST_7D ->
+ context.getString(R.string.permission_subtitle_media_only)
else -> context.getString(R.string.permission_subtitle_media_only)
}
PermSubtitle.ALL_FILES ->
when (lastAccessType) {
- Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
- R.string.app_perms_content_provider_24h_all_files)
- Utils.LAST_7D_CONTENT_PROVIDER -> context.getString(
- R.string.app_perms_content_provider_7d_all_files)
- Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ Utils.LAST_24H_CONTENT_PROVIDER ->
+ context.getString(R.string.app_perms_content_provider_24h_all_files)
+ Utils.LAST_7D_CONTENT_PROVIDER ->
+ context.getString(R.string.app_perms_content_provider_7d_all_files)
+ Utils.LAST_24H_SENSOR_TODAY ->
+ context.getString(
R.string.app_perms_24h_access_all_files,
- summaryTimestamp.first)
- Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ summaryTimestamp.first
+ )
+ Utils.LAST_24H_SENSOR_YESTERDAY ->
+ context.getString(
R.string.app_perms_24h_access_yest_all_files,
- summaryTimestamp.first)
- Utils.LAST_7D_SENSOR -> context.getString(
+ summaryTimestamp.first
+ )
+ Utils.LAST_7D_SENSOR ->
+ context.getString(
R.string.app_perms_7d_access_all_files,
- summaryTimestamp.third, summaryTimestamp.first)
- Utils.NOT_IN_LAST_7D -> context.getString(
- R.string.permission_subtitle_all_files)
+ summaryTimestamp.third,
+ summaryTimestamp.first
+ )
+ Utils.NOT_IN_LAST_7D ->
+ context.getString(R.string.permission_subtitle_all_files)
else -> context.getString(R.string.permission_subtitle_all_files)
}
else ->
// PermSubtitle.FOREGROUND_ONLY should fall into this as well
when (lastAccessType) {
- Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
- R.string.app_perms_content_provider_24h)
- Utils.LAST_7D_CONTENT_PROVIDER -> context.getString(
- R.string.app_perms_content_provider_7d)
- Utils.LAST_24H_SENSOR_TODAY -> context.getString(
- R.string.app_perms_24h_access,
- summaryTimestamp.first)
- Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ Utils.LAST_24H_CONTENT_PROVIDER ->
+ context.getString(R.string.app_perms_content_provider_24h)
+ Utils.LAST_7D_CONTENT_PROVIDER ->
+ context.getString(R.string.app_perms_content_provider_7d)
+ Utils.LAST_24H_SENSOR_TODAY ->
+ context.getString(R.string.app_perms_24h_access, summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY ->
+ context.getString(
R.string.app_perms_24h_access_yest,
- summaryTimestamp.first)
- Utils.LAST_7D_SENSOR -> context.getString(
+ summaryTimestamp.first
+ )
+ Utils.LAST_7D_SENSOR ->
+ context.getString(
R.string.app_perms_7d_access,
- summaryTimestamp.third, summaryTimestamp.first)
+ summaryTimestamp.third,
+ summaryTimestamp.first
+ )
Utils.NOT_IN_LAST_7D -> ""
else -> ""
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt
index 169cc7222..e15986657 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt
@@ -20,6 +20,7 @@ package com.android.permissioncontroller.permission.ui.model
import android.Manifest
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
import android.Manifest.permission_group.READ_MEDIA_VISUAL
import android.annotation.SuppressLint
import android.app.Activity
@@ -28,19 +29,14 @@ import android.app.AppOpsManager.MODE_ALLOWED
import android.app.AppOpsManager.MODE_ERRORED
import android.app.AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE
import android.app.Application
-import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.UserHandle
-import android.provider.MediaStore
import android.util.Log
-import androidx.activity.result.ActivityResultLauncher
-import androidx.activity.result.contract.ActivityResultContract
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
-import androidx.core.util.Consumer
import androidx.fragment.app.Fragment
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -56,14 +52,13 @@ import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState
import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
-import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
import com.android.permissioncontroller.permission.data.get
+import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData
import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission
import com.android.permissioncontroller.permission.service.PermissionChangeStorageImpl
import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl
-import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs
import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW
import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_ALWAYS
import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND
@@ -73,12 +68,14 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionViewMod
import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.DENY_FOREGROUND
import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.LOCATION_ACCURACY
import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.SELECT_PHOTOS
+import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs
import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity
import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity.EXTRA_SHOULD_SHOW_SETTINGS_SECTION
import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision
import com.android.permissioncontroller.permission.utils.KotlinUtils.isLocationAccuracyEnabled
import com.android.permissioncontroller.permission.utils.KotlinUtils.isPhotoPickerPromptEnabled
+import com.android.permissioncontroller.permission.utils.KotlinUtils.openPhotoPickerForApp
import com.android.permissioncontroller.permission.utils.LocationUtils
import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.PermissionMapping.getPartialStorageGrantPermissionsForGroup
@@ -112,7 +109,6 @@ class AppPermissionViewModel(
companion object {
private val LOG_TAG = AppPermissionViewModel::class.java.simpleName
private const val DEVICE_PROFILE_ROLE_PREFIX = "android.app.role"
- const val PHOTO_PICKER_REQUEST_CODE = 1
}
interface ConfirmDialogShowingFragment {
@@ -134,15 +130,16 @@ class AppPermissionViewModel(
GRANT_BOTH(GRANT_FOREGROUND.value or GRANT_BACKGROUND.value),
REVOKE_BOTH(REVOKE_FOREGROUND.value or REVOKE_BACKGROUND.value),
GRANT_FOREGROUND_ONLY(GRANT_FOREGROUND.value or REVOKE_BACKGROUND.value),
- GRANT_All_FILE_ACCESS(1 shl 4),
+ GRANT_ALL_FILE_ACCESS(1 shl 4),
GRANT_FINE_LOCATION(1 shl 5),
REVOKE_FINE_LOCATION(1 shl 6),
GRANT_STORAGE_SUPERGROUP(1 shl 7),
REVOKE_STORAGE_SUPERGROUP(1 shl 8),
GRANT_STORAGE_SUPERGROUP_CONFIRMED(
- GRANT_STORAGE_SUPERGROUP.value or GRANT_FOREGROUND.value),
+ GRANT_STORAGE_SUPERGROUP.value or GRANT_FOREGROUND.value
+ ),
REVOKE_STORAGE_SUPERGROUP_CONFIRMED(REVOKE_STORAGE_SUPERGROUP.value or REVOKE_BOTH.value),
- PHOTOS_SELECTED( 1 shl 9);
+ PHOTOS_SELECTED(1 shl 9);
infix fun andValue(other: ChangeRequest): Int {
return value and other.value
@@ -158,89 +155,86 @@ class AppPermissionViewModel(
DENY(5),
DENY_FOREGROUND(6),
LOCATION_ACCURACY(7),
- SELECT_PHOTOS( 8);
+ SELECT_PHOTOS(8)
}
private val isStorageAndLessThanT =
permGroupName == Manifest.permission_group.STORAGE && !SdkLevel.isAtLeastT()
private var hasConfirmedRevoke = false
private var lightAppPermGroup: LightAppPermGroup? = null
- private var photoPickerLauncher: ActivityResultLauncher<Unit>? = null
- private var photoPickerResultConsumer: Consumer<Int>? = null
private val mediaStorageSupergroupPermGroups = mutableMapOf<String, LightAppPermGroup>()
/* Whether the current ViewModel is Location permission with both Coarse and Fine */
private var shouldShowLocationAccuracy: Boolean? = null
- /**
- * A livedata which determines which detail string, if any, should be shown
- */
+ /** A livedata which determines which detail string, if any, should be shown */
val detailResIdLiveData = MutableLiveData<Pair<Int, Int?>>()
- /**
- * A livedata which stores the device admin, if there is one
- */
+ /** A livedata which stores the device admin, if there is one */
val showAdminSupportLiveData = MutableLiveData<RestrictedLockUtils.EnforcedAdmin>()
- /**
- * A livedata for determining the display state of safety label information
- */
- val showPermissionRationaleLiveData = object : SmartUpdateMediatorLiveData<Boolean>() {
- private val safetyLabelInfoLiveData = if (SdkLevel.isAtLeastU()) {
- SafetyLabelInfoLiveData[packageName, user]
- } else {
- null
- }
+ /** A livedata for determining the display state of safety label information */
+ val showPermissionRationaleLiveData =
+ object : SmartUpdateMediatorLiveData<Boolean>() {
+ private val safetyLabelInfoLiveData =
+ if (SdkLevel.isAtLeastU()) {
+ SafetyLabelInfoLiveData[packageName, user]
+ } else {
+ null
+ }
- init {
- if (safetyLabelInfoLiveData != null &&
- PermissionMapping.isSafetyLabelAwarePermissionGroup(permGroupName)) {
- addSource(safetyLabelInfoLiveData) { update() }
- } else {
- value = false
+ init {
+ if (
+ safetyLabelInfoLiveData != null &&
+ PermissionMapping.isSafetyLabelAwarePermissionGroup(permGroupName)
+ ) {
+ addSource(safetyLabelInfoLiveData) { update() }
+ } else {
+ value = false
+ }
}
- }
- override fun onUpdate() {
- if (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale) {
- return
- }
+ override fun onUpdate() {
+ if (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale) {
+ return
+ }
- val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel
- if (safetyLabel == null) {
- value = false
- return
- }
+ val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel
+ if (safetyLabel == null) {
+ value = false
+ return
+ }
- value = SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(
- safetyLabel, permGroupName).any()
+ value =
+ SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(
+ safetyLabel,
+ permGroupName
+ )
+ .any()
+ }
}
- }
- /**
- * A livedata which determines which detail string, if any, should be shown
- */
- val fullStorageStateLiveData = object : SmartUpdateMediatorLiveData<FullStoragePackageState>() {
- init {
- if (isStorageAndLessThanT) {
- addSource(FullStoragePermissionAppsLiveData) {
- update()
+ /** A livedata which determines which detail string, if any, should be shown */
+ val fullStorageStateLiveData =
+ object : SmartUpdateMediatorLiveData<FullStoragePackageState>() {
+ init {
+ if (isStorageAndLessThanT) {
+ addSource(FullStoragePermissionAppsLiveData) { update() }
+ } else {
+ value = null
}
- } else {
- value = null
}
- }
- override fun onUpdate() {
- for (state in FullStoragePermissionAppsLiveData.value ?: return) {
- if (state.packageName == packageName && state.user == user) {
- value = state
- return
+ override fun onUpdate() {
+ for (state in FullStoragePermissionAppsLiveData.value ?: return) {
+ if (state.packageName == packageName && state.user == user) {
+ value = state
+ return
+ }
}
+ value = null
+ return
}
- value = null
- return
}
- }
data class ButtonState(
var isChecked: Boolean,
@@ -251,182 +245,211 @@ class AppPermissionViewModel(
constructor() : this(false, true, false, null)
}
- /**
- * A livedata which computes the state of the radio buttons
- */
- val buttonStateLiveData = object :
- SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<ButtonType, ButtonState>>() {
+ /** A livedata which computes the state of the radio buttons */
+ val buttonStateLiveData =
+ object : SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<ButtonType, ButtonState>>() {
- private val appPermGroupLiveData = LightAppPermGroupLiveData[packageName, permGroupName,
- user]
- private val mediaStorageSupergroupLiveData =
- mutableMapOf<String, LightAppPermGroupLiveData>()
+ private val appPermGroupLiveData =
+ LightAppPermGroupLiveData[packageName, permGroupName, user]
+ private val mediaStorageSupergroupLiveData =
+ mutableMapOf<String, LightAppPermGroupLiveData>()
- init {
+ init {
+ addSource(appPermGroupLiveData) { appPermGroup ->
+ lightAppPermGroup = appPermGroup
+ if (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) {
+ onMediaPermGroupUpdate(permGroupName, appPermGroup)
+ }
+ if (appPermGroupLiveData.isInitialized && appPermGroup == null) {
+ value = null
+ } else if (appPermGroup != null) {
+ if (isStorageAndLessThanT && !fullStorageStateLiveData.isInitialized) {
+ return@addSource
+ }
+ update()
+ }
+ }
- addSource(appPermGroupLiveData) { appPermGroup ->
- lightAppPermGroup = appPermGroup
- if (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) {
- onMediaPermGroupUpdate(permGroupName, appPermGroup)
+ if (isStorageAndLessThanT) {
+ addSource(fullStorageStateLiveData) { update() }
}
- if (appPermGroupLiveData.isInitialized && appPermGroup == null) {
- value = null
- } else if (appPermGroup != null) {
- if (isStorageAndLessThanT && !fullStorageStateLiveData.isInitialized) {
- return@addSource
+
+ if (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) {
+ for (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) {
+ val liveData = LightAppPermGroupLiveData[packageName, permGroupName, user]
+ mediaStorageSupergroupLiveData[permGroupName] = liveData
+ }
+ for (permGroupName in mediaStorageSupergroupLiveData.keys) {
+ val liveData = mediaStorageSupergroupLiveData[permGroupName]!!
+ addSource(liveData) { permGroup ->
+ onMediaPermGroupUpdate(permGroupName, permGroup)
+ }
}
- update()
}
+
+ addSource(showPermissionRationaleLiveData) { update() }
}
- if (isStorageAndLessThanT) {
- addSource(fullStorageStateLiveData) {
+ private fun onMediaPermGroupUpdate(
+ permGroupName: String,
+ permGroup: LightAppPermGroup?
+ ) {
+ if (permGroup == null) {
+ mediaStorageSupergroupPermGroups.remove(permGroupName)
+ value = null
+ } else {
+ mediaStorageSupergroupPermGroups[permGroupName] = permGroup
update()
}
}
- if (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) {
- for (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) {
- val liveData = LightAppPermGroupLiveData[packageName, permGroupName, user]
- mediaStorageSupergroupLiveData[permGroupName] = liveData
- }
- for (permGroupName in mediaStorageSupergroupLiveData.keys) {
- val liveData = mediaStorageSupergroupLiveData[permGroupName]!!
- addSource(liveData) { permGroup ->
- onMediaPermGroupUpdate(permGroupName, permGroup)
+ override fun onUpdate() {
+ val group = appPermGroupLiveData.value ?: return
+ for (mediaGroupLiveData in mediaStorageSupergroupLiveData.values) {
+ if (!mediaGroupLiveData.isInitialized) {
+ return
}
}
- }
-
- addSource(showPermissionRationaleLiveData) {
- update()
- }
- }
-
- private fun onMediaPermGroupUpdate(permGroupName: String, permGroup: LightAppPermGroup?) {
- if (permGroup == null) {
- mediaStorageSupergroupPermGroups.remove(permGroupName)
- value = null
- } else {
- mediaStorageSupergroupPermGroups[permGroupName] = permGroup
- update()
- }
- }
- override fun onUpdate() {
- val group = appPermGroupLiveData.value ?: return
- for (mediaGroupLiveData in mediaStorageSupergroupLiveData.values) {
- if (!mediaGroupLiveData.isInitialized) {
+ if (!showPermissionRationaleLiveData.isInitialized) {
return
}
- }
- if (!showPermissionRationaleLiveData.isInitialized) {
- return
- }
-
- val admin = RestrictedLockUtils.getProfileOrDeviceOwner(app, user)
-
- val allowedState = ButtonState()
- val allowedAlwaysState = ButtonState()
- val allowedForegroundState = ButtonState()
- val askOneTimeState = ButtonState()
- val askState = ButtonState()
- val deniedState = ButtonState()
- val deniedForegroundState = ButtonState()
- val selectState = ButtonState()
-
- askOneTimeState.isShown = group.foreground.isGranted && group.isOneTime
- askState.isShown = PermissionMapping.supportsOneTimeGrant(permGroupName) &&
- !(group.foreground.isGranted && group.isOneTime)
- deniedState.isShown = true
-
- if (group.hasPermWithBackgroundMode) {
- // Background / Foreground / Deny case
- allowedForegroundState.isShown = true
- if (group.hasBackgroundGroup) {
- allowedAlwaysState.isShown = true
- }
+ val admin = RestrictedLockUtils.getProfileOrDeviceOwner(app, user)
+
+ val allowedState = ButtonState()
+ val allowedAlwaysState = ButtonState()
+ val allowedForegroundState = ButtonState()
+ val askOneTimeState = ButtonState()
+ val askState = ButtonState()
+ val deniedState = ButtonState()
+ val deniedForegroundState = ButtonState()
+ val selectState = ButtonState()
+
+ askOneTimeState.isShown = group.foreground.isGranted && group.isOneTime
+ askState.isShown =
+ PermissionMapping.supportsOneTimeGrant(permGroupName) &&
+ !(group.foreground.isGranted && group.isOneTime)
+ deniedState.isShown = true
- allowedAlwaysState.isChecked = group.background.isGranted &&
- group.foreground.isGranted && !group.background.isOneTime
- allowedForegroundState.isChecked = group.foreground.isGranted &&
- (!group.background.isGranted || group.background.isOneTime) &&
- !group.foreground.isOneTime
- askState.isChecked = !group.foreground.isGranted && group.isOneTime
- askOneTimeState.isChecked = group.foreground.isGranted && group.isOneTime
- askOneTimeState.isShown = askOneTimeState.isChecked
- deniedState.isChecked = !group.foreground.isGranted && !group.isOneTime
- if (applyFixToForegroundBackground(group, group.foreground.isSystemFixed,
- group.background.isSystemFixed, allowedAlwaysState,
- allowedForegroundState, askState, deniedState,
- deniedForegroundState) ||
- applyFixToForegroundBackground(group, group.foreground.isPolicyFixed,
- group.background.isPolicyFixed, allowedAlwaysState,
- allowedForegroundState, askState, deniedState,
- deniedForegroundState)) {
- showAdminSupportLiveData.value = admin
- val detailId = getDetailResIdForFixedByPolicyPermissionGroup(group,
- admin != null)
- if (detailId != 0) {
- detailResIdLiveData.value = detailId to null
+ if (group.hasPermWithBackgroundMode) {
+ // Background / Foreground / Deny case
+ allowedForegroundState.isShown = true
+ if (group.hasBackgroundGroup) {
+ allowedAlwaysState.isShown = true
}
- } else if (Utils.areGroupPermissionsIndividuallyControlled(app, permGroupName)) {
- val detailId = getIndividualPermissionDetailResId(group)
- detailResIdLiveData.value = detailId.first to detailId.second
- }
- } else if (KotlinUtils.isPhotoPickerPromptEnabled() &&
- group.permGroupName == READ_MEDIA_VISUAL &&
- group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) {
- // Allow / Select Photos / Deny case
- allowedState.isShown = true
- deniedState.isShown = true
- selectState.isShown = true
- deniedState.isChecked = !group.isGranted
- selectState.isChecked = isPartialStorageGrant(group)
- allowedState.isChecked = group.isGranted && !isPartialStorageGrant(group)
- } else {
- // Allow / Deny case
- allowedState.isShown = true
-
- allowedState.isChecked = group.foreground.isGranted && !group.foreground.isOneTime
- askState.isChecked = !group.foreground.isGranted && group.isOneTime
- askOneTimeState.isChecked = group.foreground.isGranted && group.isOneTime
- askOneTimeState.isShown = askOneTimeState.isChecked
- deniedState.isChecked = !group.foreground.isGranted && !group.isOneTime
-
- if (group.foreground.isPolicyFixed || group.foreground.isSystemFixed) {
- allowedState.isEnabled = false
- askState.isEnabled = false
- deniedState.isEnabled = false
- showAdminSupportLiveData.value = admin
- val detailId = getDetailResIdForFixedByPolicyPermissionGroup(group,
- admin != null)
- if (detailId != 0) {
- detailResIdLiveData.value = detailId to null
+ allowedAlwaysState.isChecked =
+ group.background.isGranted &&
+ group.foreground.isGranted &&
+ !group.background.isOneTime
+ allowedForegroundState.isChecked =
+ group.foreground.isGranted &&
+ (!group.background.isGranted || group.background.isOneTime) &&
+ !group.foreground.isOneTime
+ askState.isChecked = !group.foreground.isGranted && group.isOneTime
+ askOneTimeState.isChecked = group.foreground.isGranted && group.isOneTime
+ askOneTimeState.isShown = askOneTimeState.isChecked
+ deniedState.isChecked = !group.foreground.isGranted && !group.isOneTime
+ if (
+ applyFixToForegroundBackground(
+ group,
+ group.foreground.isSystemFixed,
+ group.background.isSystemFixed,
+ allowedAlwaysState,
+ allowedForegroundState,
+ askState,
+ deniedState,
+ deniedForegroundState
+ ) ||
+ applyFixToForegroundBackground(
+ group,
+ group.foreground.isPolicyFixed,
+ group.background.isPolicyFixed,
+ allowedAlwaysState,
+ allowedForegroundState,
+ askState,
+ deniedState,
+ deniedForegroundState
+ )
+ ) {
+ showAdminSupportLiveData.value = admin
+ val detailId =
+ getDetailResIdForFixedByPolicyPermissionGroup(group, admin != null)
+ if (detailId != 0) {
+ detailResIdLiveData.value = detailId to null
+ }
+ } else if (
+ Utils.areGroupPermissionsIndividuallyControlled(app, permGroupName)
+ ) {
+ val detailId = getIndividualPermissionDetailResId(group)
+ detailResIdLiveData.value = detailId.first to detailId.second
+ }
+ } else if (
+ group.permGroupName == READ_MEDIA_VISUAL &&
+ shouldShowPhotoPickerPromptForApp(group)
+ ) {
+ // Allow / Select Photos / Deny case
+ allowedState.isShown = true
+ deniedState.isShown = true
+ selectState.isShown = true
+
+ deniedState.isChecked = !group.isGranted
+ selectState.isChecked = isPartialStorageGrant(group)
+ allowedState.isChecked = group.isGranted && !isPartialStorageGrant(group)
+ if (group.foreground.isPolicyFixed || group.foreground.isSystemFixed) {
+ allowedState.isEnabled = false
+ selectState.isEnabled = false
+ deniedState.isEnabled = false
+ showAdminSupportLiveData.value = admin
+ val detailId =
+ getDetailResIdForFixedByPolicyPermissionGroup(group, admin != null)
+ if (detailId != 0) {
+ detailResIdLiveData.value = detailId to null
+ }
+ }
+ } else {
+ // Allow / Deny case
+ allowedState.isShown = true
+
+ allowedState.isChecked =
+ group.foreground.isGranted && !group.foreground.isOneTime
+ askState.isChecked = !group.foreground.isGranted && group.isOneTime
+ askOneTimeState.isChecked = group.foreground.isGranted && group.isOneTime
+ askOneTimeState.isShown = askOneTimeState.isChecked
+ deniedState.isChecked = !group.foreground.isGranted && !group.isOneTime
+
+ if (group.foreground.isPolicyFixed || group.foreground.isSystemFixed) {
+ allowedState.isEnabled = false
+ askState.isEnabled = false
+ deniedState.isEnabled = false
+ showAdminSupportLiveData.value = admin
+ val detailId =
+ getDetailResIdForFixedByPolicyPermissionGroup(group, admin != null)
+ if (detailId != 0) {
+ detailResIdLiveData.value = detailId to null
+ }
+ }
+ if (isForegroundGroupSpecialCase(permGroupName)) {
+ allowedForegroundState.isShown = true
+ allowedState.isShown = false
+ allowedForegroundState.isChecked = allowedState.isChecked
+ allowedForegroundState.isEnabled = allowedState.isEnabled
}
}
- if (isForegroundGroupSpecialCase(permGroupName)) {
- allowedForegroundState.isShown = true
- allowedState.isShown = false
- allowedForegroundState.isChecked = allowedState.isChecked
- allowedForegroundState.isEnabled = allowedState.isEnabled
+ if (group.packageInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ // Pre-M app's can't ask for runtime permissions
+ askState.isShown = false
+ deniedState.isChecked = askState.isChecked || deniedState.isChecked
+ deniedForegroundState.isChecked =
+ askState.isChecked || deniedForegroundState.isChecked
}
- }
- if (group.packageInfo.targetSdkVersion < Build.VERSION_CODES.M) {
- // Pre-M app's can't ask for runtime permissions
- askState.isShown = false
- deniedState.isChecked = askState.isChecked || deniedState.isChecked
- deniedForegroundState.isChecked = askState.isChecked ||
- deniedForegroundState.isChecked
- }
- val storageState = fullStorageStateLiveData.value
- if (isStorageAndLessThanT && storageState?.isLegacy != true) {
- val allowedAllFilesState = allowedAlwaysState
- val allowedMediaOnlyState = allowedForegroundState
- if (storageState != null) {
+ val storageState = fullStorageStateLiveData.value
+ if (isStorageAndLessThanT && storageState?.isLegacy != true) {
+ val allowedAllFilesState = allowedAlwaysState
+ val allowedMediaOnlyState = allowedForegroundState
+ if (storageState != null) {
// Set up the tri state permission for storage
allowedAllFilesState.isEnabled = allowedState.isEnabled
allowedAllFilesState.isShown = true
@@ -434,62 +457,63 @@ class AppPermissionViewModel(
allowedAllFilesState.isChecked = true
deniedState.isChecked = false
}
- } else {
- allowedAllFilesState.isEnabled = false
- allowedAllFilesState.isShown = false
+ } else {
+ allowedAllFilesState.isEnabled = false
+ allowedAllFilesState.isShown = false
+ }
+ allowedMediaOnlyState.isShown = true
+ allowedMediaOnlyState.isEnabled = allowedState.isEnabled
+ allowedMediaOnlyState.isChecked =
+ allowedState.isChecked && storageState?.isGranted != true
+ allowedState.isChecked = false
+ allowedState.isShown = false
}
- allowedMediaOnlyState.isShown = true
- allowedMediaOnlyState.isEnabled = allowedState.isEnabled
- allowedMediaOnlyState.isChecked = allowedState.isChecked &&
- storageState?.isGranted != true
- allowedState.isChecked = false
- allowedState.isShown = false
- }
- if (shouldShowLocationAccuracy == null) {
- shouldShowLocationAccuracy = isLocationAccuracyEnabled() &&
- group.permissions.containsKey(ACCESS_FINE_LOCATION)
- }
- val locationAccuracyState = ButtonState(isFineLocationChecked(group),
- true, false, null)
- if (shouldShowLocationAccuracy == true && !deniedState.isChecked) {
- locationAccuracyState.isShown = true
- }
- if (group.foreground.isSystemFixed || group.foreground.isPolicyFixed) {
- locationAccuracyState.isEnabled = false
- }
+ if (shouldShowLocationAccuracy == null) {
+ shouldShowLocationAccuracy =
+ isLocationAccuracyEnabled() &&
+ group.permissions.containsKey(ACCESS_FINE_LOCATION)
+ }
+ val locationAccuracyState =
+ ButtonState(isFineLocationChecked(group), true, false, null)
+ if (shouldShowLocationAccuracy == true && !deniedState.isChecked) {
+ locationAccuracyState.isShown = true
+ }
+ if (group.foreground.isSystemFixed || group.foreground.isPolicyFixed) {
+ locationAccuracyState.isEnabled = false
+ }
- if (value == null) {
- logAppPermissionFragmentViewed()
- }
+ if (value == null) {
+ logAppPermissionFragmentViewed()
+ }
- value = mapOf(
- ALLOW to allowedState, ALLOW_ALWAYS to allowedAlwaysState,
- ALLOW_FOREGROUND to allowedForegroundState, ASK_ONCE to askOneTimeState,
- ASK to askState, DENY to deniedState, DENY_FOREGROUND to deniedForegroundState,
- LOCATION_ACCURACY to locationAccuracyState, SELECT_PHOTOS to selectState)
+ value =
+ mapOf(
+ ALLOW to allowedState,
+ ALLOW_ALWAYS to allowedAlwaysState,
+ ALLOW_FOREGROUND to allowedForegroundState,
+ ASK_ONCE to askOneTimeState,
+ ASK to askState,
+ DENY to deniedState,
+ DENY_FOREGROUND to deniedForegroundState,
+ LOCATION_ACCURACY to locationAccuracyState,
+ SELECT_PHOTOS to selectState
+ )
+ }
}
- }
- fun registerPhotoPickerResultIfNeeded(fragment: Fragment) {
- if (permGroupName != READ_MEDIA_VISUAL) {
- return
+ private fun shouldShowPhotoPickerPromptForApp(group: LightAppPermGroup): Boolean {
+ if (
+ !isPhotoPickerPromptEnabled() ||
+ group.packageInfo.targetSdkVersion < Build.VERSION_CODES.TIRAMISU
+ ) {
+ return false
}
- photoPickerLauncher = fragment.registerForActivityResult(
- object : ActivityResultContract<Unit, Int>() {
- override fun parseResult(resultCode: Int, intent: Intent?): Int {
- return resultCode
- }
-
- override fun createIntent(context: Context, input: Unit): Intent {
- return Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP)
- .putExtra(Intent.EXTRA_UID, lightAppPermGroup?.packageInfo?.uid)
- .setType(KotlinUtils.getMimeTypeForPermissions(
- lightAppPermGroup?.foregroundPermNames ?: emptyList()))
- }
- }) { result ->
- photoPickerResultConsumer?.accept(result)
+ if (group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ return true
}
+ val userSelectedPerm = group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] ?: return false
+ return !userSelectedPerm.isImplicit
}
private fun isFineLocationChecked(group: LightAppPermGroup): Boolean {
@@ -501,11 +525,11 @@ class AppPermissionViewModel(
// 2. Else if FINE or COARSE have the isSelectedLocationAccuracy flag set, then return
// true if FINE isSelectedLocationAccuracy is set.
// 3. Else, return default precision from device config.
- return if (fineLocation.isGranted ||
- coarseLocation.isGranted) {
+ return if (fineLocation.isGranted || coarseLocation.isGranted) {
fineLocation.isGranted
- } else if (fineLocation.isSelectedLocationAccuracy ||
- coarseLocation.isSelectedLocationAccuracy) {
+ } else if (
+ fineLocation.isSelectedLocationAccuracy || coarseLocation.isSelectedLocationAccuracy
+ ) {
fineLocation.isSelectedLocationAccuracy
} else {
getDefaultPrecision()
@@ -517,7 +541,7 @@ class AppPermissionViewModel(
// TODO evanseverson: Actually change mic/camera to be a foreground only permission
private fun isForegroundGroupSpecialCase(permissionGroupName: String): Boolean {
return permissionGroupName.equals(Manifest.permission_group.CAMERA) ||
- permissionGroupName.equals(Manifest.permission_group.MICROPHONE)
+ permissionGroupName.equals(Manifest.permission_group.MICROPHONE)
}
/**
@@ -608,10 +632,12 @@ class AppPermissionViewModel(
logAppPermissionFragmentActionReportedForPermissionGroup(
/* changeId= */ Random().nextLong(),
group,
- APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_RATIONALE)
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_RATIONALE
+ )
}
- val intent = Intent(activity, PermissionRationaleActivity::class.java).apply {
+ val intent =
+ Intent(activity, PermissionRationaleActivity::class.java).apply {
putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
putExtra(Constants.EXTRA_SESSION_ID, sessionId)
@@ -622,6 +648,7 @@ class AppPermissionViewModel(
/**
* Navigate to either the App Permission Groups screen, or the Permission Apps Screen.
+ *
* @param fragment The current fragment
* @param action The action to be taken
* @param args The arguments to pass to the fragment
@@ -635,26 +662,33 @@ class AppPermissionViewModel(
fragment.findNavController().navigateSafe(actionId, args)
}
+ fun openPhotoPicker(fragment: Fragment) {
+ val appPermGroup = lightAppPermGroup ?: return
+ openPhotoPickerForApp(
+ fragment.requireActivity(),
+ appPermGroup.packageInfo.uid,
+ appPermGroup.foregroundPermNames,
+ 0
+ )
+ }
+
/**
* Request to grant/revoke permissions group.
*
* Does <u>not</u> handle:
- *
- * * Individually granted permissions
- * * Permission groups with background permissions
+ * * Individually granted permissions
+ * * Permission groups with background permissions
*
* <u>Does</u> handle:
- *
- * * Default grant permissions
+ * * Default grant permissions
*
* @param setOneTime Whether or not to set this permission as one time
* @param fragment The fragment calling this method
* @param defaultDeny The system which will show the default deny dialog. Usually the same as
- * the fragment.
+ * the fragment.
* @param changeRequest Which permission group (foreground/background/both) should be changed
* @param buttonClicked button which was pressed to initiate the change, one of
- * AppPermissionFragmentActionReported.button_pressed constants
- *
+ * AppPermissionFragmentActionReported.button_pressed constants
* @return The dialogue to show, if applicable, or if the request was processed.
*/
fun requestChange(
@@ -685,8 +719,12 @@ class AppPermissionViewModel(
if (changeRequest == ChangeRequest.REVOKE_FINE_LOCATION) {
if (!group.isOneTime) {
- val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group,
- filterPermissions = listOf(ACCESS_FINE_LOCATION))
+ val newGroup =
+ KotlinUtils.revokeForegroundRuntimePermissions(
+ app,
+ group,
+ filterPermissions = listOf(ACCESS_FINE_LOCATION)
+ )
logPermissionChanges(group, newGroup, buttonClicked)
}
KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, group, false)
@@ -696,10 +734,18 @@ class AppPermissionViewModel(
if (changeRequest == ChangeRequest.PHOTOS_SELECTED) {
val partialGrantPerms = getPartialStorageGrantPermissionsForGroup(group)
val nonSelectedPerms = group.permissions.keys.filter { it !in partialGrantPerms }
- var newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group,
- filterPermissions = nonSelectedPerms)
- newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, newGroup,
- filterPermissions = partialGrantPerms.toList())
+ var newGroup =
+ KotlinUtils.revokeForegroundRuntimePermissions(
+ app,
+ group,
+ filterPermissions = nonSelectedPerms
+ )
+ newGroup =
+ KotlinUtils.grantForegroundRuntimePermissions(
+ app,
+ newGroup,
+ filterPermissions = partialGrantPerms.toList()
+ )
logPermissionChanges(group, newGroup, buttonClicked)
return
}
@@ -713,27 +759,31 @@ class AppPermissionViewModel(
var showCDMWarning = false
if (shouldRevokeForeground && wasForegroundGranted) {
- showDefaultDenyDialog = (group.foreground.isGrantedByDefault ||
+ showDefaultDenyDialog =
+ (group.foreground.isGrantedByDefault ||
!group.supportsRuntimePerms ||
group.hasInstallToRuntimeSplit)
- showGrantedByDefaultWarning = showGrantedByDefaultWarning ||
- group.foreground.isGrantedByDefault
+ showGrantedByDefaultWarning =
+ showGrantedByDefaultWarning || group.foreground.isGrantedByDefault
showCDMWarning = showCDMWarning || group.foreground.isGrantedByRole
}
if (shouldRevokeBackground && wasBackgroundGranted) {
- showDefaultDenyDialog = showDefaultDenyDialog ||
+ showDefaultDenyDialog =
+ showDefaultDenyDialog ||
group.background.isGrantedByDefault ||
!group.supportsRuntimePerms ||
group.hasInstallToRuntimeSplit
- showGrantedByDefaultWarning = showGrantedByDefaultWarning ||
- group.background.isGrantedByDefault
+ showGrantedByDefaultWarning =
+ showGrantedByDefaultWarning || group.background.isGrantedByDefault
showCDMWarning = showCDMWarning || group.background.isGrantedByRole
}
if (showCDMWarning) {
// Refine showCDMWarning to only trigger for apps holding a device profile role
- val heldRoles = context.getSystemService(android.app.role.RoleManager::class.java)
+ val heldRoles =
+ context
+ .getSystemService(android.app.role.RoleManager::class.java)
.getHeldRolesFromController(packageName)
val heldProfiles = heldRoles.filter { it.startsWith(DEVICE_PROFILE_ROLE_PREFIX) }
showCDMWarning = showCDMWarning && heldProfiles.isNotEmpty()
@@ -743,14 +793,24 @@ class AppPermissionViewModel(
if (group.permGroupName == Manifest.permission_group.STORAGE) {
showDefaultDenyDialog = false
} else if (changeRequest == ChangeRequest.GRANT_FOREGROUND) {
- showMediaConfirmDialog(setOneTime, defaultDeny,
- ChangeRequest.GRANT_STORAGE_SUPERGROUP, buttonClicked, group.permGroupName,
- group.packageInfo.targetSdkVersion)
+ showMediaConfirmDialog(
+ setOneTime,
+ defaultDeny,
+ ChangeRequest.GRANT_STORAGE_SUPERGROUP,
+ buttonClicked,
+ group.permGroupName,
+ group.packageInfo.targetSdkVersion
+ )
return
} else if (changeRequest == ChangeRequest.REVOKE_BOTH) {
- showMediaConfirmDialog(setOneTime, defaultDeny,
- ChangeRequest.REVOKE_STORAGE_SUPERGROUP, buttonClicked, group.permGroupName,
- group.packageInfo.targetSdkVersion)
+ showMediaConfirmDialog(
+ setOneTime,
+ defaultDeny,
+ ChangeRequest.REVOKE_STORAGE_SUPERGROUP,
+ buttonClicked,
+ group.permGroupName,
+ group.packageInfo.targetSdkVersion
+ )
return
} else {
showDefaultDenyDialog = false
@@ -758,20 +818,32 @@ class AppPermissionViewModel(
}
if (showDefaultDenyDialog && !hasConfirmedRevoke && showGrantedByDefaultWarning) {
- defaultDeny.showConfirmDialog(changeRequest, R.string.system_warning, buttonClicked,
- setOneTime)
+ defaultDeny.showConfirmDialog(
+ changeRequest,
+ R.string.system_warning,
+ buttonClicked,
+ setOneTime
+ )
return
}
if (showDefaultDenyDialog && !hasConfirmedRevoke) {
- defaultDeny.showConfirmDialog(changeRequest, R.string.old_sdk_deny_warning,
- buttonClicked, setOneTime)
+ defaultDeny.showConfirmDialog(
+ changeRequest,
+ R.string.old_sdk_deny_warning,
+ buttonClicked,
+ setOneTime
+ )
return
}
if (showCDMWarning) {
- defaultDeny.showConfirmDialog(changeRequest,
- R.string.cdm_profile_revoke_warning, buttonClicked, setOneTime)
+ defaultDeny.showConfirmDialog(
+ changeRequest,
+ R.string.cdm_profile_revoke_warning,
+ buttonClicked,
+ setOneTime
+ )
return
}
@@ -780,12 +852,20 @@ class AppPermissionViewModel(
var newGroup = group2
val oldGroup = group2
- if (shouldRevokeBackground && group2.hasBackgroundGroup &&
- (wasBackgroundGranted || group2.background.isUserFixed ||
- group2.isOneTime != setOneTime)) {
- newGroup = KotlinUtils
- .revokeBackgroundRuntimePermissions(app, newGroup, oneTime = setOneTime,
- forceRemoveRevokedCompat = shouldClearOneTimeRevokedCompat(newGroup))
+ if (
+ shouldRevokeBackground &&
+ group2.hasBackgroundGroup &&
+ (wasBackgroundGranted ||
+ group2.background.isUserFixed ||
+ group2.isOneTime != setOneTime)
+ ) {
+ newGroup =
+ KotlinUtils.revokeBackgroundRuntimePermissions(
+ app,
+ newGroup,
+ oneTime = setOneTime,
+ forceRemoveRevokedCompat = shouldClearOneTimeRevokedCompat(newGroup)
+ )
// only log if we have actually denied permissions, not if we switch from
// "ask every time" to denied
@@ -794,12 +874,17 @@ class AppPermissionViewModel(
}
}
- if (shouldRevokeForeground &&
- (wasForegroundGranted || group2.isOneTime != setOneTime)) {
- newGroup = KotlinUtils
- .revokeForegroundRuntimePermissions(app, newGroup, userFixed = false,
- oneTime = setOneTime,
- forceRemoveRevokedCompat = shouldClearOneTimeRevokedCompat(newGroup))
+ if (
+ shouldRevokeForeground && (wasForegroundGranted || group2.isOneTime != setOneTime)
+ ) {
+ newGroup =
+ KotlinUtils.revokeForegroundRuntimePermissions(
+ app,
+ newGroup,
+ userFixed = false,
+ oneTime = setOneTime,
+ forceRemoveRevokedCompat = shouldClearOneTimeRevokedCompat(newGroup)
+ )
// only log if we have actually denied permissions, not if we switch from
// "ask every time" to denied
@@ -809,13 +894,16 @@ class AppPermissionViewModel(
}
if (shouldGrantForeground) {
- newGroup = if (shouldShowLocationAccuracy == true &&
- !isFineLocationChecked(newGroup)) {
- KotlinUtils.grantForegroundRuntimePermissions(app, newGroup,
- filterPermissions = listOf(ACCESS_COARSE_LOCATION))
- } else {
- KotlinUtils.grantForegroundRuntimePermissions(app, newGroup)
- }
+ newGroup =
+ if (shouldShowLocationAccuracy == true && !isFineLocationChecked(newGroup)) {
+ KotlinUtils.grantForegroundRuntimePermissions(
+ app,
+ newGroup,
+ filterPermissions = listOf(ACCESS_COARSE_LOCATION)
+ )
+ } else {
+ KotlinUtils.grantForegroundRuntimePermissions(app, newGroup)
+ }
if (!wasForegroundGranted) {
SafetyNetLogger.logPermissionToggled(newGroup)
@@ -832,15 +920,14 @@ class AppPermissionViewModel(
logPermissionChanges(oldGroup, newGroup, buttonClicked)
- fullStorageStateLiveData.value?.let {
- FullStoragePermissionAppsLiveData.recalculate()
- }
+ fullStorageStateLiveData.value?.let { FullStoragePermissionAppsLiveData.recalculate() }
}
}
private fun shouldClearOneTimeRevokedCompat(group: LightAppPermGroup): Boolean {
- return isPhotoPickerPromptEnabled() && permGroupName == READ_MEDIA_VISUAL &&
- group.permissions.values.any { it.isCompatRevoked && it.isOneTime }
+ return isPhotoPickerPromptEnabled() &&
+ permGroupName == READ_MEDIA_VISUAL &&
+ group.permissions.values.any { it.isCompatRevoked && it.isOneTime }
}
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
@@ -850,8 +937,10 @@ class AppPermissionViewModel(
}
private fun expandToSupergroup(group: LightAppPermGroup): List<LightAppPermGroup> {
- val mediaSupergroup = PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS
- .mapNotNull { mediaStorageSupergroupPermGroups[it] }
+ val mediaSupergroup =
+ PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS.mapNotNull {
+ mediaStorageSupergroupPermGroups[it]
+ }
return if (expandsToStorageSupergroup(group)) {
mediaSupergroup
} else {
@@ -860,21 +949,23 @@ class AppPermissionViewModel(
}
private fun getPermGroupIcon(permGroup: String) =
- Utils.getGroupInfo(permGroup, app.applicationContext)?.icon ?: R.drawable.ic_empty_icon
+ Utils.getGroupInfo(permGroup, app.applicationContext)?.icon ?: R.drawable.ic_empty_icon
private val storagePermGroupIcon = getPermGroupIcon(Manifest.permission_group.STORAGE)
- private val auralPermGroupIcon = if (SdkLevel.isAtLeastT()) {
- getPermGroupIcon(Manifest.permission_group.READ_MEDIA_AURAL)
- } else {
- R.drawable.ic_empty_icon
- }
+ private val auralPermGroupIcon =
+ if (SdkLevel.isAtLeastT()) {
+ getPermGroupIcon(Manifest.permission_group.READ_MEDIA_AURAL)
+ } else {
+ R.drawable.ic_empty_icon
+ }
- private val visualPermGroupIcon = if (SdkLevel.isAtLeastT()) {
- getPermGroupIcon(Manifest.permission_group.READ_MEDIA_VISUAL)
- } else {
- R.drawable.ic_empty_icon
- }
+ private val visualPermGroupIcon =
+ if (SdkLevel.isAtLeastT()) {
+ getPermGroupIcon(Manifest.permission_group.READ_MEDIA_VISUAL)
+ } else {
+ R.drawable.ic_empty_icon
+ }
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun showMediaConfirmDialog(
@@ -885,56 +976,63 @@ class AppPermissionViewModel(
groupName: String,
targetSdk: Int
) {
-
val aural = groupName == Manifest.permission_group.READ_MEDIA_AURAL
val visual = groupName == Manifest.permission_group.READ_MEDIA_VISUAL
val allow = changeRequest === ChangeRequest.GRANT_STORAGE_SUPERGROUP
val deny = changeRequest === ChangeRequest.REVOKE_STORAGE_SUPERGROUP
- val (iconId, titleId, messageId) = when {
- targetSdk < Build.VERSION_CODES.Q && aural && allow ->
- Triple(
- storagePermGroupIcon,
- R.string.media_confirm_dialog_title_a_to_p_aural_allow,
- R.string.media_confirm_dialog_message_a_to_p_aural_allow)
- targetSdk < Build.VERSION_CODES.Q && aural && deny ->
- Triple(
- storagePermGroupIcon,
- R.string.media_confirm_dialog_title_a_to_p_aural_deny,
- R.string.media_confirm_dialog_message_a_to_p_aural_deny)
- targetSdk < Build.VERSION_CODES.Q && visual && allow ->
- Triple(
- storagePermGroupIcon,
- R.string.media_confirm_dialog_title_a_to_p_visual_allow,
- R.string.media_confirm_dialog_message_a_to_p_visual_allow)
- targetSdk < Build.VERSION_CODES.Q && visual && deny ->
- Triple(
- storagePermGroupIcon,
- R.string.media_confirm_dialog_title_a_to_p_visual_deny,
- R.string.media_confirm_dialog_message_a_to_p_visual_deny)
- targetSdk <= Build.VERSION_CODES.S_V2 && aural && allow ->
- Triple(
- visualPermGroupIcon,
- R.string.media_confirm_dialog_title_q_to_s_aural_allow,
- R.string.media_confirm_dialog_message_q_to_s_aural_allow)
- targetSdk <= Build.VERSION_CODES.S_V2 && aural && deny ->
- Triple(
- visualPermGroupIcon,
- R.string.media_confirm_dialog_title_q_to_s_aural_deny,
- R.string.media_confirm_dialog_message_q_to_s_aural_deny)
- targetSdk <= Build.VERSION_CODES.S_V2 && visual && allow ->
- Triple(
- auralPermGroupIcon,
- R.string.media_confirm_dialog_title_q_to_s_visual_allow,
- R.string.media_confirm_dialog_message_q_to_s_visual_allow)
- targetSdk <= Build.VERSION_CODES.S_V2 && visual && deny ->
- Triple(
- auralPermGroupIcon,
- R.string.media_confirm_dialog_title_q_to_s_visual_deny,
- R.string.media_confirm_dialog_message_q_to_s_visual_deny)
- else ->
- Triple(0, 0, 0)
- }
+ val (iconId, titleId, messageId) =
+ when {
+ targetSdk < Build.VERSION_CODES.Q && aural && allow ->
+ Triple(
+ storagePermGroupIcon,
+ R.string.media_confirm_dialog_title_a_to_p_aural_allow,
+ R.string.media_confirm_dialog_message_a_to_p_aural_allow
+ )
+ targetSdk < Build.VERSION_CODES.Q && aural && deny ->
+ Triple(
+ storagePermGroupIcon,
+ R.string.media_confirm_dialog_title_a_to_p_aural_deny,
+ R.string.media_confirm_dialog_message_a_to_p_aural_deny
+ )
+ targetSdk < Build.VERSION_CODES.Q && visual && allow ->
+ Triple(
+ storagePermGroupIcon,
+ R.string.media_confirm_dialog_title_a_to_p_visual_allow,
+ R.string.media_confirm_dialog_message_a_to_p_visual_allow
+ )
+ targetSdk < Build.VERSION_CODES.Q && visual && deny ->
+ Triple(
+ storagePermGroupIcon,
+ R.string.media_confirm_dialog_title_a_to_p_visual_deny,
+ R.string.media_confirm_dialog_message_a_to_p_visual_deny
+ )
+ targetSdk <= Build.VERSION_CODES.S_V2 && aural && allow ->
+ Triple(
+ visualPermGroupIcon,
+ R.string.media_confirm_dialog_title_q_to_s_aural_allow,
+ R.string.media_confirm_dialog_message_q_to_s_aural_allow
+ )
+ targetSdk <= Build.VERSION_CODES.S_V2 && aural && deny ->
+ Triple(
+ visualPermGroupIcon,
+ R.string.media_confirm_dialog_title_q_to_s_aural_deny,
+ R.string.media_confirm_dialog_message_q_to_s_aural_deny
+ )
+ targetSdk <= Build.VERSION_CODES.S_V2 && visual && allow ->
+ Triple(
+ auralPermGroupIcon,
+ R.string.media_confirm_dialog_title_q_to_s_visual_allow,
+ R.string.media_confirm_dialog_message_q_to_s_visual_allow
+ )
+ targetSdk <= Build.VERSION_CODES.S_V2 && visual && deny ->
+ Triple(
+ auralPermGroupIcon,
+ R.string.media_confirm_dialog_title_q_to_s_visual_deny,
+ R.string.media_confirm_dialog_message_q_to_s_visual_deny
+ )
+ else -> Triple(0, 0, 0)
+ }
if (iconId == 0 || titleId == 0 || messageId == 0) {
throw UnsupportedOperationException()
@@ -962,9 +1060,8 @@ class AppPermissionViewModel(
*
* @param changeRequest whether to change foreground, background, or both.
* @param buttonPressed button pressed to initiate the change, one of
- * AppPermissionFragmentActionReported.button_pressed constants
+ * AppPermissionFragmentActionReported.button_pressed constants
* @param oneTime whether the change should show that the permission was selected as one-time
- *
*/
fun onDenyAnyWay(changeRequest: ChangeRequest, buttonPressed: Int, oneTime: Boolean) {
val unexpandedGroup = lightAppPermGroup ?: return
@@ -977,16 +1074,17 @@ class AppPermissionViewModel(
var newGroup = group
val oldGroup = group
- if (changeRequest andValue ChangeRequest.REVOKE_BACKGROUND != 0 &&
- group.hasBackgroundGroup) {
+ if (
+ changeRequest andValue ChangeRequest.REVOKE_BACKGROUND != 0 &&
+ group.hasBackgroundGroup
+ ) {
newGroup =
KotlinUtils.revokeBackgroundRuntimePermissions(app, newGroup, false, oneTime)
if (wasBackgroundGranted) {
SafetyNetLogger.logPermissionToggled(newGroup)
}
- hasDefaultPermissions = hasDefaultPermissions ||
- group.background.isGrantedByDefault
+ hasDefaultPermissions = hasDefaultPermissions || group.background.isGrantedByDefault
}
if (changeRequest andValue ChangeRequest.REVOKE_FOREGROUND != 0) {
@@ -1003,9 +1101,7 @@ class AppPermissionViewModel(
hasConfirmedRevoke = true
}
- fullStorageStateLiveData.value?.let {
- FullStoragePermissionAppsLiveData.recalculate()
- }
+ fullStorageStateLiveData.value?.let { FullStoragePermissionAppsLiveData.recalculate() }
}
}
@@ -1017,11 +1113,12 @@ class AppPermissionViewModel(
fun setAllFilesAccess(granted: Boolean) {
val aom = app.getSystemService(AppOpsManager::class.java)!!
val uid = lightAppPermGroup?.packageInfo?.uid ?: return
- val mode = if (granted) {
- MODE_ALLOWED
- } else {
- MODE_ERRORED
- }
+ val mode =
+ if (granted) {
+ MODE_ALLOWED
+ } else {
+ MODE_ERRORED
+ }
val fullStorageGrant = fullStorageStateLiveData.value?.isGranted
if (fullStorageGrant != null && fullStorageGrant != granted) {
aom.setUidMode(OPSTR_MANAGE_EXTERNAL_STORAGE, uid, mode)
@@ -1039,8 +1136,7 @@ class AppPermissionViewModel(
}
private fun getIndividualPermissionDetailResId(group: LightAppPermGroup): Pair<Int, Int> {
- return when (val numRevoked =
- group.permissions.filter { !it.value.isGranted }.size) {
+ return when (val numRevoked = group.permissions.filter { !it.value.isGranted }.size) {
0 -> R.string.permission_revoked_none to numRevoked
group.permissions.size -> R.string.permission_revoked_all to numRevoked
else -> R.string.permission_revoked_count to numRevoked
@@ -1055,8 +1151,8 @@ class AppPermissionViewModel(
hasAdmin: Boolean
): Int {
val isForegroundPolicyDenied = group.foreground.isPolicyFixed && !group.foreground.isGranted
- val isPolicyFullyFixedWithGrantedOrNoBkg = group.isPolicyFullyFixed &&
- (group.background.isGranted || !group.hasBackgroundGroup)
+ val isPolicyFullyFixedWithGrantedOrNoBkg =
+ group.isPolicyFullyFixed && (group.background.isGranted || !group.hasBackgroundGroup)
if (group.foreground.isSystemFixed || group.background.isSystemFixed) {
return R.string.permission_summary_enabled_system_fixed
} else if (hasAdmin) {
@@ -1110,11 +1206,17 @@ class AppPermissionViewModel(
for ((permName, permission) in oldGroup.permissions) {
val newPermission = newGroup.permissions[permName] ?: continue
- if (permission.isGranted != newPermission.isGranted ||
- permission.flags != newPermission.flags) {
+ if (
+ permission.isGranted != newPermission.isGranted ||
+ permission.flags != newPermission.flags
+ ) {
logAppPermissionFragmentActionReported(changeId, newPermission, buttonPressed)
- PermissionDecisionStorageImpl.recordPermissionDecision(app.applicationContext,
- packageName, permGroupName, newPermission.isGranted)
+ PermissionDecisionStorageImpl.recordPermissionDecision(
+ app.applicationContext,
+ packageName,
+ permGroupName,
+ newPermission.isGranted
+ )
PermissionChangeStorageImpl.recordPermissionChange(packageName)
}
}
@@ -1136,13 +1238,28 @@ class AppPermissionViewModel(
buttonPressed: Int
) {
val uid = KotlinUtils.getPackageUid(app, packageName, user) ?: return
- PermissionControllerStatsLog.write(APP_PERMISSION_FRAGMENT_ACTION_REPORTED, sessionId,
- changeId, uid, packageName, permission.permInfo.name,
- permission.isGranted, permission.flags, buttonPressed)
- Log.v(LOG_TAG, "Permission changed via UI with sessionId=$sessionId changeId=" +
- "$changeId uid=$uid packageName=$packageName permission=" + permission.permInfo.name +
- " isGranted=" + permission.isGranted + " permissionFlags=" +
- permission.flags + " buttonPressed=$buttonPressed")
+ PermissionControllerStatsLog.write(
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED,
+ sessionId,
+ changeId,
+ uid,
+ packageName,
+ permission.permInfo.name,
+ permission.isGranted,
+ permission.flags,
+ buttonPressed
+ )
+ Log.v(
+ LOG_TAG,
+ "Permission changed via UI with sessionId=$sessionId changeId=" +
+ "$changeId uid=$uid packageName=$packageName permission=" +
+ permission.permInfo.name +
+ " isGranted=" +
+ permission.isGranted +
+ " permissionFlags=" +
+ permission.flags +
+ " buttonPressed=$buttonPressed"
+ )
}
/** Logs information about this AppPermissionGroup and view session */
@@ -1156,19 +1273,20 @@ class AppPermissionViewModel(
uid,
packageName,
permGroupName,
- permissionRationaleShown)
+ permissionRationaleShown
+ )
Log.v(
LOG_TAG,
"AppPermission fragment viewed with sessionId=$sessionId uid=$uid " +
"packageName=$packageName permGroupName=$permGroupName " +
- "permissionRationaleShown=$permissionRationaleShown")
+ "permissionRationaleShown=$permissionRationaleShown"
+ )
}
/**
- * A partial storage grant happens when:
- * An app which doesn't support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED granted, or
- * An app which does support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED and/or
- * ACCESS_MEDIA_LOCATION granted
+ * A partial storage grant happens when: An app which doesn't support the photo picker has
+ * READ_MEDIA_VISUAL_USER_SELECTED granted, or An app which does support the photo picker has
+ * READ_MEDIA_VISUAL_USER_SELECTED and/or ACCESS_MEDIA_LOCATION granted
*/
private fun isPartialStorageGrant(group: LightAppPermGroup): Boolean {
if (!isPhotoPickerPromptEnabled() || group.permGroupName != READ_MEDIA_VISUAL) {
@@ -1177,9 +1295,10 @@ class AppPermissionViewModel(
val partialPerms = getPartialStorageGrantPermissionsForGroup(group)
- return group.isGranted && group.permissions.values.all {
- it.name in partialPerms || (it.name !in partialPerms && !it.isGranted)
- }
+ return group.isGranted &&
+ group.permissions.values.all {
+ it.name in partialPerms || (it.name !in partialPerms && !it.isGranted)
+ }
}
}
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 3891550f0..55baedda4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
@@ -39,11 +39,8 @@ import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP
import android.os.Build
import android.os.Bundle
import android.os.Process
-import android.os.UserManager
import android.permission.PermissionManager
-import android.provider.MediaStore
import android.util.Log
-import androidx.annotation.ChecksSdkIntAtLeast
import androidx.core.util.Consumer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@@ -73,9 +70,9 @@ import com.android.permissioncontroller.auto.DrivingDecisionReminderService
import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
-import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
import com.android.permissioncontroller.permission.data.get
+import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData
import com.android.permissioncontroller.permission.model.AppPermissionGroup
import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
@@ -119,24 +116,27 @@ import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT
import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity
-import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils
import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision
import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions
import com.android.permissioncontroller.permission.utils.KotlinUtils.grantForegroundRuntimePermissions
import com.android.permissioncontroller.permission.utils.KotlinUtils.isLocationAccuracyEnabled
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isPhotoPickerPromptEnabled
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isPhotoPickerPromptSupported
+import com.android.permissioncontroller.permission.utils.KotlinUtils.openPhotoPickerForApp
import com.android.permissioncontroller.permission.utils.KotlinUtils.revokeBackgroundRuntimePermissions
import com.android.permissioncontroller.permission.utils.KotlinUtils.revokeForegroundRuntimePermissions
import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.PermissionMapping.getPartialStorageGrantPermissionsForGroup
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
/**
- * ViewModel for the GrantPermissionsActivity. Tracks all permission groups that are affected by
- * the permissions requested by the user, and generates a RequestInfo object for each group, if
- * action is needed. It will not return any data if one of the requests is malformed.
+ * ViewModel for the GrantPermissionsActivity. Tracks all permission groups that are affected by the
+ * permissions requested by the user, and generates a RequestInfo object for each group, if action
+ * is needed. It will not return any data if one of the requests is malformed.
*
* @param app: The current application
* @param packageName: The packageName permissions are being requested for
@@ -155,9 +155,12 @@ class GrantPermissionsViewModel(
private val user = Process.myUserHandle()
private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
private val safetyLabelInfoLiveData =
- if (SdkLevel.isAtLeastU() && requestedPermissions
- .mapNotNull { PermissionMapping.getGroupOfPlatformPermission(it) }
- .any { PermissionMapping.isSafetyLabelAwarePermissionGroup(it) }) {
+ if (
+ SdkLevel.isAtLeastU() &&
+ requestedPermissions
+ .mapNotNull { PermissionMapping.getGroupOfPlatformPermission(it) }
+ .any { PermissionMapping.isSafetyLabelAwarePermissionGroup(it) }
+ ) {
SafetyLabelInfoLiveData[packageName, user]
} else {
null
@@ -183,6 +186,18 @@ class GrantPermissionsViewModel(
private var appPermGroupLiveDatas = mutableMapOf<String, LightAppPermGroupLiveData>()
+ internal data class ResultCallback(val consumer: Consumer<Intent?>, val requestCode: Int)
+
+ private var activityResultCallback: ResultCallback? = null
+
+ init {
+ if (storedState?.containsKey(SAVED_REQUEST_CODE_KEY) == true) {
+ if (storedState.getInt(SAVED_REQUEST_CODE_KEY) == PHOTO_PICKER_REQUEST_CODE) {
+ setPhotoPickerCallback()
+ }
+ }
+ }
+
/**
* A class which represents a correctly requested permission group, and the buttons and messages
* which should be shown with it.
@@ -199,418 +214,491 @@ class GrantPermissionsViewModel(
val groupName = groupInfo.name
}
- var activityResultCallback: Consumer<Intent>? = null
-
- /**
- * A LiveData which holds a list of the currently pending RequestInfos
- */
- val requestInfosLiveData = object :
- SmartUpdateMediatorLiveData<List<RequestInfo>>() {
- private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName
- private val packagePermissionsLiveData = PackagePermissionsLiveData[packageName, user]
-
- init {
- addSource(packagePermissionsLiveData) { onPackageLoaded() }
- addSource(packageInfoLiveData) { onPackageLoaded() }
- if (safetyLabelInfoLiveData != null) {
- addSource(safetyLabelInfoLiveData) { onPackageLoaded() }
- }
-
- // Load package state, if available
- onPackageLoaded()
- }
+ /** A LiveData which holds a list of the currently pending RequestInfos */
+ val requestInfosLiveData =
+ object : SmartUpdateMediatorLiveData<List<RequestInfo>>() {
+ private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName
+ private val packagePermissionsLiveData = PackagePermissionsLiveData[packageName, user]
+
+ init {
+ addSource(packagePermissionsLiveData) { onPackageLoaded() }
+ addSource(packageInfoLiveData) { onPackageLoaded() }
+ if (safetyLabelInfoLiveData != null) {
+ addSource(safetyLabelInfoLiveData) { onPackageLoaded() }
+ }
- private fun onPackageLoaded() {
- if (packageInfoLiveData.isStale ||
- packagePermissionsLiveData.isStale ||
- (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale)) {
- return
+ // Load package state, if available
+ onPackageLoaded()
}
- val groups = packagePermissionsLiveData.value
- val pI = packageInfoLiveData.value
- if (groups == null || groups.isEmpty() || pI == null) {
- Log.e(LOG_TAG, "Package $packageName not found")
- value = null
- return
- }
- packageInfo = pI
+ private fun onPackageLoaded() {
+ if (
+ packageInfoLiveData.isStale ||
+ packagePermissionsLiveData.isStale ||
+ (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale)
+ ) {
+ return
+ }
- if (packageInfo.requestedPermissions.isEmpty() ||
- packageInfo.targetSdkVersion < Build.VERSION_CODES.M) {
- Log.e(LOG_TAG, "Package $packageName has no requested permissions, or " +
- "is a pre-M app")
- value = null
- return
- }
+ val groups = packagePermissionsLiveData.value
+ val pI = packageInfoLiveData.value
+ if (groups == null || groups.isEmpty() || pI == null) {
+ Log.e(LOG_TAG, "Package $packageName not found")
+ value = null
+ return
+ }
+ packageInfo = pI
+
+ if (
+ packageInfo.requestedPermissions.isEmpty() ||
+ packageInfo.targetSdkVersion < Build.VERSION_CODES.M
+ ) {
+ Log.e(
+ LOG_TAG,
+ "Package $packageName has no requested permissions, or " + "is a pre-M app"
+ )
+ value = null
+ return
+ }
- val allAffectedPermissions = requestedPermissions.toMutableSet()
- for (requestedPerm in requestedPermissions) {
- allAffectedPermissions.addAll(computeAffectedPermissions(requestedPerm, groups))
- }
- unfilteredAffectedPermissions = allAffectedPermissions.toList()
+ val allAffectedPermissions = requestedPermissions.toMutableSet()
+ for (requestedPerm in requestedPermissions) {
+ allAffectedPermissions.addAll(computeAffectedPermissions(requestedPerm, groups))
+ }
+ unfilteredAffectedPermissions = allAffectedPermissions.toList()
- setAppPermGroupsLiveDatas(groups.toMutableMap().apply {
- remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS)
- })
+ setAppPermGroupsLiveDatas(
+ groups.toMutableMap().apply {
+ remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS)
+ }
+ )
- for (splitPerm in app.getSystemService(
- PermissionManager::class.java)!!.splitPermissions) {
- splitPermissionTargetSdkMap[splitPerm.splitPermission] = splitPerm.targetSdk
+ for (splitPerm in
+ app.getSystemService(PermissionManager::class.java)!!.splitPermissions) {
+ splitPermissionTargetSdkMap[splitPerm.splitPermission] = splitPerm.targetSdk
+ }
}
- }
- private fun setAppPermGroupsLiveDatas(groups: Map<String, List<String>>) {
+ private fun setAppPermGroupsLiveDatas(groups: Map<String, List<String>>) {
- val requestedGroups = groups.filter { (_, perms) ->
- perms.any { it in unfilteredAffectedPermissions }
- }
+ val requestedGroups =
+ groups.filter { (_, perms) ->
+ perms.any { it in unfilteredAffectedPermissions }
+ }
- if (requestedGroups.isEmpty()) {
- Log.e(LOG_TAG, "None of " +
- "$unfilteredAffectedPermissions in $groups")
- value = null
- return
- }
+ if (requestedGroups.isEmpty()) {
+ Log.e(LOG_TAG, "None of " + "$unfilteredAffectedPermissions in $groups")
+ value = null
+ return
+ }
- val getLiveDataFun = { groupName: String ->
- LightAppPermGroupLiveData[packageName, groupName, user]
+ val getLiveDataFun = { groupName: String ->
+ LightAppPermGroupLiveData[packageName, groupName, user]
+ }
+ setSourcesToDifference(requestedGroups.keys, appPermGroupLiveDatas, getLiveDataFun)
}
- setSourcesToDifference(requestedGroups.keys, appPermGroupLiveDatas, getLiveDataFun)
- }
- override fun onUpdate() {
- if (appPermGroupLiveDatas.any { it.value.isStale }) {
- return
- }
- var newGroups = false
- for ((groupName, groupLiveData) in appPermGroupLiveDatas) {
- val appPermGroup = groupLiveData.value
- if (appPermGroup == null || groupName in permGroupsToSkip) {
- if (appPermGroup == null) {
- Log.e(LOG_TAG, "Group $packageName $groupName invalid")
- }
- groupStates[groupName to true]?.state = STATE_SKIPPED
- groupStates[groupName to false]?.state = STATE_SKIPPED
- continue
+ override fun onUpdate() {
+ if (appPermGroupLiveDatas.any { it.value.isStale }) {
+ return
}
-
- packageInfo = appPermGroup.packageInfo
-
- val states = groupStates.filter { it.key.first == groupName }
- if (states.isNotEmpty()) {
- for ((key, state) in states) {
- val allAffectedGranted = state.affectedPermissions.all { perm ->
- appPermGroup.permissions[perm]?.isGranted == true &&
- appPermGroup.permissions[perm]?.isRevokeWhenRequested == false
- }
- if (allAffectedGranted) {
- groupStates[key]!!.state = STATE_ALLOWED
+ var newGroups = false
+ for ((groupName, groupLiveData) in appPermGroupLiveDatas) {
+ val appPermGroup = groupLiveData.value
+ if (appPermGroup == null || groupName in permGroupsToSkip) {
+ if (appPermGroup == null) {
+ Log.e(LOG_TAG, "Group $packageName $groupName invalid")
}
+ groupStates[groupName to true]?.state = STATE_SKIPPED
+ groupStates[groupName to false]?.state = STATE_SKIPPED
+ continue
}
- } else {
- newGroups = true
- }
- }
- if (newGroups) {
- groupStates = getRequiredGroupStates(
- appPermGroupLiveDatas.mapNotNull { it.value.value })
- }
- setRequestInfosFromGroupStates()
- }
+ packageInfo = appPermGroup.packageInfo
- private fun setRequestInfosFromGroupStates() {
- val requestInfos = mutableListOf<RequestInfo>()
- for ((key, groupState) in groupStates) {
- val groupInfo = groupState.group.permGroupInfo
- val (groupName, isBackground) = key
- if (groupState.state != STATE_UNKNOWN) {
- continue
- }
- val fgState = groupStates[groupName to false]
- val bgState = groupStates[groupName to true]
- var needFgPermissions = false
- var needBgPermissions = false
- var isFgUserSet = false
- var isBgUserSet = false
- var minSdkForOrderedSplitPermissions = Build.VERSION_CODES.R
- if (fgState?.group != null) {
- val fgGroup = fgState.group
- for (perm in fgState.affectedPermissions) {
- minSdkForOrderedSplitPermissions = maxOf(minSdkForOrderedSplitPermissions,
- splitPermissionTargetSdkMap.getOrDefault(perm, 0))
- if (fgGroup.permissions[perm]?.isGranted == false) {
- // If any of the requested permissions is not granted,
- // needFgPermissions = true
- needFgPermissions = true
- // If any of the requested permission's UserSet is true and the
- // permission is not granted, isFgUserSet = true.
- if (fgGroup.permissions[perm]?.isUserSet == true) {
- isFgUserSet = true
+ val states = groupStates.filter { it.key.first == groupName }
+ if (states.isNotEmpty()) {
+ for ((key, state) in states) {
+ val allAffectedGranted =
+ state.affectedPermissions.all { perm ->
+ appPermGroup.permissions[perm]?.isGranted == true &&
+ appPermGroup.permissions[perm]?.isRevokeWhenRequested ==
+ false
+ }
+ if (allAffectedGranted) {
+ groupStates[key]!!.state = STATE_ALLOWED
}
}
+ } else {
+ newGroups = true
}
}
- if (bgState?.group?.background?.isGranted == false) {
- needBgPermissions = true
- isBgUserSet = bgState.group.background.isUserSet
+
+ if (newGroups) {
+ groupStates =
+ getRequiredGroupStates(appPermGroupLiveDatas.mapNotNull { it.value.value })
}
+ setRequestInfosFromGroupStates()
+ }
- val buttonVisibilities = MutableList(NEXT_BUTTON) { false }
- buttonVisibilities[ALLOW_BUTTON] = true
- buttonVisibilities[DENY_BUTTON] = true
- buttonVisibilities[ALLOW_ONE_TIME_BUTTON] =
- PermissionMapping.supportsOneTimeGrant(groupName)
- var message = RequestMessage.FG_MESSAGE
- // Whether or not to use the foreground, background, or no detail message.
- // null ==
- var detailMessage = RequestMessage.NO_MESSAGE
-
- if (KotlinUtils.isPhotoPickerPromptEnabled() &&
- groupState.group.permGroupName == READ_MEDIA_VISUAL &&
- groupState.group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) {
- // If the USER_SELECTED permission is user fixed and granted, or the app is only
- // requesting USER_SELECTED, direct straight to photo picker
- val userPerm = groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]
- if ((userPerm?.isUserFixed == true && userPerm.isGranted) ||
- groupState.affectedPermissions == listOf(READ_MEDIA_VISUAL_USER_SELECTED)) {
- requestInfos.add(RequestInfo(groupInfo, openPhotoPicker = true))
+ private fun setRequestInfosFromGroupStates() {
+ val requestInfos = mutableListOf<RequestInfo>()
+ for ((key, groupState) in groupStates) {
+ val groupInfo = groupState.group.permGroupInfo
+ val (groupName, isBackground) = key
+ if (groupState.state != STATE_UNKNOWN) {
continue
- } else if (isPartialStorageGrant(groupState.group)) {
- // More photos dialog
- message = RequestMessage.MORE_PHOTOS_MESSAGE
- buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = false
- buttonVisibilities[DENY_BUTTON] = false
- buttonVisibilities[DONT_ALLOW_MORE_SELECTED_BUTTON] = true
- } else {
- // Standard photos dialog
- buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
- buttonVisibilities[DENY_BUTTON] = !isFgUserSet
}
- buttonVisibilities[ALLOW_SELECTED_BUTTON] = true
- buttonVisibilities[ALLOW_BUTTON] = false
- buttonVisibilities[ALLOW_ALL_BUTTON] = true
- } else if (groupState.group.packageInfo.targetSdkVersion >=
- minSdkForOrderedSplitPermissions) {
- if (isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group)) {
- if (needFgPermissions) {
- if (needBgPermissions) {
- if (groupState.group.permGroupName
- .equals(Manifest.permission_group.CAMERA) ||
- groupState.group.permGroupName
- .equals(Manifest.permission_group.MICROPHONE)) {
- if (groupState.group.packageInfo.targetSdkVersion >=
- Build.VERSION_CODES.S) {
- Log.e(LOG_TAG,
+ val fgState = groupStates[groupName to false]
+ val bgState = groupStates[groupName to true]
+ var needFgPermissions = false
+ var needBgPermissions = false
+ var isFgUserSet = false
+ var isBgUserSet = false
+ var minSdkForOrderedSplitPermissions = Build.VERSION_CODES.R
+ if (fgState?.group != null) {
+ val fgGroup = fgState.group
+ for (perm in fgState.affectedPermissions) {
+ minSdkForOrderedSplitPermissions =
+ maxOf(
+ minSdkForOrderedSplitPermissions,
+ splitPermissionTargetSdkMap.getOrDefault(perm, 0)
+ )
+ if (fgGroup.permissions[perm]?.isGranted == false) {
+ // If any of the requested permissions is not granted,
+ // needFgPermissions = true
+ needFgPermissions = true
+ // If any of the requested permission's UserSet is true and the
+ // permission is not granted, isFgUserSet = true.
+ if (fgGroup.permissions[perm]?.isUserSet == true) {
+ isFgUserSet = true
+ }
+ }
+ }
+ }
+ if (bgState?.group?.background?.isGranted == false) {
+ needBgPermissions = true
+ isBgUserSet = bgState.group.background.isUserSet
+ }
+
+ val buttonVisibilities = MutableList(NEXT_BUTTON) { false }
+ buttonVisibilities[ALLOW_BUTTON] = true
+ buttonVisibilities[DENY_BUTTON] = true
+ buttonVisibilities[ALLOW_ONE_TIME_BUTTON] =
+ PermissionMapping.supportsOneTimeGrant(groupName)
+ var message = RequestMessage.FG_MESSAGE
+ // Whether or not to use the foreground, background, or no detail message.
+ // null ==
+ var detailMessage = RequestMessage.NO_MESSAGE
+
+ if (
+ groupState.group.permGroupName == READ_MEDIA_VISUAL &&
+ shouldShowPhotoPickerPromptForApp(groupState.group)
+ ) {
+ // If the USER_SELECTED permission is user fixed and granted, or the app is
+ // only
+ // requesting USER_SELECTED, direct straight to photo picker
+ val userPerm = groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]
+ if (
+ (userPerm?.isUserFixed == true && userPerm.isGranted) ||
+ groupState.affectedPermissions ==
+ listOf(READ_MEDIA_VISUAL_USER_SELECTED)
+ ) {
+ requestInfos.add(RequestInfo(groupInfo, openPhotoPicker = true))
+ continue
+ } else if (isPartialStorageGrant(groupState.group)) {
+ // More photos dialog
+ message = RequestMessage.MORE_PHOTOS_MESSAGE
+ buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = false
+ buttonVisibilities[DENY_BUTTON] = false
+ buttonVisibilities[DONT_ALLOW_MORE_SELECTED_BUTTON] = true
+ } else {
+ // Standard photos dialog
+ buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
+ buttonVisibilities[DENY_BUTTON] = !isFgUserSet
+ }
+ buttonVisibilities[ALLOW_SELECTED_BUTTON] = true
+ buttonVisibilities[ALLOW_BUTTON] = false
+ buttonVisibilities[ALLOW_ALL_BUTTON] = true
+ } else if (
+ groupState.group.packageInfo.targetSdkVersion >=
+ minSdkForOrderedSplitPermissions
+ ) {
+ if (
+ isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group)
+ ) {
+ if (needFgPermissions) {
+ if (needBgPermissions) {
+ if (
+ groupState.group.permGroupName.equals(
+ Manifest.permission_group.CAMERA
+ ) ||
+ groupState.group.permGroupName.equals(
+ Manifest.permission_group.MICROPHONE
+ )
+ ) {
+ if (
+ groupState.group.packageInfo.targetSdkVersion >=
+ Build.VERSION_CODES.S
+ ) {
+ Log.e(
+ LOG_TAG,
"For S apps, background permissions must be " +
- "requested after foreground permissions are" +
- " already granted")
- value = null
- return
- } else {
- // Case: sdk < S, BG&FG mic/camera permission requested
- buttonVisibilities[ALLOW_BUTTON] = false
- buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true
- buttonVisibilities[DENY_BUTTON] = !isFgUserSet
- buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] =
+ "requested after foreground permissions are" +
+ " already granted"
+ )
+ value = null
+ return
+ } else {
+ // Case: sdk < S, BG&FG mic/camera permission requested
+ buttonVisibilities[ALLOW_BUTTON] = false
+ buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true
+ buttonVisibilities[DENY_BUTTON] = !isFgUserSet
+ buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] =
isFgUserSet
- if (needBgPermissions) {
- // Case: sdk < R, BG/FG permission requesting both
- message = RequestMessage.BG_MESSAGE
- detailMessage = RequestMessage.BG_MESSAGE
+ if (needBgPermissions) {
+ // Case: sdk < R, BG/FG permission requesting both
+ message = RequestMessage.BG_MESSAGE
+ detailMessage = RequestMessage.BG_MESSAGE
+ }
}
+ } else {
+ // Shouldn't be reached as background must be requested as a
+ // singleton
+ Log.e(
+ LOG_TAG,
+ "For R+ apps, background permissions must be " +
+ "requested after foreground permissions are " +
+ "already granted"
+ )
+ value = null
+ return
}
} else {
- // Shouldn't be reached as background must be requested as a
- // singleton
- Log.e(LOG_TAG, "For R+ apps, background permissions must be " +
- "requested after foreground permissions are already" +
- " granted")
- value = null
- return
+ buttonVisibilities[ALLOW_BUTTON] = false
+ buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true
+ buttonVisibilities[DENY_BUTTON] = !isFgUserSet
+ buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
+ }
+ } else if (needBgPermissions) {
+ // Case: sdk >= R, BG/FG permission requesting BG only
+ if (
+ storedState != null &&
+ storedState.containsKey(
+ getInstanceStateKey(
+ groupInfo.name,
+ groupState.isBackground
+ )
+ )
+ ) {
+ // If we're restoring state, and we had this groupInfo in our
+ // previous state, that means that we likely sent the user to
+ // settings already. Don't send the user back.
+ permGroupsToSkip.add(groupInfo.name)
+ groupState.state = STATE_SKIPPED
+ } else {
+ requestInfos.add(
+ RequestInfo(groupInfo, sendToSettingsImmediately = true)
+ )
}
+ continue
} else {
+ // Not reached as the permissions should be auto-granted
+ value = null
+ return
+ }
+ } else {
+ // Case: sdk >= R, Requesting normal permission
+ buttonVisibilities[DENY_BUTTON] = !isFgUserSet
+ buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
+ }
+ } else {
+ if (
+ isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group)
+ ) {
+ if (needFgPermissions) {
+ // Case: sdk < R, BG/FG permission requesting both or FG only
buttonVisibilities[ALLOW_BUTTON] = false
buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true
buttonVisibilities[DENY_BUTTON] = !isFgUserSet
buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
- }
- } else if (needBgPermissions) {
- // Case: sdk >= R, BG/FG permission requesting BG only
- if (storedState != null && storedState.containsKey(getInstanceStateKey(
- groupInfo.name, groupState.isBackground))) {
- // If we're restoring state, and we had this groupInfo in our
- // previous state, that means that we likely sent the user to
- // settings already. Don't send the user back.
- permGroupsToSkip.add(groupInfo.name)
- groupState.state = STATE_SKIPPED
+ if (needBgPermissions) {
+ // Case: sdk < R, BG/FG permission requesting both
+ message = RequestMessage.BG_MESSAGE
+ detailMessage = RequestMessage.BG_MESSAGE
+ }
+ } else if (needBgPermissions) {
+ // Case: sdk < R, BG/FG permission requesting BG only
+ if (!groupState.group.foreground.isGranted) {
+ Log.e(
+ LOG_TAG,
+ "Background permissions can't be requested " +
+ "solely before foreground permissions are granted."
+ )
+ value = null
+ return
+ }
+ message = RequestMessage.UPGRADE_MESSAGE
+ detailMessage = RequestMessage.UPGRADE_MESSAGE
+ buttonVisibilities[ALLOW_BUTTON] = false
+ buttonVisibilities[DENY_BUTTON] = false
+ buttonVisibilities[ALLOW_ONE_TIME_BUTTON] = false
+ if (groupState.group.isOneTime) {
+ buttonVisibilities[NO_UPGRADE_OT_BUTTON] = !isBgUserSet
+ buttonVisibilities[NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON] =
+ isBgUserSet
+ } else {
+ buttonVisibilities[NO_UPGRADE_BUTTON] = !isBgUserSet
+ buttonVisibilities[NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON] =
+ isBgUserSet
+ }
} else {
- requestInfos.add(RequestInfo(
- groupInfo, sendToSettingsImmediately = true))
+ // Not reached as the permissions should be auto-granted
+ value = null
+ return
}
- continue
} else {
- // Not reached as the permissions should be auto-granted
- value = null
- return
- }
- } else {
- // Case: sdk >= R, Requesting normal permission
- buttonVisibilities[DENY_BUTTON] = !isFgUserSet
- buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
- }
- } else {
- if (isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group)) {
- if (needFgPermissions) {
- // Case: sdk < R, BG/FG permission requesting both or FG only
- buttonVisibilities[ALLOW_BUTTON] = false
- buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true
- buttonVisibilities[DENY_BUTTON] = !isFgUserSet
- buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
- if (needBgPermissions) {
- // Case: sdk < R, BG/FG permission requesting both
- message = RequestMessage.BG_MESSAGE
- detailMessage = RequestMessage.BG_MESSAGE
- }
- } else if (needBgPermissions) {
- // Case: sdk < R, BG/FG permission requesting BG only
- if (!groupState.group.foreground.isGranted) {
- Log.e(LOG_TAG, "Background permissions can't be requested " +
- "solely before foreground permissions are granted.")
+ // If no permissions needed, do nothing
+ if (!needFgPermissions && !needBgPermissions) {
value = null
return
}
- message = RequestMessage.UPGRADE_MESSAGE
- detailMessage = RequestMessage.UPGRADE_MESSAGE
- buttonVisibilities[ALLOW_BUTTON] = false
- buttonVisibilities[DENY_BUTTON] = false
- buttonVisibilities[ALLOW_ONE_TIME_BUTTON] = false
- if (groupState.group.isOneTime) {
- buttonVisibilities[NO_UPGRADE_OT_BUTTON] = !isBgUserSet
- buttonVisibilities[NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON] =
- isBgUserSet
- } else {
- buttonVisibilities[NO_UPGRADE_BUTTON] = !isBgUserSet
- buttonVisibilities[NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON] =
- isBgUserSet
- }
- } else {
- // Not reached as the permissions should be auto-granted
- value = null
- return
- }
- } else {
- // If no permissions needed, do nothing
- if (!needFgPermissions && !needBgPermissions) {
- value = null
- return
+ // Case: sdk < R, Requesting normal permission
+ buttonVisibilities[DENY_BUTTON] = !isFgUserSet
+ buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
}
- // Case: sdk < R, Requesting normal permission
- buttonVisibilities[DENY_BUTTON] = !isFgUserSet
- buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
}
- }
- buttonVisibilities[LINK_TO_SETTINGS] =
- detailMessage != RequestMessage.NO_MESSAGE
-
- // Show location permission dialogs based on location permissions
- val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false }
- if (groupState.group.permGroupName == LOCATION && isLocationAccuracyEnabled() &&
- packageInfo.targetSdkVersion >= Build.VERSION_CODES.S) {
- if (needFgPermissions) {
- locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true
- if (fgState != null &&
- fgState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) {
- val coarseLocationPerm =
- groupState.group.allPermissions[ACCESS_COARSE_LOCATION]
- if (coarseLocationPerm?.isGranted == true) {
- // Upgrade flow
- locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] = true
- message = RequestMessage.FG_FINE_LOCATION_MESSAGE
- // If COARSE was granted one time, hide 'While in use' button
- if (coarseLocationPerm.isOneTime) {
- buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = false
- }
- } else {
- if (!fgState.affectedPermissions.contains(ACCESS_COARSE_LOCATION)) {
- Log.e(LOG_TAG, "ACCESS_FINE_LOCATION must be requested " +
- "with ACCESS_COARSE_LOCATION.")
- value = null
- return
- }
- // Normal flow with both Coarse and Fine locations
- locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS] = true
- // Steps to decide location accuracy default state
- // 1. If none of the FINE and COARSE isSelectedLocationAccuracy
- // flags is set, then use default precision from device config.
- // 2. Otherwise set to whichever isSelectedLocationAccuracy is true.
- val fineLocationPerm =
+ buttonVisibilities[LINK_TO_SETTINGS] =
+ detailMessage != RequestMessage.NO_MESSAGE
+
+ // Show location permission dialogs based on location permissions
+ val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false }
+ if (
+ groupState.group.permGroupName == LOCATION &&
+ isLocationAccuracyEnabledForApp(groupState.group)
+ ) {
+ if (needFgPermissions) {
+ locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true
+ if (
+ fgState != null &&
+ fgState.affectedPermissions.contains(ACCESS_FINE_LOCATION)
+ ) {
+ val coarseLocationPerm =
+ groupState.group.allPermissions[ACCESS_COARSE_LOCATION]
+ if (coarseLocationPerm?.isGranted == true) {
+ // Upgrade flow
+ locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] = true
+ message = RequestMessage.FG_FINE_LOCATION_MESSAGE
+ // If COARSE was granted one time, hide 'While in use' button
+ if (coarseLocationPerm.isOneTime) {
+ buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = false
+ }
+ } else {
+ if (
+ !fgState.affectedPermissions.contains(
+ ACCESS_COARSE_LOCATION
+ )
+ ) {
+ Log.e(
+ LOG_TAG,
+ "ACCESS_FINE_LOCATION must be requested " +
+ "with ACCESS_COARSE_LOCATION."
+ )
+ value = null
+ return
+ }
+ // Normal flow with both Coarse and Fine locations
+ locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS] = true
+ // Steps to decide location accuracy default state
+ // 1. If none of the FINE and COARSE isSelectedLocationAccuracy
+ // flags is set, then use default precision from device
+ // config.
+ // 2. Otherwise set to whichever isSelectedLocationAccuracy is
+ // true.
+ val fineLocationPerm =
groupState.group.allPermissions[ACCESS_FINE_LOCATION]
- if (coarseLocationPerm?.isSelectedLocationAccuracy == false &&
- fineLocationPerm?.isSelectedLocationAccuracy == false) {
- if (getDefaultPrecision()) {
- locationVisibilities[FINE_RADIO_BUTTON] = true
- } else {
+ if (
+ coarseLocationPerm?.isSelectedLocationAccuracy == false &&
+ fineLocationPerm?.isSelectedLocationAccuracy == false
+ ) {
+ if (getDefaultPrecision()) {
+ locationVisibilities[FINE_RADIO_BUTTON] = true
+ } else {
+ locationVisibilities[COARSE_RADIO_BUTTON] = true
+ }
+ } else if (
+ coarseLocationPerm?.isSelectedLocationAccuracy == true
+ ) {
locationVisibilities[COARSE_RADIO_BUTTON] = true
+ } else {
+ locationVisibilities[FINE_RADIO_BUTTON] = true
}
- } else if (coarseLocationPerm?.isSelectedLocationAccuracy == true) {
- locationVisibilities[COARSE_RADIO_BUTTON] = true
- } else {
- locationVisibilities[FINE_RADIO_BUTTON] = true
}
+ } else if (
+ fgState != null &&
+ fgState.affectedPermissions.contains(ACCESS_COARSE_LOCATION)
+ ) {
+ // Request Coarse only
+ locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY] = true
+ message = RequestMessage.FG_COARSE_LOCATION_MESSAGE
}
- } else if (fgState != null && fgState.affectedPermissions
- .contains(ACCESS_COARSE_LOCATION)) {
- // Request Coarse only
- locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY] = true
- message = RequestMessage.FG_COARSE_LOCATION_MESSAGE
}
}
- }
- if (SdkLevel.isAtLeastT()) {
- // If app is T+, requests for the STORAGE group are ignored
- if (packageInfo.targetSdkVersion > Build.VERSION_CODES.S_V2 &&
- groupState.group.permGroupName == Manifest.permission_group.STORAGE) {
- continue
- }
- // If app is <T and requests STORAGE, grant dialogs has special text
- if (groupState.group.permGroupName in
- PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) {
- if (packageInfo.targetSdkVersion < Build.VERSION_CODES.Q) {
- message = RequestMessage.STORAGE_SUPERGROUP_MESSAGE_PRE_Q
- } else if (packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2) {
- message = RequestMessage.STORAGE_SUPERGROUP_MESSAGE_Q_TO_S
+ if (SdkLevel.isAtLeastT()) {
+ // If app is T+, requests for the STORAGE group are ignored
+ if (
+ packageInfo.targetSdkVersion > Build.VERSION_CODES.S_V2 &&
+ groupState.group.permGroupName == Manifest.permission_group.STORAGE
+ ) {
+ continue
+ }
+ // If app is <T and requests STORAGE, grant dialogs has special text
+ if (
+ groupState.group.permGroupName in
+ PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS
+ ) {
+ if (packageInfo.targetSdkVersion < Build.VERSION_CODES.Q) {
+ message = RequestMessage.STORAGE_SUPERGROUP_MESSAGE_PRE_Q
+ } else if (packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2) {
+ message = RequestMessage.STORAGE_SUPERGROUP_MESSAGE_Q_TO_S
+ }
}
}
- }
-
- val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel
- val showPermissionRationale =
- shouldShowPermissionRationale(safetyLabel, groupState.group.permGroupName)
- buttonVisibilities[LINK_TO_PERMISSION_RATIONALE] = showPermissionRationale
-
- requestInfos.add(RequestInfo(
- groupInfo,
- buttonVisibilities,
- locationVisibilities,
- message,
- detailMessage))
- }
- sortPermissionGroups(requestInfos)
+ val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel
+ val showPermissionRationale =
+ shouldShowPermissionRationale(safetyLabel, groupState.group.permGroupName)
+ buttonVisibilities[LINK_TO_PERMISSION_RATIONALE] = showPermissionRationale
+
+ requestInfos.add(
+ RequestInfo(
+ groupInfo,
+ buttonVisibilities,
+ locationVisibilities,
+ message,
+ detailMessage
+ )
+ )
+ }
- value = if (requestInfos.any { it.sendToSettingsImmediately } &&
- requestInfos.size > 1) {
- Log.e(LOG_TAG, "For R+ apps, background permissions must be requested " +
- "individually")
- null
- } else {
- requestInfos
+ sortPermissionGroups(requestInfos)
+
+ value =
+ if (
+ requestInfos.any { it.sendToSettingsImmediately } && requestInfos.size > 1
+ ) {
+ Log.e(
+ LOG_TAG,
+ "For R+ apps, background permissions must be requested " +
+ "individually"
+ )
+ null
+ } else {
+ requestInfos
+ }
}
}
- }
fun sortPermissionGroups(requestInfos: MutableList<RequestInfo>) {
requestInfos.sortWith { rhs, lhs ->
@@ -618,8 +706,8 @@ class GrantPermissionsViewModel(
val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
if (rhsHasOneTime && !lhsHasOneTime) {
-1
- } else if ((!rhsHasOneTime && lhsHasOneTime) ||
- isHealthPermissionGroup(rhs.groupName)
+ } else if (
+ (!rhsHasOneTime && lhsHasOneTime) || Utils.isHealthPermissionGroup(rhs.groupName)
) {
1
} else {
@@ -636,34 +724,37 @@ class GrantPermissionsViewModel(
return false
}
- val purposes = SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(safetyLabel,
- permissionGroupName)
+ val purposes =
+ SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(safetyLabel, permissionGroupName)
return purposes.isNotEmpty()
}
- /**
- * Converts a list of LightAppPermGroups into a list of GroupStates
- */
+ /** Converts a list of LightAppPermGroups into a list of GroupStates */
private fun getRequiredGroupStates(
groups: List<LightAppPermGroup>
): MutableMap<Pair<String, Boolean>, GroupState> {
val groupStates = mutableMapOf<Pair<String, Boolean>, GroupState>()
- val filteredPermissions = unfilteredAffectedPermissions.filter { perm ->
- val group = getGroupWithPerm(perm, groups)
- group != null && isPermissionGrantableAndNotFixed(perm, group)
- }
+ val filteredPermissions =
+ unfilteredAffectedPermissions.filter { perm ->
+ val group = getGroupWithPerm(perm, groups)
+ group != null && isPermissionGrantableAndNotFixed(perm, group)
+ }
for (perm in filteredPermissions) {
val group = getGroupWithPerm(perm, groups)!!
val isBackground = perm in group.backgroundPermNames
- val groupStateInfo = groupStates.getOrPut(group.permGroupName to isBackground) {
- GroupState(group, isBackground)
- }
+ val groupStateInfo =
+ groupStates.getOrPut(group.permGroupName to isBackground) {
+ GroupState(group, isBackground)
+ }
var currGroupState = groupStateInfo.state
if (storedState != null && currGroupState != STATE_UNKNOWN) {
- currGroupState = storedState.getInt(getInstanceStateKey(group.permGroupName,
- isBackground), STATE_UNKNOWN)
+ currGroupState =
+ storedState.getInt(
+ getInstanceStateKey(group.permGroupName, isBackground),
+ STATE_UNKNOWN
+ )
}
val otherGroupPermissions = filteredPermissions.filter { it in group.permissions }
@@ -694,7 +785,6 @@ class GrantPermissionsViewModel(
* target an SDK before the split, this method automatically adds the split off permission.
*
* @param perm The requested permission
- *
* @return The actually requested permissions
*/
private fun computeAffectedPermissions(
@@ -742,8 +832,10 @@ class GrantPermissionsViewModel(
// If the permission is restricted it does not show in the UI and
// is not added to the group at all, so check that first.
if (perm in group.packageInfo.requestedPermissions && perm !in group.permissions) {
- reportRequestResult(perm,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION)
+ reportRequestResult(
+ perm,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION
+ )
return false
}
@@ -751,11 +843,12 @@ class GrantPermissionsViewModel(
return !(group.permissions[perm]?.isUserFixed ?: true)
}
- val subGroup = if (perm in group.backgroundPermNames) {
- group.background
- } else {
- group.foreground
- }
+ val subGroup =
+ if (perm in group.backgroundPermNames) {
+ group.background
+ } else {
+ group.foreground
+ }
val lightPermission = group.permissions[perm] ?: return false
@@ -764,7 +857,7 @@ class GrantPermissionsViewModel(
// Skip showing groups that we know cannot be granted.
return false
} else if (subGroup.isUserFixed) {
- if (perm == ACCESS_COARSE_LOCATION) {
+ if (perm == ACCESS_COARSE_LOCATION && isLocationAccuracyEnabledForApp(group)) {
val coarsePerm = group.permissions[perm]
if (coarsePerm != null && !coarsePerm.isUserFixed) {
// If the location group is user fixed but ACCESS_COARSE_LOCATION is not, then
@@ -772,18 +865,24 @@ class GrantPermissionsViewModel(
// is still grantable.
return true
}
- } else if (perm in getPartialStorageGrantPermissionsForGroup(group) &&
- lightPermission.isGranted) {
+ } else if (
+ perm in getPartialStorageGrantPermissionsForGroup(group) &&
+ lightPermission.isGranted
+ ) {
// If a partial storage permission is granted as fixed, we should immediately show
// the photo picker
return true
}
- reportRequestResult(perm,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED)
+ reportRequestResult(
+ perm,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED
+ )
return false
} else if (subGroup.isPolicyFixed && !subGroup.isGranted || lightPermission.isPolicyFixed) {
- reportRequestResult(perm,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED)
+ reportRequestResult(
+ perm,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED
+ )
return false
}
@@ -800,15 +899,18 @@ class GrantPermissionsViewModel(
return policyState
}
- if (perm == POST_NOTIFICATIONS &&
- packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2 &&
- group.foreground.isUserSet) {
+ if (
+ perm == POST_NOTIFICATIONS &&
+ packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2 &&
+ group.foreground.isUserSet
+ ) {
return STATE_SKIPPED
} else if (perm == READ_MEDIA_VISUAL_USER_SELECTED) {
val partialPerms = getPartialStorageGrantPermissionsForGroup(group)
- val otherRequestedPerms = unfilteredAffectedPermissions.filter { otherPerm ->
- otherPerm in group.permissions && otherPerm !in partialPerms
- }
+ val otherRequestedPerms =
+ unfilteredAffectedPermissions.filter { otherPerm ->
+ otherPerm in group.permissions && otherPerm !in partialPerms
+ }
if (otherRequestedPerms.isEmpty()) {
// If the app requested USER_SELECTED while not supporting the photo picker, or if
// the app explicitly requested only USER_SELECTED and/or ACCESS_MEDIA_LOCATION,
@@ -819,37 +921,48 @@ class GrantPermissionsViewModel(
val isBackground = perm in group.backgroundPermNames
- val hasForegroundRequest = groupRequestedPermissions.any {
- it !in group.backgroundPermNames
- }
+ val hasForegroundRequest =
+ groupRequestedPermissions.any { it !in group.backgroundPermNames }
// Do not attempt to grant background access if foreground access is not either already
// granted or requested
- if (isBackground && !group.foreground.allowFullGroupGrant &&
- !hasForegroundRequest) {
- Log.w(LOG_TAG, "Cannot grant $perm as the matching foreground permission is not " +
- "already granted.")
- val affectedPermissions = groupRequestedPermissions.filter {
- it in group.backgroundPermNames
- }
- reportRequestResult(affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED)
+ if (isBackground && !group.foreground.allowFullGroupGrant && !hasForegroundRequest) {
+ Log.w(
+ LOG_TAG,
+ "Cannot grant $perm as the matching foreground permission is not " +
+ "already granted."
+ )
+ val affectedPermissions =
+ groupRequestedPermissions.filter { it in group.backgroundPermNames }
+ reportRequestResult(
+ affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED
+ )
return STATE_SKIPPED
}
- if ((isBackground && group.background.allowFullGroupGrant ||
- !isBackground && group.foreground.allowFullGroupGrant) &&
- canAutoGrantWholeGroup(group)) {
+ if (
+ (isBackground && group.background.allowFullGroupGrant ||
+ !isBackground && group.foreground.allowFullGroupGrant) &&
+ canAutoGrantWholeGroup(group)
+ ) {
if (group.permissions[perm]?.isGranted == false) {
if (isBackground) {
grantBackgroundRuntimePermissions(app, group, listOf(perm))
} else {
grantForegroundRuntimePermissions(app, group, listOf(perm), group.isOneTime)
}
- KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to false,
- FLAG_PERMISSION_USER_FIXED to false, filterPermissions = listOf(perm))
- reportRequestResult(perm,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED)
+ KotlinUtils.setGroupFlags(
+ app,
+ group,
+ FLAG_PERMISSION_USER_SET to false,
+ FLAG_PERMISSION_USER_FIXED to false,
+ filterPermissions = listOf(perm)
+ )
+ reportRequestResult(
+ perm,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED
+ )
}
return if (storedState == null) {
@@ -862,14 +975,17 @@ class GrantPermissionsViewModel(
}
/**
- * Determines if remaining permissions in the group can be auto granted based on
- * granted permissions in the group.
+ * Determines if remaining permissions in the group can be auto granted based on granted
+ * permissions in the group.
*/
private fun canAutoGrantWholeGroup(group: LightAppPermGroup): Boolean {
// If FINE location is not granted, do not grant it automatically when COARSE
// location is already granted.
- if (group.permGroupName == LOCATION &&
- group.allPermissions[ACCESS_FINE_LOCATION]?.isGranted == false) {
+ if (
+ group.permGroupName == LOCATION &&
+ isLocationAccuracyEnabledForApp(group) &&
+ group.allPermissions[ACCESS_FINE_LOCATION]?.isGranted == false
+ ) {
return false
}
// If READ_MEDIA_VISUAL_USER_SELECTED is the only permission in the group that is granted,
@@ -881,20 +997,34 @@ class GrantPermissionsViewModel(
}
/**
- * A partial storage grant happens when:
- * An app which doesn't support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED granted, or
- * An app which does support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED and/or
- * ACCESS_MEDIA_LOCATION granted
+ * A partial storage grant happens when: An app which doesn't support the photo picker has
+ * READ_MEDIA_VISUAL_USER_SELECTED granted, or An app which does support the photo picker has
+ * READ_MEDIA_VISUAL_USER_SELECTED and/or ACCESS_MEDIA_LOCATION granted
*/
private fun isPartialStorageGrant(group: LightAppPermGroup): Boolean {
- if (!KotlinUtils.isPhotoPickerPromptEnabled() || group.permGroupName != READ_MEDIA_VISUAL) {
+ if (!isPhotoPickerPromptSupported() || group.permGroupName != READ_MEDIA_VISUAL) {
return false
}
val partialPerms = getPartialStorageGrantPermissionsForGroup(group)
- return group.isGranted && group.permissions.values.all {
- it.name in partialPerms || (it.name !in partialPerms && !it.isGranted)
+ return group.isGranted &&
+ group.permissions.values.all {
+ it.name in partialPerms || (it.name !in partialPerms && !it.isGranted)
+ }
+ }
+
+ private fun shouldShowPhotoPickerPromptForApp(group: LightAppPermGroup): Boolean {
+ if (
+ !isPhotoPickerPromptEnabled() ||
+ group.packageInfo.targetSdkVersion < Build.VERSION_CODES.TIRAMISU
+ ) {
+ return false
}
+ if (group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ return true
+ }
+ val userSelectedPerm = group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] ?: return false
+ return !userSelectedPerm.isImplicit
}
private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int {
@@ -903,36 +1033,54 @@ class GrantPermissionsViewModel(
var state = STATE_UNKNOWN
when (permissionPolicy) {
DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT -> {
- if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission(
- app, perm, user.identifier)) {
+ if (
+ AdminRestrictedPermissionsUtils.mayAdminGrantPermission(
+ app,
+ perm,
+ user.identifier
+ )
+ ) {
if (isBackground) {
grantBackgroundRuntimePermissions(app, group, listOf(perm))
} else {
grantForegroundRuntimePermissions(app, group, listOf(perm))
}
- KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true,
- FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false,
- filterPermissions = listOf(perm))
+ KotlinUtils.setGroupFlags(
+ app,
+ group,
+ FLAG_PERMISSION_POLICY_FIXED to true,
+ FLAG_PERMISSION_USER_SET to false,
+ FLAG_PERMISSION_USER_FIXED to false,
+ filterPermissions = listOf(perm)
+ )
state = STATE_ALLOWED
skipGroup = true
getAutoGrantNotifier().onPermissionAutoGranted(perm)
- reportRequestResult(perm,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED)
+ reportRequestResult(
+ perm,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED
+ )
}
}
-
DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY -> {
if (group.permissions[perm]?.isPolicyFixed == false) {
- KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true,
- FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false,
- filterPermissions = listOf(perm))
+ KotlinUtils.setGroupFlags(
+ app,
+ group,
+ FLAG_PERMISSION_POLICY_FIXED to true,
+ FLAG_PERMISSION_USER_SET to false,
+ FLAG_PERMISSION_USER_FIXED to false,
+ filterPermissions = listOf(perm)
+ )
}
state = STATE_DENIED
skipGroup = true
- reportRequestResult(perm,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED)
+ reportRequestResult(
+ perm,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED
+ )
}
}
if (skipGroup && storedState == null) {
@@ -967,12 +1115,14 @@ class GrantPermissionsViewModel(
}
// If this is a legacy app, and a storage group is requested: request all storage groups
- if (!alreadyRequestedStorageGroupsIfNeeded &&
- groupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS &&
- packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2) {
+ if (
+ !alreadyRequestedStorageGroupsIfNeeded &&
+ groupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS &&
+ packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2
+ ) {
for (storageGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) {
- val groupPerms = appPermGroupLiveDatas[storageGroupName]
- ?.value?.allPermissions?.keys?.toList()
+ val groupPerms =
+ appPermGroupLiveDatas[storageGroupName]?.value?.allPermissions?.keys?.toList()
onPermissionGrantResult(storageGroupName, groupPerms, result, true)
}
return
@@ -983,13 +1133,17 @@ class GrantPermissionsViewModel(
when (result) {
CANCELED -> {
if (foregroundGroupState != null) {
- reportRequestResult(foregroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED)
+ reportRequestResult(
+ foregroundGroupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED
+ )
foregroundGroupState.state = STATE_SKIPPED
}
if (backgroundGroupState != null) {
- reportRequestResult(backgroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED)
+ reportRequestResult(
+ backgroundGroupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED
+ )
backgroundGroupState.state = STATE_SKIPPED
}
requestInfosLiveData.update()
@@ -997,67 +1151,108 @@ class GrantPermissionsViewModel(
}
GRANTED_ALWAYS -> {
if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = true, isOneTime = false,
- doNotAskAgain = false)
+ onPermissionGrantResultSingleState(
+ foregroundGroupState,
+ affectedForegroundPermissions,
+ granted = true,
+ isOneTime = false,
+ doNotAskAgain = false
+ )
}
if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = true, isOneTime = false,
- doNotAskAgain = false)
+ onPermissionGrantResultSingleState(
+ backgroundGroupState,
+ affectedForegroundPermissions,
+ granted = true,
+ isOneTime = false,
+ doNotAskAgain = false
+ )
}
}
GRANTED_FOREGROUND_ONLY -> {
if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = true, isOneTime = false,
- doNotAskAgain = false)
+ onPermissionGrantResultSingleState(
+ foregroundGroupState,
+ affectedForegroundPermissions,
+ granted = true,
+ isOneTime = false,
+ doNotAskAgain = false
+ )
}
if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = false)
+ onPermissionGrantResultSingleState(
+ backgroundGroupState,
+ affectedForegroundPermissions,
+ granted = false,
+ isOneTime = false,
+ doNotAskAgain = false
+ )
}
}
GRANTED_ONE_TIME -> {
if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = true, isOneTime = true,
- doNotAskAgain = false)
+ onPermissionGrantResultSingleState(
+ foregroundGroupState,
+ affectedForegroundPermissions,
+ granted = true,
+ isOneTime = true,
+ doNotAskAgain = false
+ )
}
if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = true,
- doNotAskAgain = false)
+ onPermissionGrantResultSingleState(
+ backgroundGroupState,
+ affectedForegroundPermissions,
+ granted = false,
+ isOneTime = true,
+ doNotAskAgain = false
+ )
}
}
- GRANTED_USER_SELECTED, DENIED_MORE -> {
+ GRANTED_USER_SELECTED,
+ DENIED_MORE -> {
if (foregroundGroupState != null) {
grantUserSelectedVisualGroupPermissions(foregroundGroupState)
}
}
DENIED -> {
if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = false)
+ onPermissionGrantResultSingleState(
+ foregroundGroupState,
+ affectedForegroundPermissions,
+ granted = false,
+ isOneTime = false,
+ doNotAskAgain = false
+ )
}
if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = false)
+ onPermissionGrantResultSingleState(
+ backgroundGroupState,
+ affectedForegroundPermissions,
+ granted = false,
+ isOneTime = false,
+ doNotAskAgain = false
+ )
}
}
DENIED_DO_NOT_ASK_AGAIN -> {
if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = true)
+ onPermissionGrantResultSingleState(
+ foregroundGroupState,
+ affectedForegroundPermissions,
+ granted = false,
+ isOneTime = false,
+ doNotAskAgain = true
+ )
}
if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = true)
+ onPermissionGrantResultSingleState(
+ backgroundGroupState,
+ affectedForegroundPermissions,
+ granted = false,
+ isOneTime = false,
+ doNotAskAgain = true
+ )
}
}
}
@@ -1067,32 +1262,60 @@ class GrantPermissionsViewModel(
val userSelectedPerm =
groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] ?: return
if (userSelectedPerm.isImplicit) {
- val nonSelectedPerms = groupState.group.permissions.keys
- .filter { it != READ_MEDIA_VISUAL_USER_SELECTED }
+ val nonSelectedPerms =
+ groupState.group.permissions.keys.filter { it != READ_MEDIA_VISUAL_USER_SELECTED }
// If the permission is implicit, grant USER_SELECTED as user set, and all other
// permissions as one time, and without app ops.
- grantForegroundRuntimePermissions(app, groupState.group,
- listOf(READ_MEDIA_VISUAL_USER_SELECTED))
- grantForegroundRuntimePermissions(app, groupState.group,
- nonSelectedPerms, isOneTime = true, userFixed = false, withoutAppOps = true)
- val appPermGroup = AppPermissionGroup.create(app, packageName,
- groupState.group.permGroupName, groupState.group.userHandle, false)
+ grantForegroundRuntimePermissions(
+ app,
+ groupState.group,
+ listOf(READ_MEDIA_VISUAL_USER_SELECTED)
+ )
+ grantForegroundRuntimePermissions(
+ app,
+ groupState.group,
+ nonSelectedPerms,
+ isOneTime = true,
+ userFixed = false,
+ withoutAppOps = true
+ )
+ val appPermGroup =
+ AppPermissionGroup.create(
+ app,
+ packageName,
+ groupState.group.permGroupName,
+ groupState.group.userHandle,
+ false
+ )
appPermGroup.setSelfRevoked()
appPermGroup.persistChanges(false, null, nonSelectedPerms.toSet())
} else {
- val partialPerms = getPartialStorageGrantPermissionsForGroup(groupState.group).filter {
- it in groupState.affectedPermissions
- }
+ val partialPerms =
+ getPartialStorageGrantPermissionsForGroup(groupState.group).filter {
+ it in groupState.affectedPermissions
+ }
val nonSelectedPerms = groupState.affectedPermissions.filter { it !in partialPerms }
val setUserFixed = userSelectedPerm.isUserFixed || userSelectedPerm.isUserSet
- grantForegroundRuntimePermissions(app, groupState.group,
- partialPerms.toList(), userFixed = setUserFixed)
- revokeForegroundRuntimePermissions(app, groupState.group,
- userFixed = setUserFixed, oneTime = false, filterPermissions = nonSelectedPerms)
+ grantForegroundRuntimePermissions(
+ app,
+ groupState.group,
+ partialPerms.toList(),
+ userFixed = setUserFixed
+ )
+ revokeForegroundRuntimePermissions(
+ app,
+ groupState.group,
+ userFixed = setUserFixed,
+ oneTime = false,
+ filterPermissions = nonSelectedPerms
+ )
}
groupState.state = STATE_ALLOWED
- reportButtonClickResult(groupState, true,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED)
+ reportButtonClickResult(
+ groupState,
+ true,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED
+ )
}
@SuppressLint("NewApi")
@@ -1109,38 +1332,50 @@ class GrantPermissionsViewModel(
}
val result: Int
if (granted) {
- result = if (isOneTime) {
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME
- } else {
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED
- }
+ result =
+ if (isOneTime) {
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME
+ } else {
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED
+ }
var affectedPermissions: List<String> = groupState.affectedPermissions
if (groupState.isBackground) {
grantBackgroundRuntimePermissions(app, groupState.group, affectedPermissions)
} else {
if (affectedForegroundPermissions == null) {
- grantForegroundRuntimePermissions(app, groupState.group,
- affectedPermissions, isOneTime)
+ grantForegroundRuntimePermissions(
+ app,
+ groupState.group,
+ affectedPermissions,
+ isOneTime
+ )
// This prevents weird flag state when app targetSDK switches from S+ to R-
if (groupState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) {
- KotlinUtils.setFlagsWhenLocationAccuracyChanged(
- app, groupState.group, true)
+ KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, groupState.group, true)
}
} else {
affectedPermissions = affectedForegroundPermissions
- val newGroup = grantForegroundRuntimePermissions(app,
- groupState.group, affectedPermissions, isOneTime)
+ val newGroup =
+ grantForegroundRuntimePermissions(
+ app,
+ groupState.group,
+ affectedPermissions,
+ isOneTime
+ )
if (!isOneTime || newGroup.isOneTime) {
- KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, newGroup,
- affectedForegroundPermissions.contains(ACCESS_FINE_LOCATION))
+ KotlinUtils.setFlagsWhenLocationAccuracyChanged(
+ app,
+ newGroup,
+ affectedForegroundPermissions.contains(ACCESS_FINE_LOCATION)
+ )
}
}
}
val shouldDenyFullGroupGrant =
groupState.group.isPlatformPermissionGroup &&
- affectedPermissions.none {
- groupState.group.permissions[it]?.isPlatformOrSystem == true
- }
+ affectedPermissions.none {
+ groupState.group.permissions[it]?.isPlatformOrSystem == true
+ }
groupState.state =
if (shouldDenyFullGroupGrant) {
STATE_UNKNOWN
@@ -1149,25 +1384,40 @@ class GrantPermissionsViewModel(
}
} else {
if (groupState.isBackground) {
- revokeBackgroundRuntimePermissions(app, groupState.group,
- userFixed = doNotAskAgain, filterPermissions = groupState.affectedPermissions)
+ revokeBackgroundRuntimePermissions(
+ app,
+ groupState.group,
+ userFixed = doNotAskAgain,
+ filterPermissions = groupState.affectedPermissions
+ )
} else {
- if (affectedForegroundPermissions == null ||
- affectedForegroundPermissions.contains(ACCESS_COARSE_LOCATION)) {
- revokeForegroundRuntimePermissions(app, groupState.group,
+ if (
+ affectedForegroundPermissions == null ||
+ affectedForegroundPermissions.contains(ACCESS_COARSE_LOCATION)
+ ) {
+ revokeForegroundRuntimePermissions(
+ app,
+ groupState.group,
userFixed = doNotAskAgain,
- filterPermissions = groupState.affectedPermissions, oneTime = isOneTime)
+ filterPermissions = groupState.affectedPermissions,
+ oneTime = isOneTime
+ )
} else {
- revokeForegroundRuntimePermissions(app, groupState.group,
+ revokeForegroundRuntimePermissions(
+ app,
+ groupState.group,
userFixed = doNotAskAgain,
- filterPermissions = affectedForegroundPermissions, oneTime = isOneTime)
+ filterPermissions = affectedForegroundPermissions,
+ oneTime = isOneTime
+ )
}
}
- result = if (doNotAskAgain) {
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
- } else {
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED
- }
+ result =
+ if (doNotAskAgain) {
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
+ } else {
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED
+ }
groupState.state = STATE_DENIED
}
reportButtonClickResult(groupState, granted, result)
@@ -1177,8 +1427,12 @@ class GrantPermissionsViewModel(
reportRequestResult(groupState.affectedPermissions, result)
// group state has changed, reload liveData
requestInfosLiveData.update()
- PermissionDecisionStorageImpl.recordPermissionDecision(app.applicationContext,
- packageName, groupState.group.permGroupName, granted)
+ PermissionDecisionStorageImpl.recordPermissionDecision(
+ app.applicationContext,
+ packageName,
+ groupState.group.permGroupName,
+ granted
+ )
PermissionChangeStorageImpl.recordPermissionChange(packageName)
if (granted) {
startDrivingDecisionReminderServiceIfNecessary(groupState.group.permGroupName)
@@ -1194,7 +1448,10 @@ class GrantPermissionsViewModel(
return
}
DrivingDecisionReminderService.startServiceIfCurrentlyRestricted(
- Utils.getUserContext(app, user), packageName, permGroupName)
+ Utils.getUserContext(app, user),
+ packageName,
+ permGroupName
+ )
}
private fun getGroupWithPerm(
@@ -1218,12 +1475,13 @@ class GrantPermissionsViewModel(
internal var state: Int = STATE_UNKNOWN
) {
override fun toString(): String {
- val stateStr: String = when (state) {
- STATE_UNKNOWN -> "unknown"
- STATE_ALLOWED -> "granted"
- STATE_DENIED -> "denied"
- else -> "skipped"
- }
+ val stateStr: String =
+ when (state) {
+ STATE_UNKNOWN -> "unknown"
+ STATE_ALLOWED -> "granted"
+ STATE_DENIED -> "denied"
+ else -> "skipped"
+ }
return "${group.permGroupName} $isBackground $stateStr $affectedPermissions"
}
}
@@ -1242,19 +1500,32 @@ class GrantPermissionsViewModel(
*/
private fun reportRequestResult(permission: String, result: Int) {
val isImplicit = permission !in requestedPermissions
- val isPermissionRationaleShown = shouldShowPermissionRationale(
- safetyLabelInfoLiveData?.value?.safetyLabel,
- PermissionMapping.getGroupOfPlatformPermission(permission))
-
- Log.v(LOG_TAG, "Permission grant result requestId=$sessionId " +
- "callingUid=${packageInfo.uid} callingPackage=$packageName permission=$permission " +
- "isImplicit=$isImplicit result=$result " +
- "isPermissionRationaleShown=$isPermissionRationaleShown")
+ val isPermissionRationaleShown =
+ shouldShowPermissionRationale(
+ safetyLabelInfoLiveData?.value?.safetyLabel,
+ PermissionMapping.getGroupOfPlatformPermission(permission)
+ )
+
+ Log.v(
+ LOG_TAG,
+ "Permission grant result requestId=$sessionId " +
+ "callingUid=${packageInfo.uid} " +
+ "callingPackage=$packageName " +
+ "permission=$permission " +
+ "isImplicit=$isImplicit result=$result " +
+ "isPermissionRationaleShown=$isPermissionRationaleShown"
+ )
PermissionControllerStatsLog.write(
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED, sessionId,
- packageInfo.uid, packageName, permission, isImplicit, result,
- isPermissionRationaleShown)
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED,
+ sessionId,
+ packageInfo.uid,
+ packageName,
+ permission,
+ isImplicit,
+ result,
+ isPermissionRationaleShown
+ )
}
/**
@@ -1268,13 +1539,14 @@ class GrantPermissionsViewModel(
val (groupName, isBackground) = groupKey
outState.putInt(getInstanceStateKey(groupName, isBackground), groupState.state)
}
+ activityResultCallback?.let { outState.putInt(SAVED_REQUEST_CODE_KEY, it.requestCode) }
}
/**
* Determine if the activity should return permission state to the caller
*
* @return Whether or not state should be returned. False only if the package is pre-M, true
- * otherwise.
+ * otherwise.
*/
fun shouldReturnPermissionState(): Boolean {
return if (packageInfoLiveData.value != null) {
@@ -1283,33 +1555,45 @@ class GrantPermissionsViewModel(
// Should not be reached, as this method shouldn't be called before data is passed to
// the activity for the first time
try {
- Utils.getUserContext(app, user).packageManager
- .getApplicationInfo(packageName, 0).targetSdkVersion >= Build.VERSION_CODES.M
+ Utils.getUserContext(app, user)
+ .packageManager
+ .getApplicationInfo(packageName, 0)
+ .targetSdkVersion >= Build.VERSION_CODES.M
} catch (e: PackageManager.NameNotFoundException) {
true
}
}
}
- @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- private fun isHealthPermissionGroup(permGroupName: String): Boolean {
- return SdkLevel.isAtLeastU() && HEALTH_PERMISSION_GROUP.equals(permGroupName)
+ fun handleCallback(data: Intent?, requestCode: Int) {
+ val currCallback = activityResultCallback
+ if (currCallback == null || requestCode != currCallback.requestCode) {
+ return
+ }
+ currCallback.consumer.accept(data)
+ activityResultCallback = null
}
fun handleHealthConnectPermissions(activity: Activity) {
if (activityResultCallback == null) {
- activityResultCallback = Consumer {
- permGroupsToSkip.add(HEALTH_PERMISSION_GROUP)
- requestInfosLiveData.update()
- }
- val healthPermissions = unfilteredAffectedPermissions.filter { permission ->
- isHealthPermission(activity, permission)
- }.toTypedArray()
- val intent: Intent = Intent(ACTION_REQUEST_HEALTH_PERMISSIONS)
- .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
- .putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, healthPermissions)
- .putExtra(Intent.EXTRA_USER, Process.myUserHandle())
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ activityResultCallback =
+ ResultCallback(
+ {
+ permGroupsToSkip.add(HEALTH_PERMISSION_GROUP)
+ requestInfosLiveData.update()
+ },
+ APP_PERMISSION_REQUEST_CODE
+ )
+ val healthPermissions =
+ unfilteredAffectedPermissions
+ .filter { permission -> isHealthPermission(activity, permission) }
+ .toTypedArray()
+ val intent: Intent =
+ Intent(ACTION_REQUEST_HEALTH_PERMISSIONS)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+ .putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, healthPermissions)
+ .putExtra(Intent.EXTRA_USER, Process.myUserHandle())
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE)
}
}
@@ -1322,56 +1606,71 @@ class GrantPermissionsViewModel(
*/
fun sendDirectlyToSettings(activity: Activity, groupName: String) {
if (activityResultCallback == null) {
- activityResultCallback = Consumer { data ->
- if (data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) == null) {
- // User didn't interact, count against rate limit
- val group = groupStates[groupName to false]?.group
- ?: groupStates[groupName to true]?.group ?: return@Consumer
- if (group.background.isUserSet) {
- KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_FIXED to true,
- filterPermissions = group.backgroundPermNames)
- } else {
- KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to true,
- filterPermissions = group.backgroundPermNames)
- }
- }
+ activityResultCallback =
+ ResultCallback(
+ Consumer { data ->
+ if (data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) == null) {
+ // User didn't interact, count against rate limit
+ val group =
+ groupStates[groupName to false]?.group
+ ?: groupStates[groupName to true]?.group ?: return@Consumer
+ if (group.background.isUserSet) {
+ KotlinUtils.setGroupFlags(
+ app,
+ group,
+ FLAG_PERMISSION_USER_FIXED to true,
+ filterPermissions = group.backgroundPermNames
+ )
+ } else {
+ KotlinUtils.setGroupFlags(
+ app,
+ group,
+ FLAG_PERMISSION_USER_SET to true,
+ filterPermissions = group.backgroundPermNames
+ )
+ }
+ }
- permGroupsToSkip.add(groupName)
- // Update our liveData now that there is a new skipped group
- requestInfosLiveData.update()
- }
+ permGroupsToSkip.add(groupName)
+ // Update our liveData now that there is a new skipped group
+ requestInfosLiveData.update()
+ },
+ APP_PERMISSION_REQUEST_CODE
+ )
startAppPermissionFragment(activity, groupName)
}
}
- fun openPhotoPicker(activity: Activity, result: Int) {
+ fun openPhotoPicker(activity: Activity) {
if (activityResultCallback != null) {
return
}
if (groupStates[READ_MEDIA_VISUAL to false]?.affectedPermissions == null) {
return
}
- activityResultCallback = Consumer { data ->
- val anySelected = data?.getBooleanExtra(INTENT_PHOTOS_SELECTED, true) == true
- if (anySelected) {
- onPermissionGrantResult(READ_MEDIA_VISUAL, null, result)
- } else {
- onPermissionGrantResult(READ_MEDIA_VISUAL, null, CANCELED)
- }
- requestInfosLiveData.update()
- }
- // A clone profile doesn't have a MediaProvider. If this user is a clone profile, open
- // the photo picker in the parent profile
- val userManager = activity.getSystemService(UserManager::class.java)!!
- val user = if (userManager.isCloneProfile) {
- userManager.getProfileParent(Process.myUserHandle()) ?: Process.myUserHandle()
- } else {
- Process.myUserHandle()
- }
- activity.startActivityForResultAsUser(Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP)
- .putExtra(Intent.EXTRA_UID, packageInfo.uid)
- .setType(KotlinUtils.getMimeTypeForPermissions(unfilteredAffectedPermissions)),
- PHOTO_PICKER_REQUEST_CODE, user)
+ setPhotoPickerCallback()
+ openPhotoPickerForApp(
+ activity,
+ packageInfo.uid,
+ unfilteredAffectedPermissions,
+ PHOTO_PICKER_REQUEST_CODE
+ )
+ }
+
+ private fun setPhotoPickerCallback() {
+ activityResultCallback =
+ ResultCallback(
+ { data ->
+ val anySelected = data?.getBooleanExtra(INTENT_PHOTOS_SELECTED, true) == true
+ if (anySelected) {
+ onPermissionGrantResult(READ_MEDIA_VISUAL, null, GRANTED_USER_SELECTED)
+ } else {
+ onPermissionGrantResult(READ_MEDIA_VISUAL, null, CANCELED)
+ }
+ requestInfosLiveData.update()
+ },
+ PHOTO_PICKER_REQUEST_CODE
+ )
}
/**
@@ -1382,54 +1681,66 @@ class GrantPermissionsViewModel(
*/
fun sendToSettingsFromLink(activity: Activity, groupName: String) {
startAppPermissionFragment(activity, groupName)
- activityResultCallback = Consumer { data ->
- val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED)
- if (returnGroupName != null) {
- permGroupsToSkip.add(returnGroupName)
- val result = data.getIntExtra(EXTRA_RESULT_PERMISSION_RESULT, -1)
- logSettingsInteraction(returnGroupName, result)
- requestInfosLiveData.update()
- }
- }
+ activityResultCallback =
+ ResultCallback(
+ { data ->
+ val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED)
+ if (returnGroupName != null) {
+ permGroupsToSkip.add(returnGroupName)
+ val result = data.getIntExtra(EXTRA_RESULT_PERMISSION_RESULT, -1)
+ logSettingsInteraction(returnGroupName, result)
+ requestInfosLiveData.update()
+ }
+ },
+ APP_PERMISSION_REQUEST_CODE
+ )
}
/**
- * Shows the Permission Rationale Dialog. For use with U+ only, otherwise no-op.
- *
- * @param activity The current activity
- * @param groupName The name of the permission group whose fragment should be opened
- */
+ * Shows the Permission Rationale Dialog. For use with U+ only, otherwise no-op.
+ *
+ * @param activity The current activity
+ * @param groupName The name of the permission group whose fragment should be opened
+ */
fun showPermissionRationaleActivity(activity: Activity, groupName: String) {
if (!SdkLevel.isAtLeastU()) {
return
}
- val intent = Intent(activity, PermissionRationaleActivity::class.java).apply {
- putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
- putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
- putExtra(Constants.EXTRA_SESSION_ID, sessionId)
- }
- activityResultCallback = Consumer { data ->
- val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED)
- if (returnGroupName != null) {
- permGroupsToSkip.add(returnGroupName)
- val result = data.getIntExtra(EXTRA_RESULT_PERMISSION_RESULT, CANCELED)
- logSettingsInteraction(returnGroupName, result)
- requestInfosLiveData.update()
+ val intent =
+ Intent(activity, PermissionRationaleActivity::class.java).apply {
+ putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+ putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
+ putExtra(Constants.EXTRA_SESSION_ID, sessionId)
}
- }
+ activityResultCallback =
+ ResultCallback(
+ { data ->
+ val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED)
+ if (returnGroupName != null) {
+ permGroupsToSkip.add(returnGroupName)
+ val result = data.getIntExtra(EXTRA_RESULT_PERMISSION_RESULT, CANCELED)
+ logSettingsInteraction(returnGroupName, result)
+ requestInfosLiveData.update()
+ }
+ },
+ APP_PERMISSION_REQUEST_CODE
+ )
activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE)
}
private fun startAppPermissionFragment(activity: Activity, groupName: String) {
- val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSION)
- .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
- .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
- .putExtra(Intent.EXTRA_USER, user)
- .putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME,
- GrantPermissionsActivity::class.java.name)
- .putExtra(Constants.EXTRA_SESSION_ID, sessionId)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ val intent =
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSION)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+ .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
+ .putExtra(Intent.EXTRA_USER, user)
+ .putExtra(
+ ManagePermissionsActivity.EXTRA_CALLER_NAME,
+ GrantPermissionsActivity::class.java.name
+ )
+ .putExtra(Constants.EXTRA_SESSION_ID, sessionId)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE)
}
@@ -1445,50 +1756,69 @@ class GrantPermissionsViewModel(
when (result) {
GRANTED_ALWAYS -> {
if (foregroundGroupState != null) {
- reportRequestResult(foregroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS)
+ reportRequestResult(
+ foregroundGroupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS
+ )
}
if (backgroundGroupState != null) {
- reportRequestResult(backgroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS)
+ reportRequestResult(
+ backgroundGroupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS
+ )
}
}
GRANTED_FOREGROUND_ONLY -> {
if (foregroundGroupState != null) {
- reportRequestResult(foregroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS)
+ reportRequestResult(
+ foregroundGroupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS
+ )
}
if (backgroundGroupState != null) {
- reportRequestResult(backgroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
+ reportRequestResult(
+ backgroundGroupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS
+ )
}
}
DENIED -> {
if (foregroundGroupState != null) {
- reportRequestResult(foregroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
+ reportRequestResult(
+ foregroundGroupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS
+ )
}
if (backgroundGroupState != null) {
- reportRequestResult(backgroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
+ reportRequestResult(
+ backgroundGroupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS
+ )
}
}
DENIED_DO_NOT_ASK_AGAIN -> {
if (foregroundGroupState != null) {
- reportRequestResult(foregroundGroupState.affectedPermissions,
- deniedPrejudiceInSettings)
+ reportRequestResult(
+ foregroundGroupState.affectedPermissions,
+ deniedPrejudiceInSettings
+ )
}
if (backgroundGroupState != null) {
- reportRequestResult(backgroundGroupState.affectedPermissions,
- deniedPrejudiceInSettings)
+ reportRequestResult(
+ backgroundGroupState.affectedPermissions,
+ deniedPrejudiceInSettings
+ )
}
}
}
}
- /**
- * Log all permission groups which were requested
- */
+ private fun isLocationAccuracyEnabledForApp(group: LightAppPermGroup): Boolean {
+ return isLocationAccuracyEnabled() &&
+ group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.S
+ }
+
+ /** Log all permission groups which were requested */
fun logRequestedPermissionGroups() {
if (groupStates.isEmpty()) {
return
@@ -1502,7 +1832,7 @@ class GrantPermissionsViewModel(
*
* @param groupName The name of the permission group which was interacted with
* @param selectedPrecision Selected precision of the location permission - bit flags indicate
- * which locations were chosen
+ * which locations were chosen
* @param clickedButton The button that was clicked by the user
* @param presentedButtons All buttons which were shown to the user
*/
@@ -1518,29 +1848,42 @@ class GrantPermissionsViewModel(
}
if (!requestInfosLiveData.isInitialized || !packageInfoLiveData.isInitialized) {
- Log.wtf(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" +
+ Log.wtf(
+ LOG_TAG,
+ "Logged buttons presented and clicked permissionGroupName=" +
"$groupName package=$packageName presentedButtons=$presentedButtons " +
"clickedButton=$clickedButton isPermissionRationaleShown=" +
"$isPermissionRationaleShown sessionId=$sessionId, but requests were not yet" +
- "initialized", IllegalStateException())
+ "initialized",
+ IllegalStateException()
+ )
return
}
- PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS,
- groupName, packageInfo.uid, packageName, presentedButtons, clickedButton, sessionId,
- packageInfo.targetSdkVersion, selectedPrecision,
- isPermissionRationaleShown)
- Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" +
+ PermissionControllerStatsLog.write(
+ GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS,
+ groupName,
+ packageInfo.uid,
+ packageName,
+ presentedButtons,
+ clickedButton,
+ sessionId,
+ packageInfo.targetSdkVersion,
+ selectedPrecision,
+ isPermissionRationaleShown
+ )
+ Log.v(
+ LOG_TAG,
+ "Logged buttons presented and clicked permissionGroupName=" +
"$groupName uid=${packageInfo.uid} selectedPrecision=$selectedPrecision " +
"package=$packageName presentedButtons=$presentedButtons " +
"clickedButton=$clickedButton isPermissionRationaleShown=" +
"$isPermissionRationaleShown sessionId=$sessionId " +
- "targetSdk=${packageInfo.targetSdkVersion}")
+ "targetSdk=${packageInfo.targetSdkVersion}"
+ )
}
- /**
- * Use the autoGrantNotifier to notify of auto-granted permissions.
- */
+ /** Use the autoGrantNotifier to notify of auto-granted permissions. */
fun autoGrantNotify() {
autoGrantNotifier?.notifyOfAutoGrantPermissions(true)
}
@@ -1548,6 +1891,7 @@ class GrantPermissionsViewModel(
companion object {
const val APP_PERMISSION_REQUEST_CODE = 1
const val PHOTO_PICKER_REQUEST_CODE = 2
+ const val SAVED_REQUEST_CODE_KEY = "saved_request_code"
private const val STATE_UNKNOWN = 0
private const val STATE_ALLOWED = 1
private const val STATE_DENIED = 2
@@ -1582,8 +1926,9 @@ class GrantPermissionsViewModel(
.filter { !it.isNullOrEmpty() }
// POST_NOTIFICATIONS is actively disallowed to be declared by apps below T.
// Others we don't care as much if they were declared but not used.
- .filter { targetSdkVersion >= Build.VERSION_CODES.TIRAMISU ||
- it != POST_NOTIFICATIONS }
+ .filter {
+ targetSdkVersion >= Build.VERSION_CODES.TIRAMISU || it != POST_NOTIFICATIONS
+ }
.filterIsInstance<String>()
}
}
@@ -1604,7 +1949,13 @@ class GrantPermissionsViewModelFactory(
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
- return GrantPermissionsViewModel(app, packageName, requestedPermissions,
- sessionId, savedState) as T
+ return GrantPermissionsViewModel(
+ app,
+ packageName,
+ requestedPermissions,
+ sessionId,
+ savedState
+ )
+ as T
}
}
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 09866870a..bd80a88cd 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt
@@ -35,12 +35,9 @@ import com.android.permissioncontroller.permission.utils.navigateSafe
*
* @param app The current application of the fragment
*/
-class ManageCustomPermissionsViewModel(
- private val app: Application
-) : AndroidViewModel(app) {
+class ManageCustomPermissionsViewModel(private val app: Application) : AndroidViewModel(app) {
- val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app,
- UsedCustomPermGroupNamesLiveData())
+ val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, UsedCustomPermGroupNamesLiveData())
/**
* Navigate to a Permission Apps fragment
@@ -58,12 +55,10 @@ class ManageCustomPermissionsViewModel(
*
* @param app The current application of the fragment
*/
-class ManageCustomPermissionsViewModelFactory(
- private val app: Application
-) : ViewModelProvider.Factory {
+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
}
}
@@ -72,14 +67,13 @@ class ManageCustomPermissionsViewModelFactory(
* package. This includes single-permission permission groups, as well as the Undefined permission
* group, and any other permission groups not defined by the system.
*/
-class UsedCustomPermGroupNamesLiveData :
- SmartUpdateMediatorLiveData<List<String>>() {
+class UsedCustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String>>() {
init {
- addSource(PermGroupsPackagesLiveData.get(customGroups = true)) {
- value = it.keys.toList()
- }
+ addSource(PermGroupsPackagesLiveData.get(customGroups = true)) { value = it.keys.toList() }
}
- override fun onUpdate() { /* No op override */ }
+ override fun onUpdate() {
+ /* No op override */
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
index 871a89aeb..f964fb9d2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
@@ -26,71 +26,71 @@ import com.android.permissioncontroller.permission.model.livedatatypes.PermGroup
/**
* A [androidx.lifecycle.ViewModel] for [ManagePermissionsFragment] and
- * [ManagePermissionsOtherFragment].
- * However, [ManagePermissionsViewModel] is designed in a way so that its owner should be an
- * [Activity][androidx.fragment.app.FragmentActivity] rather than individual
- * [Fragments][androidx.fragment.app.Fragment], and the aforementioned Fragments that manage
- * different sets of the permission groups should to share a single instance of
+ * [ManagePermissionsOtherFragment]. However, [ManagePermissionsViewModel] is designed in a way so
+ * that its owner should be an [Activity][androidx.fragment.app.FragmentActivity] rather than
+ * individual [Fragments][androidx.fragment.app.Fragment], and the aforementioned Fragments that
+ * manage different sets of the permission groups should to share a single instance of
* [ManagePermissionsViewModel].
*/
class ManagePermissionsViewModel(app: Application) : AndroidViewModel(app) {
- /**
- * [LiveData] that contains a list of all platform-defined permission groups.
- */
+ /** [LiveData] that contains a list of all platform-defined permission groups. */
val standardPermGroupsLiveData: LiveData<List<PermGroupPackagesUiInfo>> =
MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply {
addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) {
- permGroups -> value = permGroups.values.filterNotNull()
+ permGroups ->
+ value = permGroups.values.filterNotNull()
}
}
/**
- * [LiveData] that contains a list of platform-defined permission groups, such
- * that at least one the permissions in the group has been requested at runtime by at least one
- * non-system application or has been pregranted to a non-system application.
+ * [LiveData] that contains a list of platform-defined permission groups, such that at least one
+ * the permissions in the group has been requested at runtime by at least one non-system
+ * application or has been pregranted to a non-system application.
+ *
* @see com.android.permissioncontroller.permission.ui.television.ManagePermissionsFragment
*/
val usedPermissionGroups: LiveData<List<PermGroupPackagesUiInfo>> =
MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply {
- addSource(standardPermGroupsLiveData) {
- permGroups -> value = permGroups.filter { it.nonSystemUserSetOrPreGranted > 0 }
+ addSource(standardPermGroupsLiveData) { permGroups ->
+ value = permGroups.filter { it.nonSystemUserSetOrPreGranted > 0 }
}
}
/**
- * [LiveData] that contains a list of platform-defined permission groups, such that all
- * of the permissions in the group neither has been requested at runtime by any of the
- * non-system applications nor has been pregranted to any such application. But at least one of
- * the permissions in the group is requested by or pregranted to at least one system
- * application, other than the Shell (we do not show permission groups that are granted only to
- * the Shell, because it has all the permissions granted).
+ * [LiveData] that contains a list of platform-defined permission groups, such that all of the
+ * permissions in the group neither has been requested at runtime by any of the non-system
+ * applications nor has been pregranted to any such application. But at least one of the
+ * permissions in the group is requested by or pregranted to at least one system application,
+ * other than the Shell (we do not show permission groups that are granted only to the Shell,
+ * because it has all the permissions granted).
+ *
* @see com.android.permissioncontroller.permission.ui.television.ManagePermissionsOtherFragment
*/
val unusedPermissionGroups: LiveData<List<PermGroupPackagesUiInfo>> =
MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply {
- addSource(standardPermGroupsLiveData) {
- permGroups -> value = permGroups
- .filter { it.nonSystemUserSetOrPreGranted == 0 }
- .filter { it.systemUserSetOrPreGranted > 0 }
- .filterNot { it.onlyShellPackageGranted }
+ addSource(standardPermGroupsLiveData) { permGroups ->
+ value =
+ permGroups
+ .filter { it.nonSystemUserSetOrPreGranted == 0 }
+ .filter { it.systemUserSetOrPreGranted > 0 }
+ .filterNot { it.onlyShellPackageGranted }
}
}
/**
- * [LiveData] that contains a list of the application-defined permission groups
- * (a.k.a. "custom" permissions), such that at least one of the permissions in the group has
- * been requested at runtime by or has been pregranted to at least one application (system or
- * non-system).
+ * [LiveData] that contains a list of the application-defined permission groups (a.k.a. "custom"
+ * permissions), such that at least one of the permissions in the group has been requested at
+ * runtime by or has been pregranted to at least one application (system or non-system).
+ *
* @see com.android.permissioncontroller.permission.ui.television.ManagePermissionsOtherFragment
*/
val additionalPermissionGroups: LiveData<List<PermGroupPackagesUiInfo>> =
MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply {
- addSource(PermGroupsPackagesUiInfoLiveData(
- app, UsedCustomPermGroupNamesLiveData())) {
- permGroups -> value = permGroups.values
- .filterNotNull()
- .filter {
+ addSource(PermGroupsPackagesUiInfoLiveData(app, UsedCustomPermGroupNamesLiveData())) {
+ permGroups ->
+ value =
+ permGroups.values.filterNotNull().filter {
(it.nonSystemUserSetOrPreGranted > 0) or (it.systemUserSetOrPreGranted > 0)
}
}
@@ -98,16 +98,18 @@ class ManagePermissionsViewModel(app: Application) : AndroidViewModel(app) {
/**
* [LiveData] that indicates whether there any unused or additional permission groups.
+ *
* @see com.android.permissioncontroller.permission.ui.television.ManagePermissionsFragment
*/
@get:JvmName("hasUnusedOrAdditionalPermissionGroups")
val hasUnusedOrAdditionalPermissionGroups: LiveData<Boolean> =
MediatorLiveData<Boolean>().apply {
val updateValue: (Any?) -> Unit = {
- value = !unusedPermissionGroups.value.isNullOrEmpty() ||
- !additionalPermissionGroups.value.isNullOrEmpty()
+ value =
+ !unusedPermissionGroups.value.isNullOrEmpty() ||
+ !additionalPermissionGroups.value.isNullOrEmpty()
}
addSource(unusedPermissionGroups, updateValue)
addSource(additionalPermissionGroups, updateValue)
}
-} \ No newline at end of file
+}
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 e529f1cd5..aeab0aa89 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
@@ -23,7 +23,7 @@ import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.Transformations
+import androidx.lifecycle.map
import androidx.navigation.fragment.findNavController
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.data.PermGroupsPackagesLiveData
@@ -37,20 +37,16 @@ import com.android.permissioncontroller.permission.utils.navigateSafe
/**
* A ViewModel for the ManageStandardPermissionsFragment. Provides a LiveData which watches over all
* platform permission groups, and sends async updates when these groups have changes. It also
- * provides a liveData which watches the custom permission groups of the system, and provides
- * a list of group names.
+ * provides a liveData which watches the custom permission groups of the system, and provides a list
+ * of group names.
+ *
* @param app The current application of the fragment
*/
-class ManageStandardPermissionsViewModel(
- private val app: Application
-) : AndroidViewModel(app) {
+class ManageStandardPermissionsViewModel(private val app: Application) : AndroidViewModel(app) {
- val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app,
- StandardPermGroupNamesLiveData)
+ val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)
val numCustomPermGroups = NumCustomPermGroupsWithPackagesLiveData()
- val numAutoRevoked = Transformations.map(unusedAutoRevokePackagesLiveData) {
- it?.size ?: 0
- }
+ val numAutoRevoked = unusedAutoRevokePackagesLiveData.map { it?.size ?: 0 }
/**
* Navigate to the Custom Permissions screen
@@ -74,8 +70,7 @@ class ManageStandardPermissionsViewModel(
Utils.navigateToNotificationSettings(fragment.context!!)
return
}
- if (Utils.isHealthPermissionUiEnabled() &&
- groupName == HEALTH_PERMISSION_GROUP) {
+ if (Utils.isHealthPermissionUiEnabled() && groupName == HEALTH_PERMISSION_GROUP) {
Utils.navigateToHealthConnectSettings(fragment.context!!)
return
}
@@ -91,15 +86,12 @@ class ManageStandardPermissionsViewModel(
* A LiveData which tracks the number of custom permission groups that are used by at least one
* package
*/
-class NumCustomPermGroupsWithPackagesLiveData() :
- SmartUpdateMediatorLiveData<Int>() {
+class NumCustomPermGroupsWithPackagesLiveData() : SmartUpdateMediatorLiveData<Int>() {
private val customPermGroupPackages = PermGroupsPackagesLiveData.get(customGroups = true)
init {
- addSource(customPermGroupPackages) {
- update()
- }
+ addSource(customPermGroupPackages) { update() }
}
override fun onUpdate() {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
index 1b17041b6..a4f719843 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
@@ -67,9 +67,9 @@ import java.util.concurrent.TimeUnit
import kotlin.math.max
/**
- * ViewModel for the PermissionAppsFragment. Has a liveData with all of the UI info for each
- * package which requests permissions in this permission group, a liveData which tracks whether or
- * not to show system apps, and a liveData tracking whether there are any system apps which request
+ * ViewModel for the PermissionAppsFragment. Has a liveData with all of the UI info for each package
+ * which requests permissions in this permission group, a liveData which tracks whether or not to
+ * show system apps, and a liveData tracking whether there are any system apps which request
* permissions in this group.
*
* @param app The current application
@@ -96,10 +96,10 @@ class PermissionAppsViewModel(
val categorizedAppsLiveData = CategorizedAppsLiveData(groupName)
@get:RequiresApi(Build.VERSION_CODES.S)
- val sensorStatusLiveData: SensorStatusLiveData by lazy(LazyThreadSafetyMode.NONE)
- @RequiresApi(Build.VERSION_CODES.S) {
- SensorStatusLiveData()
- }
+ val sensorStatusLiveData: SensorStatusLiveData by
+ lazy(LazyThreadSafetyMode.NONE) @RequiresApi(Build.VERSION_CODES.S) {
+ SensorStatusLiveData()
+ }
fun updateShowSystem(showSystem: Boolean) {
if (showSystem != state.get(SHOULD_SHOW_SYSTEM_KEY)) {
@@ -111,9 +111,7 @@ class PermissionAppsViewModel(
get() = state.get(CREATION_LOGGED_KEY) ?: false
set(value) = state.set(CREATION_LOGGED_KEY, value)
- /**
- * A LiveData that tracks the status (blocked or available) of a sensor
- */
+ /** A LiveData that tracks the status (blocked or available) of a sensor */
@RequiresApi(Build.VERSION_CODES.S)
inner class SensorStatusLiveData() : SmartUpdateMediatorLiveData<Boolean>() {
val sensorPrivacyManager = app.getSystemService(SensorPrivacyManager::class.java)!!
@@ -157,13 +155,9 @@ class PermissionAppsViewModel(
}
}
- private val listener = { _: Int, status: Boolean ->
- value = status
- }
+ private val listener = { _: Int, status: Boolean -> value = status }
- private val locListener = { status: Boolean ->
- value = !status
- }
+ private val locListener = { status: Boolean -> value = !status }
override fun onUpdate() {
// Do nothing
@@ -171,8 +165,9 @@ class PermissionAppsViewModel(
}
inner class CategorizedAppsLiveData(groupName: String) :
- MediatorLiveData<@kotlin.jvm.JvmSuppressWildcards
- Map<Category, List<Pair<String, UserHandle>>>>() {
+ MediatorLiveData<
+ @kotlin.jvm.JvmSuppressWildcards Map<Category, List<Pair<String, UserHandle>>>
+ >() {
private val packagesUiInfoLiveData = SinglePermGroupPackagesUiInfoLiveData[groupName]
init {
@@ -193,18 +188,18 @@ class PermissionAppsViewModel(
}
addSource(packagesUiInfoLiveData) {
- if (fullStorageLiveData == null || fullStorageLiveData.isInitialized)
- update()
+ if (fullStorageLiveData == null || fullStorageLiveData.isInitialized) update()
}
addSource(shouldShowSystemLiveData) {
- if (fullStorageLiveData == null || fullStorageLiveData.isInitialized)
- update()
+ if (fullStorageLiveData == null || fullStorageLiveData.isInitialized) update()
}
- if ((fullStorageLiveData == null || fullStorageLiveData.isInitialized) &&
- packagesUiInfoLiveData.isInitialized) {
- packagesWithFullFileAccess = fullStorageLiveData?.value?.filter { it.isGranted }
- ?: emptyList()
+ if (
+ (fullStorageLiveData == null || fullStorageLiveData.isInitialized) &&
+ packagesUiInfoLiveData.isInitialized
+ ) {
+ packagesWithFullFileAccess =
+ fullStorageLiveData?.value?.filter { it.isGranted } ?: emptyList()
update()
}
}
@@ -218,12 +213,14 @@ class PermissionAppsViewModel(
categoryMap[Category.ASK] = mutableListOf()
categoryMap[Category.DENIED] = mutableListOf()
- val packageMap = packagesUiInfoLiveData.value ?: run {
- if (packagesUiInfoLiveData.isInitialized) {
- value = categoryMap
- }
- return
- }
+ val packageMap =
+ packagesUiInfoLiveData.value
+ ?: run {
+ if (packagesUiInfoLiveData.isInitialized) {
+ value = categoryMap
+ }
+ return
+ }
val hasSystem = packageMap.any { it.value.isSystem && it.value.shouldShow }
if (hasSystem != state.get(HAS_SYSTEM_APPS_KEY)) {
@@ -241,22 +238,31 @@ class PermissionAppsViewModel(
continue
}
- if (uiInfo.permGrantState == PermGrantState.PERMS_ALLOWED_ALWAYS ||
- uiInfo.permGrantState == PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY) {
+ if (
+ uiInfo.permGrantState == PermGrantState.PERMS_ALLOWED_ALWAYS ||
+ uiInfo.permGrantState == PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY
+ ) {
showAlwaysAllowedString = true
}
- var category = when (uiInfo.permGrantState) {
- PermGrantState.PERMS_ALLOWED -> Category.ALLOWED
- PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY -> Category.ALLOWED_FOREGROUND
- PermGrantState.PERMS_ALLOWED_ALWAYS -> Category.ALLOWED
- PermGrantState.PERMS_DENIED -> Category.DENIED
- PermGrantState.PERMS_ASK -> Category.ASK
- }
+ var category =
+ when (uiInfo.permGrantState) {
+ PermGrantState.PERMS_ALLOWED -> Category.ALLOWED
+ PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY -> Category.ALLOWED_FOREGROUND
+ PermGrantState.PERMS_ALLOWED_ALWAYS -> Category.ALLOWED
+ PermGrantState.PERMS_DENIED -> Category.DENIED
+ PermGrantState.PERMS_ASK -> Category.ASK
+ }
- if (!SdkLevel.isAtLeastT() && groupName == Manifest.permission_group.STORAGE &&
- packagesWithFullFileAccess.any { !it.isLegacy && it.isGranted &&
- it.packageName to it.user == packageUserPair }) {
+ if (
+ !SdkLevel.isAtLeastT() &&
+ groupName == Manifest.permission_group.STORAGE &&
+ packagesWithFullFileAccess.any {
+ !it.isLegacy &&
+ it.isGranted &&
+ it.packageName to it.user == packageUserPair
+ }
+ ) {
category = Category.ALLOWED
}
categoryMap[category]!!.add(packageUserPair)
@@ -267,9 +273,9 @@ class PermissionAppsViewModel(
}
/**
- * If this is the storage permission group, some apps have full access to storage, while
- * others just have access to media files. This list contains the packages with full access.
- * To listen for changes, create and observe a FullStoragePermissionAppsLiveData
+ * If this is the storage permission group, some apps have full access to storage, while others
+ * just have access to media files. This list contains the packages with full access. To listen
+ * for changes, create and observe a FullStoragePermissionAppsLiveData
*/
private var packagesWithFullFileAccess = listOf<FullStoragePackageState>()
@@ -280,17 +286,15 @@ class PermissionAppsViewModel(
*
* @param packageName The name of the package we want to check
* @param user The name of the user whose package we want to check
- *
* @return true if the package and user has full file access
*/
fun packageHasFullStorage(packageName: String, user: UserHandle): Boolean {
- return packagesWithFullFileAccess.any {
- it.packageName == packageName && it.user == user }
+ return packagesWithFullFileAccess.any { it.packageName == packageName && it.user == user }
}
/**
- * Whether or not packages have been loaded from the system.
- * To update, need to observe the allPackageInfosLiveData.
+ * Whether or not packages have been loaded from the system. To update, need to observe the
+ * allPackageInfosLiveData.
*
* @return Whether or not all packages have been loaded
*/
@@ -313,16 +317,16 @@ class PermissionAppsViewModel(
args: Bundle
) {
val activity = fragment.activity!!
- if (LocationUtils.isLocationGroupAndProvider(
- activity, groupName, packageName)) {
+ if (LocationUtils.isLocationGroupAndProvider(activity, groupName, packageName)) {
val intent = Intent(activity, LocationProviderInterceptDialog::class.java)
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
activity.startActivityAsUser(intent, user)
return
}
- if (LocationUtils.isLocationGroupAndControllerExtraPackage(
- activity, groupName, packageName)) {
+ if (
+ LocationUtils.isLocationGroupAndControllerExtraPackage(activity, groupName, packageName)
+ ) {
// Redirect to location controller extra package settings.
LocationUtils.startLocationControllerExtraPackageSettings(activity, user)
return
@@ -332,31 +336,38 @@ class PermissionAppsViewModel(
}
fun getFilterTimeBeginMillis(): Long {
- val aggregateDataFilterBeginDays = if (is7DayToggleEnabled())
- AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1
+ val aggregateDataFilterBeginDays =
+ if (is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7
+ else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1
- return max(System.currentTimeMillis() -
+ return max(
+ System.currentTimeMillis() -
TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays.toLong()),
- Instant.EPOCH.toEpochMilli())
+ Instant.EPOCH.toEpochMilli()
+ )
}
/**
* Return a mapping of user + packageName to their last access timestamps for the permission
* group.
*/
- fun extractGroupUsageLastAccessTime(appPermissionUsages: List<AppPermissionUsage>):
- MutableMap<String, Long> {
+ fun extractGroupUsageLastAccessTime(
+ appPermissionUsages: List<AppPermissionUsage>
+ ): MutableMap<String, Long> {
val accessTime: MutableMap<String, Long> = HashMap()
if (!SdkLevel.isAtLeastS()) {
return accessTime
}
- val aggregateDataFilterBeginDays = if (is7DayToggleEnabled())
- AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1
+ val aggregateDataFilterBeginDays =
+ if (is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7
+ else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1
val now = System.currentTimeMillis()
- val filterTimeBeginMillis = max(
+ val filterTimeBeginMillis =
+ max(
now - TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays.toLong()),
- Instant.EPOCH.toEpochMilli())
+ Instant.EPOCH.toEpochMilli()
+ )
val numApps: Int = appPermissionUsages.size
for (appIndex in 0 until numApps) {
val appUsage: AppPermissionUsage = appPermissionUsages.get(appIndex)
@@ -380,41 +391,39 @@ class PermissionAppsViewModel(
return accessTime
}
- /**
- * Return the String preference summary based on the last access time.
- */
- fun getPreferenceSummary(res: Resources, summaryTimestamp: Triple<String, Int, String>):
- String {
+ /** Return the String preference summary based on the last access time. */
+ fun getPreferenceSummary(
+ res: Resources,
+ summaryTimestamp: Triple<String, Int, String>
+ ): String {
return when (summaryTimestamp.second) {
- Utils.LAST_24H_CONTENT_PROVIDER -> res.getString(
- R.string.app_perms_content_provider_24h)
- Utils.LAST_7D_CONTENT_PROVIDER -> res.getString(
- R.string.app_perms_content_provider_7d)
- Utils.LAST_24H_SENSOR_TODAY -> res.getString(R.string.app_perms_24h_access,
- summaryTimestamp.first)
- Utils.LAST_24H_SENSOR_YESTERDAY -> res.getString(R.string.app_perms_24h_access_yest,
- summaryTimestamp.first)
- Utils.LAST_7D_SENSOR -> res.getString(R.string.app_perms_7d_access,
- summaryTimestamp.third, summaryTimestamp.first)
+ Utils.LAST_24H_CONTENT_PROVIDER ->
+ res.getString(R.string.app_perms_content_provider_24h)
+ Utils.LAST_7D_CONTENT_PROVIDER -> res.getString(R.string.app_perms_content_provider_7d)
+ Utils.LAST_24H_SENSOR_TODAY ->
+ res.getString(R.string.app_perms_24h_access, summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY ->
+ res.getString(R.string.app_perms_24h_access_yest, summaryTimestamp.first)
+ Utils.LAST_7D_SENSOR ->
+ res.getString(
+ R.string.app_perms_7d_access,
+ summaryTimestamp.third,
+ summaryTimestamp.first
+ )
else -> ""
}
}
- /**
- * Return two preferences to determine their ordering.
- */
+ /** Return two preferences to determine their ordering. */
fun comparePreference(collator: Collator, lhs: Preference, rhs: Preference): Int {
- var result: Int = collator.compare(lhs.title.toString(),
- rhs.title.toString())
+ var result: Int = collator.compare(lhs.title.toString(), rhs.title.toString())
if (result == 0) {
result = lhs.key.compareTo(rhs.key)
}
return result
}
- /**
- * Log that the fragment was created.
- */
+ /** Log that the fragment was created. */
fun logPermissionAppsFragmentCreated(
packageName: String,
user: UserHandle,
@@ -439,14 +448,30 @@ class PermissionAppsViewModel(
category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED
}
}
- val uid = getPackageUid(application,
- packageName, user) ?: return
+ val uid = getPackageUid(application, packageName, user) ?: return
PermissionControllerStatsLog.write(
- PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED, sessionId, viewId,
- permGroupName, uid, packageName, category)
- Log.v(tag, tag + " created with sessionId=" + sessionId +
- " permissionGroupName=" + permGroupName + " appUid=" + uid +
- " packageName=" + packageName + " category=" + category)
+ PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED,
+ sessionId,
+ viewId,
+ permGroupName,
+ uid,
+ packageName,
+ category
+ )
+ Log.v(
+ tag,
+ tag +
+ " created with sessionId=" +
+ sessionId +
+ " permissionGroupName=" +
+ permGroupName +
+ " appUid=" +
+ uid +
+ " packageName=" +
+ packageName +
+ " category=" +
+ category
+ )
}
}
@@ -464,13 +489,16 @@ class PermissionAppsViewModelFactory(
owner: SavedStateRegistryOwner,
defaultArgs: Bundle
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
-
- override fun <T : ViewModel> create(p0: String, p1: Class<T>, state: SavedStateHandle): T {
- state.set(SHOULD_SHOW_SYSTEM_KEY, state.get<Boolean>(SHOULD_SHOW_SYSTEM_KEY) ?: false)
- state.set(HAS_SYSTEM_APPS_KEY, state.get<Boolean>(HAS_SYSTEM_APPS_KEY) ?: true)
- state.set(SHOW_ALWAYS_ALLOWED, state.get<Boolean>(SHOW_ALWAYS_ALLOWED) ?: false)
- state.set(CREATION_LOGGED_KEY, state.get<Boolean>(CREATION_LOGGED_KEY) ?: false)
+ override fun <T : ViewModel> create(
+ key: String,
+ modelClass: Class<T>,
+ handle: SavedStateHandle
+ ): T {
+ handle.set(SHOULD_SHOW_SYSTEM_KEY, handle.get<Boolean>(SHOULD_SHOW_SYSTEM_KEY) ?: false)
+ handle.set(HAS_SYSTEM_APPS_KEY, handle.get<Boolean>(HAS_SYSTEM_APPS_KEY) ?: true)
+ handle.set(SHOW_ALWAYS_ALLOWED, handle.get<Boolean>(SHOW_ALWAYS_ALLOWED) ?: false)
+ handle.set(CREATION_LOGGED_KEY, handle.get<Boolean>(CREATION_LOGGED_KEY) ?: false)
@Suppress("UNCHECKED_CAST")
- return PermissionAppsViewModel(state, app, groupName) as T
+ return PermissionAppsViewModel(handle, app, groupName) as T
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt
index 7431637a8..ecb551ffc 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt
@@ -39,53 +39,51 @@ import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import java.util.stream.Collectors
-/**
- * View model for legacy {@link ReviewPermissionsFragment}.
- */
-class ReviewPermissionsViewModel(
- val app: Application,
- val packageInfo: PackageInfo
-) : ViewModel() {
+/** View model for legacy {@link ReviewPermissionsFragment}. */
+class ReviewPermissionsViewModel(val app: Application, val packageInfo: PackageInfo) : ViewModel() {
private val mUser = android.os.Process.myUserHandle()
- /**
- * Holds permission groups for a package or an empty map in case no user review is required.
- */
+ /** Holds permission groups for a package or an empty map in case no user review is required. */
val permissionGroupsLiveData =
object : SmartUpdateMediatorLiveData<Map<String, LightAppPermGroup>>() {
val packagePermsLiveData = PackagePermissionsLiveData[packageInfo.packageName, mUser]
init {
- addSource(packagePermsLiveData) {
- update()
- }
+ addSource(packagePermsLiveData) { update() }
}
val permissionGroups = mutableMapOf<String, LightAppPermGroupLiveData>()
override fun onUpdate() {
val permissionGroupsMap = packagePermsLiveData.value ?: return
- val filteredGroups = permissionGroupsMap.keys.stream()
- .filter { it -> !it.equals(NON_RUNTIME_NORMAL_PERMS) }
- .collect(Collectors.toList())
+ val filteredGroups =
+ permissionGroupsMap.keys
+ .stream()
+ .filter { it -> !it.equals(NON_RUNTIME_NORMAL_PERMS) }
+ .collect(Collectors.toList())
val getPermGroupLiveData = { permGroupName: String ->
LightAppPermGroupLiveData[packageInfo.packageName, permGroupName, mUser]
}
setSourcesToDifference(filteredGroups, permissionGroups, getPermGroupLiveData)
- if (permissionGroups.values.all { it.isInitialized } &&
- permissionGroups.values.all { !it.isStale }) {
- val permGroups: List<LightAppPermGroup?> = permissionGroups.values.map {
- it.value }
- val reviewGroups = permGroups.filterNotNull().filter {
- shouldShowPermission(it) &&
- Utils.OS_PKG == it.permGroupInfo.packageName
- }.associateBy {
- it.permGroupName
- }
- value = if (reviewGroups.any { it.value.isReviewRequired }) reviewGroups
- else emptyMap()
+ if (
+ permissionGroups.values.all { it.isInitialized } &&
+ permissionGroups.values.all { !it.isStale }
+ ) {
+ val permGroups: List<LightAppPermGroup?> =
+ permissionGroups.values.map { it.value }
+ val reviewGroups =
+ permGroups
+ .filterNotNull()
+ .filter {
+ shouldShowPermission(it) &&
+ Utils.OS_PKG == it.permGroupInfo.packageName
+ }
+ .associateBy { it.permGroupName }
+ value =
+ if (reviewGroups.any { it.value.isReviewRequired }) reviewGroups
+ else emptyMap()
}
}
}
@@ -110,15 +108,15 @@ class ReviewPermissionsViewModel(
}
/**
- * Update the summary of a permission group that has background permission.
- * This does not apply to permission groups that are fixed by policy
+ * Update the summary of a permission group that has background permission. This does not apply
+ * to permission groups that are fixed by policy
*/
- fun getSummaryForPermGroupWithBackgroundPermission(
- state: PermissionTarget
- ): PermissionSummary {
+ fun getSummaryForPermGroupWithBackgroundPermission(state: PermissionTarget): PermissionSummary {
if (state != PermissionTarget.PERMISSION_NONE) {
- if (state.and(PermissionTarget.PERMISSION_BACKGROUND)
- != PermissionTarget.PERMISSION_NONE.value) {
+ if (
+ state.and(PermissionTarget.PERMISSION_BACKGROUND) !=
+ PermissionTarget.PERMISSION_NONE.value
+ ) {
return SummaryMessage.ACCESS_ALWAYS.toPermSummary()
} else {
return SummaryMessage.ACCESS_ONLY_FOREGROUND.toPermSummary()
@@ -152,9 +150,7 @@ class ReviewPermissionsViewModel(
}
}
- /**
- * Show all individual permissions in this group in a new fragment.
- */
+ /** Show all individual permissions in this group in a new fragment. */
fun showAllPermissions(fragment: Fragment, args: Bundle) {
val navController: NavController = NavHostFragment.findNavController(fragment)
navController.navigateSafe(R.id.app_to_all_perms, args)
@@ -222,8 +218,10 @@ class ReviewPermissionsViewModel(
SummaryMessage.ENFORCED_BY_POLICY.toPermSummary()
}
} else {
- if (mState.and(PermissionTarget.PERMISSION_BACKGROUND) !=
- PermissionTarget.PERMISSION_NONE.value) {
+ if (
+ mState.and(PermissionTarget.PERMISSION_BACKGROUND) !=
+ PermissionTarget.PERMISSION_NONE.value
+ ) {
return if (hasAdmin) {
SummaryMessage.ENABLED_BY_ADMIN.toPermSummary()
} else {
@@ -242,8 +240,10 @@ class ReviewPermissionsViewModel(
} else {
// Part of the permission group can still be switched
if (permGroup.background.isPolicyFixed) {
- return if (mState.and(PermissionTarget.PERMISSION_BACKGROUND) !=
- PermissionTarget.PERMISSION_NONE.value) {
+ return if (
+ mState.and(PermissionTarget.PERMISSION_BACKGROUND) !=
+ PermissionTarget.PERMISSION_NONE.value
+ ) {
if (hasAdmin) {
SummaryMessage.ENABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true)
} else {
@@ -277,11 +277,10 @@ class ReviewPermissionsViewModel(
return mGroup.foreground.isPolicyFixed && !mGroup.isGranted
}
- /**
- * Whether policy is system fixed or fully fixed or foreground disabled
- */
+ /** Whether policy is system fixed or fully fixed or foreground disabled */
fun isFixedOrForegroundDisabled(mGroup: LightAppPermGroup): Boolean {
- return mGroup.isSystemFixed || mGroup.isPolicyFullyFixed ||
+ return mGroup.isSystemFixed ||
+ mGroup.isPolicyFullyFixed ||
isForegroundDisabledByPolicy(mGroup)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/UnusedAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/UnusedAppsViewModel.kt
index 3c3f347a4..868f9229f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/UnusedAppsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/UnusedAppsViewModel.kt
@@ -96,85 +96,89 @@ class UnusedAppsViewModel(private val app: Application, private val sessionId: L
private data class PackageLastUsageTime(val packageName: String, val usageTime: Long)
val unusedPackageCategoriesLiveData =
- object : SmartAsyncMediatorLiveData<Map<UnusedPeriod, List<UnusedPackageInfo>>>(
- alwaysUpdateOnActive = false
- ) {
- // Get apps usage stats from the longest interesting period (MAX_UNUSED_PERIOD_MILLIS)
- private val usageStatsLiveData = UsageStatsLiveData[MAX_UNUSED_PERIOD_MILLIS]
-
- init {
- addSource(getUnusedPackages()) {
- onUpdate()
- }
-
- addSource(AllPackageInfosLiveData) {
- onUpdate()
- }
+ object :
+ SmartAsyncMediatorLiveData<Map<UnusedPeriod, List<UnusedPackageInfo>>>(
+ alwaysUpdateOnActive = false
+ ) {
+ // Get apps usage stats from the longest interesting period (MAX_UNUSED_PERIOD_MILLIS)
+ private val usageStatsLiveData = UsageStatsLiveData[MAX_UNUSED_PERIOD_MILLIS]
- addSource(usageStatsLiveData) {
- onUpdate()
- }
- }
+ init {
+ addSource(getUnusedPackages()) { onUpdate() }
- override suspend fun loadDataAndPostValue(job: Job) {
- if (!getUnusedPackages().isInitialized ||
- !usageStatsLiveData.isInitialized || !AllPackageInfosLiveData.isInitialized
- ) {
- return
- }
+ addSource(AllPackageInfosLiveData) { onUpdate() }
- val unusedApps = getUnusedPackages().value!!
- Log.i(LOG_TAG, "Unused apps: $unusedApps")
- val categorizedApps = mutableMapOf<UnusedPeriod, MutableList<UnusedPackageInfo>>()
- for (period in UnusedPeriod.allPeriods) {
- categorizedApps[period] = mutableListOf()
+ addSource(usageStatsLiveData) { onUpdate() }
}
- // Get all packages which cannot be uninstalled.
- val systemApps = getUnusedSystemApps(AllPackageInfosLiveData.value!!, unusedApps)
- val lastUsedDataUnusedApps =
- extractUnusedAppsUsageData(usageStatsLiveData.value!!, unusedApps)
- { it: UsageStats ->
- PackageLastUsageTime(it.packageName, it.lastTimePackageUsed())
+ override suspend fun loadDataAndPostValue(job: Job) {
+ if (
+ !getUnusedPackages().isInitialized ||
+ !usageStatsLiveData.isInitialized ||
+ !AllPackageInfosLiveData.isInitialized
+ ) {
+ return
}
- val firstInstallDataUnusedApps =
- extractUnusedAppsUsageData(AllPackageInfosLiveData.value!!, unusedApps)
- { it: LightPackageInfo ->
- PackageLastUsageTime(it.packageName, it.firstInstallTime)
+
+ val unusedApps = getUnusedPackages().value!!
+ Log.i(LOG_TAG, "Unused apps: $unusedApps")
+ val categorizedApps = mutableMapOf<UnusedPeriod, MutableList<UnusedPackageInfo>>()
+ for (period in UnusedPeriod.allPeriods) {
+ categorizedApps[period] = mutableListOf()
}
- val now = System.currentTimeMillis()
- unusedApps.keys.forEach { (packageName, user) ->
- val userPackage = packageName to user
-
- // If we didn't find the stat for a package in our usageStats search, it is more than
- // 6 months old, or the app has never been opened. Then use first install date instead.
- var lastUsageTime =
- lastUsedDataUnusedApps[userPackage] ?: firstInstallDataUnusedApps[
- userPackage] ?: 0L
-
- val period = UnusedPeriod.findLongestValidPeriod(now - lastUsageTime)
- categorizedApps[period]!!.add(
- UnusedPackageInfo(
- packageName, user, systemApps.contains(userPackage),
- unusedApps[userPackage]!!
+ // Get all packages which cannot be uninstalled.
+ val systemApps = getUnusedSystemApps(AllPackageInfosLiveData.value!!, unusedApps)
+ val lastUsedDataUnusedApps =
+ extractUnusedAppsUsageData(usageStatsLiveData.value!!, unusedApps) {
+ it: UsageStats ->
+ PackageLastUsageTime(it.packageName, it.lastTimePackageUsed())
+ }
+ val firstInstallDataUnusedApps =
+ extractUnusedAppsUsageData(AllPackageInfosLiveData.value!!, unusedApps) {
+ it: LightPackageInfo ->
+ PackageLastUsageTime(it.packageName, it.firstInstallTime)
+ }
+
+ val now = System.currentTimeMillis()
+ unusedApps.keys.forEach { (packageName, user) ->
+ val userPackage = packageName to user
+
+ // If we didn't find the stat for a package in our usageStats search, it is more
+ // than
+ // 6 months old, or the app has never been opened. Then use first install date
+ // instead.
+ var lastUsageTime =
+ lastUsedDataUnusedApps[userPackage]
+ ?: firstInstallDataUnusedApps[userPackage] ?: 0L
+
+ val period = UnusedPeriod.findLongestValidPeriod(now - lastUsageTime)
+ categorizedApps[period]!!.add(
+ UnusedPackageInfo(
+ packageName,
+ user,
+ systemApps.contains(userPackage),
+ unusedApps[userPackage]!!
+ )
)
- )
- }
+ }
- postValue(categorizedApps)
+ postValue(categorizedApps)
+ }
}
- }
// Extract UserPackage information for unused system apps from source map.
private fun getUnusedSystemApps(
userPackages: Map<UserHandle, List<LightPackageInfo>>,
- unusedApps: Map<UserPackage, Set<String>>,
+ unusedApps: Map<UserPackage, Set<String>>,
): List<UserPackage> {
- return userPackages.flatMap { (userHandle, packageList) ->
- packageList.filter { (it.appFlags and ApplicationInfo.FLAG_SYSTEM) != 0 }
- .map { it.packageName to userHandle }
- }.filter { unusedApps.contains(it) }
+ return userPackages
+ .flatMap { (userHandle, packageList) ->
+ packageList
+ .filter { (it.appFlags and ApplicationInfo.FLAG_SYSTEM) != 0 }
+ .map { it.packageName to userHandle }
+ }
+ .filter { unusedApps.contains(it) }
}
/**
@@ -187,9 +191,11 @@ class UnusedAppsViewModel(private val app: Application, private val sessionId: L
unusedApps: Map<UserPackage, Set<String>>,
extractUsageData: (fullData: PackageData) -> PackageLastUsageTime,
): Map<UserPackage, Long> {
- return userPackages.flatMap { (userHandle, fullData) ->
- fullData.map { userHandle to extractUsageData(it) }
- }.associate { (handle, appData) -> (appData.packageName to handle) to appData.usageTime }
+ return userPackages
+ .flatMap { (userHandle, fullData) ->
+ fullData.map { userHandle to extractUsageData(it) }
+ }
+ .associate { (handle, appData) -> (appData.packageName to handle) to appData.usageTime }
.filterKeys { unusedApps.contains(it) }
}
@@ -215,37 +221,57 @@ class UnusedAppsViewModel(private val app: Application, private val sessionId: L
Log.i(LOG_TAG, "sessionId: $sessionId, Disabling $packageName, $user")
logAppInteraction(packageName, user, AUTO_REVOKED_APP_INTERACTION__ACTION__REMOVE)
val userContext = Utils.getUserContext(app, user)
- userContext.packageManager.setApplicationEnabledSetting(packageName,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0)
+ userContext.packageManager.setApplicationEnabledSetting(
+ packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER,
+ 0
+ )
}
private fun logAppInteraction(packageName: String, user: UserHandle, action: Int) {
GlobalScope.launch(IPC) {
// If we are logging an app interaction, then the AllPackageInfosLiveData is not stale.
- val uid = AllPackageInfosLiveData.value?.get(user)
- ?.find { info -> info.packageName == packageName }?.uid
+ val uid =
+ AllPackageInfosLiveData.value
+ ?.get(user)
+ ?.find { info -> info.packageName == packageName }
+ ?.uid
if (uid != null) {
- PermissionControllerStatsLog.write(AUTO_REVOKED_APP_INTERACTION, sessionId,
- uid, packageName, action)
+ PermissionControllerStatsLog.write(
+ AUTO_REVOKED_APP_INTERACTION,
+ sessionId,
+ uid,
+ packageName,
+ action
+ )
}
}
}
fun logAppView(packageName: String, user: UserHandle, groupName: String, isNew: Boolean) {
GlobalScope.launch(IPC) {
- val uid = AllPackageInfosLiveData.value!![user]!!.find { info ->
- info.packageName == packageName
- }?.uid
+ val uid =
+ AllPackageInfosLiveData.value!![user]!!.find { info ->
+ info.packageName == packageName
+ }
+ ?.uid
if (uid != null) {
- val bucket = if (isNew) {
- AUTO_REVOKE_FRAGMENT_APP_VIEWED__AGE__NEWER_BUCKET
- } else {
- AUTO_REVOKE_FRAGMENT_APP_VIEWED__AGE__OLDER_BUCKET
- }
- PermissionControllerStatsLog.write(AUTO_REVOKE_FRAGMENT_APP_VIEWED, sessionId,
- uid, packageName, groupName, bucket)
+ val bucket =
+ if (isNew) {
+ AUTO_REVOKE_FRAGMENT_APP_VIEWED__AGE__NEWER_BUCKET
+ } else {
+ AUTO_REVOKE_FRAGMENT_APP_VIEWED__AGE__OLDER_BUCKET
+ }
+ PermissionControllerStatsLog.write(
+ AUTO_REVOKE_FRAGMENT_APP_VIEWED,
+ sessionId,
+ uid,
+ packageName,
+ groupName,
+ bucket
+ )
}
}
}
@@ -259,7 +285,6 @@ class UnusedAppsViewModelFactory(
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
- @Suppress("UNCHECKED_CAST")
- return UnusedAppsViewModel(app, sessionId) as T
+ @Suppress("UNCHECKED_CAST") return UnusedAppsViewModel(app, sessionId) as T
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt
index db79165c3..cd046ec73 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt
@@ -36,11 +36,12 @@ import com.android.permissioncontroller.permission.utils.StringUtils
@RequiresApi(Build.VERSION_CODES.S)
object PermissionUsageControlPreferenceUtils {
- private val SENSOR_DATA_PERMISSIONS: List<String> = listOf(
- Manifest.permission_group.LOCATION,
- Manifest.permission_group.CAMERA,
- Manifest.permission_group.MICROPHONE
- )
+ private val SENSOR_DATA_PERMISSIONS: List<String> =
+ listOf(
+ Manifest.permission_group.LOCATION,
+ Manifest.permission_group.CAMERA,
+ Manifest.permission_group.MICROPHONE
+ )
@JvmStatic
fun initPreference(
@@ -56,19 +57,28 @@ object PermissionUsageControlPreferenceUtils {
return preference.apply {
title = permGroupLabel
icon = KotlinUtils.getPermGroupIcon(context, groupName)
- summary = StringUtils.getIcuPluralsString(context,
- R.string.permission_usage_preference_label, count)
+ summary =
+ StringUtils.getIcuPluralsString(
+ context,
+ R.string.permission_usage_preference_label,
+ count
+ )
if (count == 0) {
isEnabled = false
- val permissionUsageSummaryNotUsed = if (show7Days) {
- StringUtils.getIcuPluralsString(context,
+ val permissionUsageSummaryNotUsed =
+ if (show7Days) {
+ StringUtils.getIcuPluralsString(
+ context,
R.string.permission_usage_preference_summary_not_used_in_past_n_days,
- 7)
- } else {
- StringUtils.getIcuPluralsString(context,
+ 7
+ )
+ } else {
+ StringUtils.getIcuPluralsString(
+ context,
R.string.permission_usage_preference_summary_not_used_in_past_n_hours,
- 24)
- }
+ 24
+ )
+ }
setSummary(permissionUsageSummaryNotUsed)
} else if (SENSOR_DATA_PERMISSIONS.contains(groupName)) {
onPreferenceClickListener = OnPreferenceClickListener {
@@ -92,18 +102,19 @@ object PermissionUsageControlPreferenceUtils {
}
private fun logSensorDataTimelineViewed(groupName: String, sessionId: Long) {
- val act = when (groupName) {
- Manifest.permission_group.LOCATION -> {
- PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__LOCATION_ACCESS_TIMELINE_VIEWED
- }
- Manifest.permission_group.CAMERA -> {
- PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__CAMERA_ACCESS_TIMELINE_VIEWED
- }
- Manifest.permission_group.MICROPHONE -> {
- PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__MICROPHONE_ACCESS_TIMELINE_VIEWED
+ val act =
+ when (groupName) {
+ Manifest.permission_group.LOCATION -> {
+ PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__LOCATION_ACCESS_TIMELINE_VIEWED
+ }
+ Manifest.permission_group.CAMERA -> {
+ PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__CAMERA_ACCESS_TIMELINE_VIEWED
+ }
+ Manifest.permission_group.MICROPHONE -> {
+ PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__MICROPHONE_ACCESS_TIMELINE_VIEWED
+ }
+ else -> 0
}
- else -> 0
- }
PermissionControllerStatsLog.write(PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, act)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt
index f77bfff3b..6dba04b55 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt
@@ -28,6 +28,7 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Resources
+import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.os.UserHandle
@@ -37,7 +38,6 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.savedstate.SavedStateRegistryOwner
import com.android.modules.utils.build.SdkLevel
-import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.compat.IntentCompat
import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData
@@ -54,7 +54,6 @@ import com.android.permissioncontroller.permission.model.livedatatypes.v31.Light
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
-import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageLabel
import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils
@@ -80,6 +79,9 @@ class PermissionUsageDetailsViewModel(
val showSystemLiveData = state.getLiveData(SHOULD_SHOW_SYSTEM_KEY, false)
val show7DaysLiveData = state.getLiveData(SHOULD_SHOW_7_DAYS_KEY, false)
+ private val packageIconCache: MutableMap<Pair<String, UserHandle>, Drawable> = mutableMapOf()
+ private val packageLabelCache: MutableMap<String, String> = mutableMapOf()
+
private val roleManager =
Utils.getSystemServiceSafe(application.applicationContext, RoleManager::class.java)
@@ -109,14 +111,19 @@ class PermissionUsageDetailsViewModel(
}
val startTime =
(System.currentTimeMillis() - showPermissionUsagesDuration).coerceAtLeast(
- Instant.EPOCH.toEpochMilli())
+ Instant.EPOCH.toEpochMilli()
+ )
return PermissionUsageDetailsUiInfo(
show7Days,
showSystem,
buildAppPermissionAccessUiInfoList(
- allLightHistoricalPackageOpsLiveData, startTime, showSystem),
- containsSystemAppUsages(allLightHistoricalPackageOpsLiveData, startTime))
+ allLightHistoricalPackageOpsLiveData,
+ startTime,
+ showSystem
+ ),
+ containsSystemAppUsages(allLightHistoricalPackageOpsLiveData, startTime)
+ )
}
/**
@@ -162,9 +169,11 @@ class PermissionUsageDetailsViewModel(
// The Telecom doesn't request microphone or camera permissions. However, telecom app may
// use these permissions and they are considered system app permissions, so we return true
// even if the AppPermGroupUiInfo is unavailable.
- if (appPermissionId.packageName == TELECOM_PACKAGE &&
- (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA ||
- appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE)) {
+ if (
+ appPermissionId.packageName == TELECOM_PACKAGE &&
+ (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA ||
+ appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE)
+ ) {
return true
}
return false
@@ -244,7 +253,8 @@ class PermissionUsageDetailsViewModel(
it.appPermissionId,
it.attributionLabel,
it.attributionTags,
- updatedDiscreteAccesses)
+ updatedDiscreteAccesses
+ )
}
/** Filters out data for apps and permissions that don't need to be displayed in the UI. */
@@ -268,7 +278,8 @@ class PermissionUsageDetailsViewModel(
this.appPermissionId,
Resources.ID_NULL,
attributionTags = emptyList(),
- this.discreteAccesses)
+ this.discreteAccesses
+ )
/** Groups tag-attributed accesses for the provided app and permission by attribution label. */
private fun AttributedAppPermissionDiscreteAccesses.groupAccessesByLabel(
@@ -307,7 +318,9 @@ class PermissionUsageDetailsViewModel(
appPermissionId,
label,
tags,
- discreteAccesses.sortedBy { -1 * it.accessTimeMs }))
+ discreteAccesses.sortedBy { -1 * it.accessTimeMs }
+ )
+ )
}
return appPermissionDiscreteAccessWithLabels
@@ -334,7 +347,9 @@ class PermissionUsageDetailsViewModel(
appPermAccesses.appPermissionId,
appPermAccesses.attributionLabel,
appPermAccesses.attributionTags,
- currentDiscreteAccesses.toMutableList()))
+ currentDiscreteAccesses.toMutableList()
+ )
+ )
currentDiscreteAccesses.clear()
currentDiscreteAccesses.add(discreteAccess)
} else {
@@ -348,7 +363,9 @@ class PermissionUsageDetailsViewModel(
appPermAccesses.appPermissionId,
appPermAccesses.attributionLabel,
appPermAccesses.attributionTags,
- currentDiscreteAccesses.toMutableList()))
+ currentDiscreteAccesses.toMutableList()
+ )
+ )
}
return clusters
}
@@ -384,12 +401,15 @@ class PermissionUsageDetailsViewModel(
return AppPermissionAccessUiInfo(
this.appPermissionId.userHandle,
this.appPermissionId.packageName,
+ getPackageLabel(this.appPermissionId.packageName, this.appPermissionId.userHandle),
permissionGroup,
this.discreteAccesses.last().accessTimeMs,
this.discreteAccesses.first().accessTimeMs,
summary,
showingSubAttribution,
- ArrayList(this.attributionTags))
+ ArrayList(this.attributionTags),
+ getBadgedPackageIcon(this.appPermissionId.packageName, this.appPermissionId.userHandle)
+ )
}
/** Builds a summary of the permission access. */
@@ -410,10 +430,14 @@ class PermissionUsageDetailsViewModel(
R.string.history_preference_subtext_3,
subTextStrings[0],
subTextStrings[1],
- subTextStrings[2])
+ subTextStrings[2]
+ )
2 ->
context.getString(
- R.string.history_preference_subtext_2, subTextStrings[0], subTextStrings[1])
+ R.string.history_preference_subtext_2,
+ subTextStrings[0],
+ subTextStrings[1]
+ )
1 -> subTextStrings[0]
else -> null
}
@@ -462,7 +486,6 @@ class PermissionUsageDetailsViewModel(
.firstOrNull { it.proxy?.packageName != null }
?.let {
getPackageLabel(
- PermissionControllerApplication.get(),
it.proxy!!.packageName!!,
UserHandle.getUserHandleForUid(it.proxy.uid))
}
@@ -492,13 +515,15 @@ class PermissionUsageDetailsViewModel(
/** Data used to create a preference for an app's permission usage. */
data class AppPermissionAccessUiInfo(
val userHandle: UserHandle,
- val pkgName: String,
+ val packageName: String,
+ val packageLabel: String,
val permissionGroup: String,
val accessStartTime: Long,
val accessEndTime: Long,
val summaryText: CharSequence?,
val showingAttribution: Boolean,
val attributionTags: ArrayList<String>,
+ val badgedPackageIcon: Drawable?,
)
/**
@@ -593,13 +618,17 @@ class PermissionUsageDetailsViewModel(
setSourcesToDifference(
appPermissionIds,
appPermGroupUiInfoLiveDataList,
- getAppPermGroupUiInfoLiveData) {
- update()
- }
+ getAppPermGroupUiInfoLiveData
+ ) {
+ update()
+ }
setSourcesToDifference(
- allPackages, lightPackageInfoLiveDataMap, getLightPackageInfoLiveData) {
- update()
- }
+ allPackages,
+ lightPackageInfoLiveDataMap,
+ getLightPackageInfoLiveData
+ ) {
+ update()
+ }
if (appPermGroupUiInfoLiveDataList.any { it.value.isStale }) {
return
@@ -613,6 +642,36 @@ class PermissionUsageDetailsViewModel(
}
}
+ /**
+ * Returns the icon for the provided package name and user, by first searching the cache
+ * otherwise retrieving it from the app's [android.content.pm.ApplicationInfo].
+ */
+ private fun getBadgedPackageIcon(packageName: String, userHandle: UserHandle): Drawable? {
+ val packageNameWithUser: Pair<String, UserHandle> = Pair(packageName, userHandle)
+ if (packageIconCache.containsKey(packageNameWithUser)) {
+ return requireNotNull(packageIconCache[packageNameWithUser])
+ }
+ val packageIcon = KotlinUtils.getBadgedPackageIcon(application, packageName, userHandle)
+ if (packageIcon != null) packageIconCache[packageNameWithUser] = packageIcon
+
+ return packageIcon
+ }
+
+ /**
+ * Returns the label for the provided package name, by first searching the cache otherwise
+ * retrieving it from the app's [android.content.pm.ApplicationInfo].
+ */
+ private fun getPackageLabel(packageName: String, user: UserHandle): String {
+ if (packageLabelCache.containsKey(packageName)) {
+ return requireNotNull(packageLabelCache[packageName])
+ }
+
+ val packageLabel = KotlinUtils.getPackageLabel(application, packageName, user)
+ packageLabelCache[packageName] = packageLabel
+
+ return packageLabel
+ }
+
/** Companion object for [PermissionUsageDetailsViewModel]. */
companion object {
private const val ONE_HOUR_MS = 3_600_000
@@ -629,7 +688,8 @@ class PermissionUsageDetailsViewModel(
listOf(
Manifest.permission_group.CAMERA,
Manifest.permission_group.LOCATION,
- Manifest.permission_group.MICROPHONE)
+ Manifest.permission_group.MICROPHONE
+ )
.flatMap { group -> PermissionMapping.getPlatformPermissionNamesOfGroup(group) }
.mapNotNull { permName -> AppOpsManager.permissionToOp(permName) }
.toMutableSet()
@@ -659,7 +719,8 @@ class PermissionUsageDetailsViewModel(
accessStartTime,
accessEndTime,
showingAttribution,
- attributionTags)
+ attributionTags
+ )
?: getDefaultManageAppPermissionsIntent(packageName, userHandle)
}
@@ -692,11 +753,16 @@ class PermissionUsageDetailsViewModel(
}
val resolveInfo =
context.packageManager.resolveActivity(
- intent, PackageManager.ResolveInfoFlags.of(0))
- if (resolveInfo?.activityInfo == null ||
- !Objects.equals(
- resolveInfo.activityInfo.permission,
- Manifest.permission.START_VIEW_PERMISSION_USAGE)) {
+ intent,
+ PackageManager.ResolveInfoFlags.of(0)
+ )
+ if (
+ resolveInfo?.activityInfo == null ||
+ !Objects.equals(
+ resolveInfo.activityInfo.permission,
+ Manifest.permission.START_VIEW_PERMISSION_USAGE
+ )
+ ) {
return null
}
intent.component = ComponentName(packageName, resolveInfo.activityInfo.name)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt
index eeac22124..6e633dad5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt
@@ -98,7 +98,8 @@ class PermissionUsageViewModel(
showSystem,
show7Days,
mAllLightPackageOpsLiveData.containsSystemAppUsages(startTime),
- mAllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts(startTime, showSystem))
+ mAllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts(startTime, showSystem)
+ )
}
/** Builds a map of permission groups to the number of apps that recently accessed them. */
@@ -119,9 +120,14 @@ class PermissionUsageViewModel(
lightPackageOps.lastPermissionGroupAccessTimesMs.entries
.filterOutExemptedPermissionGroupsFromKeys()
.filterOutPermissionsNotRequestedByApp(
- lightPackageOps.packageName, lightPackageOps.userHandle)
+ lightPackageOps.packageName,
+ lightPackageOps.userHandle
+ )
.filterOutSystemAppPermissionsIfNecessary(
- showSystem, lightPackageOps.packageName, lightPackageOps.userHandle)
+ showSystem,
+ lightPackageOps.packageName,
+ lightPackageOps.userHandle
+ )
.filterAccessTimeLaterThan(startTime)
val recentlyUsedPermissions: List<String> = permGroupsToLastAccess.map { it.key }
@@ -147,10 +153,14 @@ class PermissionUsageViewModel(
.filterAccessTimeLaterThan(startTime)
.map { it.key }
.toSet()
- if (recentlyUsedPermissions
- .filterOutExemptedPermissionGroups()
- .containsSystemAppPermission(
- lightPackageOps.packageName, lightPackageOps.userHandle)) {
+ if (
+ recentlyUsedPermissions
+ .filterOutExemptedPermissionGroups()
+ .containsSystemAppPermission(
+ lightPackageOps.packageName,
+ lightPackageOps.userHandle
+ )
+ ) {
return true
}
}
@@ -186,9 +196,11 @@ class PermissionUsageViewModel(
// The Telecom doesn't request microphone or camera permissions. However, telecom app may
// use these permissions and they are considered system app permissions, so we return true
// even if the AppPermGroupUiInfo is unavailable.
- if (appPermissionId.packageName == TELECOM_PACKAGE &&
- (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA ||
- appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE)) {
+ if (
+ appPermissionId.packageName == TELECOM_PACKAGE &&
+ (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA ||
+ appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE)
+ ) {
return true
}
return false
@@ -302,37 +314,56 @@ class PermissionUsageViewModel(
if (mAllLightPackageOpsLiveData.isStale) {
return
}
+ if (
+ appPermGroupUiInfoLiveDataList.any {
+ !it.value.isInitialized || it.value.isStale
+ }
+ ) {
+ return
+ }
+ if (
+ lightPackageInfoLiveDataMap.any { !it.value.isInitialized || it.value.isStale }
+ ) {
+ return
+ }
+ val packageOps: Map<Pair<String, UserHandle>, LightPackageOps> =
+ mAllLightPackageOpsLiveData.value ?: emptyMap()
val appPermissionIds = mutableListOf<AppPermissionId>()
- val allPackages = mAllLightPackageOpsLiveData.value?.keys ?: setOf()
- for (packageWithUserHandle: Pair<String, UserHandle> in allPackages) {
- for (permissionGroup in getAllEligiblePermissionGroups()) {
+ val allPackages = packageOps.keys
+
+ packageOps.forEach { (packageWithUserHandle, pkgOps) ->
+ pkgOps.lastPermissionGroupAccessTimesMs.keys.forEach { permissionGroup ->
appPermissionIds.add(
AppPermissionId(
packageWithUserHandle.first,
packageWithUserHandle.second,
permissionGroup,
- ))
+ )
+ )
}
}
setSourcesToDifference(
appPermissionIds,
appPermGroupUiInfoLiveDataList,
- getAppPermGroupUiInfoLiveData) {
- update()
- }
+ getAppPermGroupUiInfoLiveData
+ ) {
+ update()
+ }
setSourcesToDifference(
- allPackages, lightPackageInfoLiveDataMap, getLightPackageInfoLiveData) {
- update()
- }
+ allPackages,
+ lightPackageInfoLiveDataMap,
+ getLightPackageInfoLiveData
+ ) {
+ update()
+ }
if (lightPackageInfoLiveDataMap.any { it.value.isStale }) {
return
}
-
if (appPermGroupUiInfoLiveDataList.any { it.value.isStale }) {
return
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt
index 9c9a55523..0d8a129e9 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/
@file:Suppress("DEPRECATION")
+
package com.android.permissioncontroller.permission.ui.model.v31
import android.Manifest
@@ -62,16 +63,11 @@ private const val CALL_OP_USAGE_KEY = "CALL_OP_USAGE"
private const val USAGES_KEY = "USAGES_KEY"
private const val MIC_MUTED_KEY = "MIC_MUTED_KEY"
-/**
- * ViewModel for {@link ReviewOngoingUsageFragment}
- */
-class ReviewOngoingUsageViewModel(
- state: SavedStateHandle,
- extraDurationMills: Long
-) : ViewModel() {
+/** ViewModel for {@link ReviewOngoingUsageFragment} */
+class ReviewOngoingUsageViewModel(state: SavedStateHandle, extraDurationMills: Long) : ViewModel() {
/** Time of oldest usages considered */
- private val startTime = max(state.get<Long>(FIRST_OPENED_KEY)!! - extraDurationMills,
- Instant.EPOCH.toEpochMilli())
+ private val startTime =
+ max(state.get<Long>(FIRST_OPENED_KEY)!! - extraDurationMills, Instant.EPOCH.toEpochMilli())
private val SYSTEM_PKG = "android"
@@ -80,8 +76,10 @@ class ReviewOngoingUsageViewModel(
val appUsages: Map<PackageAttribution, Set<String>>,
/** Op-names of phone call accesses */
val callUsages: Collection<String>,
- /** A map of attribution, packageName and user -> list of attribution labels to show with
- * microphone*/
+ /**
+ * A map of attribution, packageName and user -> list of attribution labels to show with
+ * microphone
+ */
val shownAttributions: Map<PackageAttribution, List<CharSequence>> = emptyMap()
)
@@ -101,292 +99,355 @@ class ReviewOngoingUsageViewModel(
*
* <p>Note: This does not use a cached live-data to avoid getting stale data
*/
- private val permGroupUsages = LoadAndFreezeLifeData(state, USAGES_KEY,
- PermGroupUsageLiveData(PermissionControllerApplication.get(),
- if (shouldShowPermissionsDashboard() || shouldShowLocationIndicators()) {
- listOf(CAMERA, LOCATION, MICROPHONE)
- } else {
- listOf(CAMERA, MICROPHONE)
- }, System.currentTimeMillis() - startTime))
+ private val permGroupUsages =
+ LoadAndFreezeLifeData(
+ state,
+ USAGES_KEY,
+ PermGroupUsageLiveData(
+ PermissionControllerApplication.get(),
+ if (shouldShowPermissionsDashboard() || shouldShowLocationIndicators()) {
+ listOf(CAMERA, LOCATION, MICROPHONE)
+ } else {
+ listOf(CAMERA, MICROPHONE)
+ },
+ System.currentTimeMillis() - startTime
+ )
+ )
- /**
- * Whether the mic is muted
- */
+ /** Whether the mic is muted */
private val isMicMuted = LoadAndFreezeLifeData(state, MIC_MUTED_KEY, micMutedLiveData)
/** App runtime permission usages */
- private val appUsagesLiveData = object : SmartUpdateMediatorLiveData<Map<PackageAttribution,
- Set<String>>>() {
- private val app = PermissionControllerApplication.get()
-
- init {
- addSource(permGroupUsages) {
- update()
- }
+ private val appUsagesLiveData =
+ object : SmartUpdateMediatorLiveData<Map<PackageAttribution, Set<String>>>() {
+ private val app = PermissionControllerApplication.get()
- addSource(isMicMuted) {
- update()
- }
- }
+ init {
+ addSource(permGroupUsages) { update() }
- override fun onUpdate() {
- if (!permGroupUsages.isInitialized || !isMicMuted.isInitialized) {
- return
+ addSource(isMicMuted) { update() }
}
- if (permGroupUsages.value == null) {
- value = null
- return
- }
+ override fun onUpdate() {
+ if (!permGroupUsages.isInitialized || !isMicMuted.isInitialized) {
+ return
+ }
- // Filter out system package
- val filteredUsages = mutableMapOf<PackageAttribution, MutableSet<String>>()
- for ((permGroupName, usages) in permGroupUsages.value!!) {
- if (permGroupName == MICROPHONE && isMicMuted.value == true) {
- continue
+ if (permGroupUsages.value == null) {
+ value = null
+ return
}
- for (usage in usages) {
- if (usage.packageName != SYSTEM_PKG) {
- filteredUsages.getOrPut(getPackageAttr(usage),
- { mutableSetOf() }).add(permGroupName)
+ // Filter out system package
+ val filteredUsages = mutableMapOf<PackageAttribution, MutableSet<String>>()
+ for ((permGroupName, usages) in permGroupUsages.value!!) {
+ if (permGroupName == MICROPHONE && isMicMuted.value == true) {
+ continue
+ }
+
+ for (usage in usages) {
+ if (usage.packageName != SYSTEM_PKG) {
+ filteredUsages
+ .getOrPut(getPackageAttr(usage), { mutableSetOf() })
+ .add(permGroupName)
+ }
}
}
- }
- value = filteredUsages
- }
+ value = filteredUsages
+ }
- // TODO ntmyren: Replace this with better check if this moves beyond teamfood
- private fun isAppPredictor(usage: OpAccess): Boolean {
- return Utils.getUserContext(app, usage.user).packageManager.checkPermission(
- Manifest.permission.MANAGE_APP_PREDICTIONS, usage.packageName) ==
- PackageManager.PERMISSION_GRANTED
+ // TODO ntmyren: Replace this with better check if this moves beyond teamfood
+ private fun isAppPredictor(usage: OpAccess): Boolean {
+ return Utils.getUserContext(app, usage.user)
+ .packageManager
+ .checkPermission(
+ Manifest.permission.MANAGE_APP_PREDICTIONS,
+ usage.packageName
+ ) == PackageManager.PERMISSION_GRANTED
+ }
}
- }
/**
- * Gets all trusted proxied voice IME and voice recognition microphone uses, and get the
- * label needed to display with it, as well as information about the proxy whose label is being
- * shown, if applicable.
+ * Gets all trusted proxied voice IME and voice recognition microphone uses, and get the label
+ * needed to display with it, as well as information about the proxy whose label is being shown,
+ * if applicable.
*/
- private val trustedAttrsLiveData = object : SmartAsyncMediatorLiveData<
- Map<PackageAttribution, CharSequence>>() {
- private val VOICE_IME_SUBTYPE = "voice"
+ private val trustedAttrsLiveData =
+ object : SmartAsyncMediatorLiveData<Map<PackageAttribution, CharSequence>>() {
+ private val VOICE_IME_SUBTYPE = "voice"
- private val attributionLabelLiveDatas =
- mutableMapOf<Triple<String?, String, UserHandle>, AttributionLabelLiveData>()
+ private val attributionLabelLiveDatas =
+ mutableMapOf<Triple<String?, String, UserHandle>, AttributionLabelLiveData>()
- init {
- addSource(permGroupUsages) {
- updateAsync()
+ init {
+ addSource(permGroupUsages) { updateAsync() }
}
- }
- override suspend fun loadDataAndPostValue(job: Job) {
- if (!permGroupUsages.isInitialized) {
- return
- }
- val usages = permGroupUsages.value?.get(MICROPHONE) ?: run {
- postValue(emptyMap())
- return
- }
- val proxies = usages.mapNotNull { it.proxyAccess }
-
- val proxyLabelLiveDatas = proxies.map {
- Triple(it.attributionTag, it.packageName, it.user) }
- val toAddLabelLiveDatas = (usages.map { Triple(it.attributionTag, it.packageName,
- it.user) } + proxyLabelLiveDatas).distinct()
- val getLiveDataFun = { key: Triple<String?, String, UserHandle> ->
- AttributionLabelLiveData[key] }
- setSourcesToDifference(toAddLabelLiveDatas, attributionLabelLiveDatas, getLiveDataFun)
-
- if (attributionLabelLiveDatas.any { !it.value.isInitialized }) {
- return
- }
+ override suspend fun loadDataAndPostValue(job: Job) {
+ if (!permGroupUsages.isInitialized) {
+ return
+ }
+ val usages =
+ permGroupUsages.value?.get(MICROPHONE)
+ ?: run {
+ postValue(emptyMap())
+ return
+ }
+ val proxies = usages.mapNotNull { it.proxyAccess }
+
+ val proxyLabelLiveDatas =
+ proxies.map { Triple(it.attributionTag, it.packageName, it.user) }
+ val toAddLabelLiveDatas =
+ (usages.map { Triple(it.attributionTag, it.packageName, it.user) } +
+ proxyLabelLiveDatas)
+ .distinct()
+ val getLiveDataFun = { key: Triple<String?, String, UserHandle> ->
+ AttributionLabelLiveData[key]
+ }
+ setSourcesToDifference(
+ toAddLabelLiveDatas,
+ attributionLabelLiveDatas,
+ getLiveDataFun
+ )
+
+ if (attributionLabelLiveDatas.any { !it.value.isInitialized }) {
+ return
+ }
- val approvedAttrs = mutableMapOf<PackageAttribution, String>()
- for (user in usages.map { it.user }.distinct()) {
- val userContext = Utils.getUserContext(PermissionControllerApplication.get(), user)
-
- // TODO ntmyren: Observe changes, possibly split into separate LiveDatas
- val voiceInputs = mutableMapOf<String, CharSequence>()
- userContext.getSystemService(InputMethodManager::class.java)!!
- .enabledInputMethodList.forEach {
- for (i in 0 until it.subtypeCount) {
- if (it.getSubtypeAt(i).mode == VOICE_IME_SUBTYPE) {
- voiceInputs[it.packageName] =
- it.serviceInfo.loadSafeLabel(userContext.packageManager,
- Float.MAX_VALUE, 0)
- break
+ val approvedAttrs = mutableMapOf<PackageAttribution, String>()
+ for (user in usages.map { it.user }.distinct()) {
+ val userContext =
+ Utils.getUserContext(PermissionControllerApplication.get(), user)
+
+ // TODO ntmyren: Observe changes, possibly split into separate LiveDatas
+ val voiceInputs = mutableMapOf<String, CharSequence>()
+ userContext
+ .getSystemService(InputMethodManager::class.java)!!
+ .enabledInputMethodList
+ .forEach {
+ for (i in 0 until it.subtypeCount) {
+ if (it.getSubtypeAt(i).mode == VOICE_IME_SUBTYPE) {
+ voiceInputs[it.packageName] =
+ it.serviceInfo.loadSafeLabel(
+ userContext.packageManager,
+ Float.MAX_VALUE,
+ 0
+ )
+ break
+ }
}
}
+
+ // Get the currently selected recognizer from the secure setting.
+ val recognitionPackageName =
+ Settings.Secure.getString(
+ userContext.contentResolver,
+ // Settings.Secure.VOICE_RECOGNITION_SERVICE
+ "voice_recognition_service"
+ )
+ ?.let(ComponentName::unflattenFromString)
+ ?.packageName
+
+ val recognizers = mutableMapOf<String, CharSequence>()
+ val availableRecognizers =
+ userContext.packageManager.queryIntentServices(
+ Intent(RecognitionService.SERVICE_INTERFACE),
+ PackageManager.GET_META_DATA
+ )
+ availableRecognizers.forEach {
+ val sI = it.serviceInfo
+ if (sI.packageName == recognitionPackageName) {
+ recognizers[sI.packageName] =
+ sI.loadSafeLabel(userContext.packageManager, Float.MAX_VALUE, 0)
+ }
}
- // Get the currently selected recognizer from the secure setting.
- val recognitionPackageName = Settings.Secure.getString(userContext.contentResolver,
- // Settings.Secure.VOICE_RECOGNITION_SERVICE
- "voice_recognition_service")
- ?.let(ComponentName::unflattenFromString)?.packageName
-
- val recognizers = mutableMapOf<String, CharSequence>()
- val availableRecognizers = userContext.packageManager.queryIntentServices(
- Intent(RecognitionService.SERVICE_INTERFACE), PackageManager.GET_META_DATA)
- availableRecognizers.forEach {
- val sI = it.serviceInfo
- if (sI.packageName == recognitionPackageName) {
- recognizers[sI.packageName] = sI.loadSafeLabel(userContext.packageManager,
- Float.MAX_VALUE, 0)
+ val recognizerIntents = mutableMapOf<String, CharSequence>()
+ val availableRecognizerIntents =
+ userContext.packageManager.queryIntentActivities(
+ Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
+ PackageManager.GET_META_DATA
+ )
+ availableRecognizers.forEach { rI ->
+ val servicePkg = rI.serviceInfo.packageName
+ if (
+ servicePkg == recognitionPackageName &&
+ availableRecognizerIntents.any {
+ it.activityInfo.packageName == servicePkg
+ }
+ ) {
+ // If this recognizer intent is also a recognizer service, and is
+ // trusted,
+ // Then attribute to voice recognition
+ recognizerIntents[servicePkg] =
+ rI.serviceInfo.loadSafeLabel(
+ userContext.packageManager,
+ Float.MAX_VALUE,
+ 0
+ )
+ }
}
- }
- val recognizerIntents = mutableMapOf<String, CharSequence>()
- val availableRecognizerIntents = userContext.packageManager.queryIntentActivities(
- Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), PackageManager.GET_META_DATA)
- availableRecognizers.forEach { rI ->
- val servicePkg = rI.serviceInfo.packageName
- if (servicePkg == recognitionPackageName && availableRecognizerIntents.any {
- it.activityInfo.packageName == servicePkg }) {
- // If this recognizer intent is also a recognizer service, and is trusted,
- // Then attribute to voice recognition
- recognizerIntents[servicePkg] =
- rI.serviceInfo.loadSafeLabel(userContext.packageManager,
- Float.MAX_VALUE, 0)
+ // get attribution labels for voice IME, recognition intents, and recognition
+ // services
+ for (opAccess in usages) {
+ setTrustedAttrsForAccess(
+ userContext,
+ opAccess,
+ user,
+ false,
+ voiceInputs,
+ approvedAttrs
+ )
+ setTrustedAttrsForAccess(
+ userContext,
+ opAccess,
+ user,
+ false,
+ recognizerIntents,
+ approvedAttrs
+ )
+ setTrustedAttrsForAccess(
+ userContext,
+ opAccess,
+ user,
+ true,
+ recognizers,
+ approvedAttrs
+ )
}
}
+ postValue(approvedAttrs)
+ }
+
+ private fun setTrustedAttrsForAccess(
+ context: Context,
+ opAccess: OpAccess,
+ currUser: UserHandle,
+ getProxyLabel: Boolean,
+ trustedMap: Map<String, CharSequence>,
+ toSetMap: MutableMap<PackageAttribution, String>
+ ) {
+ val access =
+ if (getProxyLabel) {
+ opAccess.proxyAccess
+ } else {
+ opAccess
+ }
- // get attribution labels for voice IME, recognition intents, and recognition
- // services
- for (opAccess in usages) {
- setTrustedAttrsForAccess(userContext, opAccess, user, false, voiceInputs,
- approvedAttrs)
- setTrustedAttrsForAccess(userContext, opAccess, user, false, recognizerIntents,
- approvedAttrs)
- setTrustedAttrsForAccess(userContext, opAccess, user, true, recognizers,
- approvedAttrs)
+ if (
+ access == null || access.user != currUser || access.packageName !in trustedMap
+ ) {
+ return
}
- }
- postValue(approvedAttrs)
- }
-
- private fun setTrustedAttrsForAccess(
- context: Context,
- opAccess: OpAccess,
- currUser: UserHandle,
- getProxyLabel: Boolean,
- trustedMap: Map<String, CharSequence>,
- toSetMap: MutableMap<PackageAttribution, String>
- ) {
- val access = if (getProxyLabel) {
- opAccess.proxyAccess
- } else {
- opAccess
- }
- if (access == null || access.user != currUser || access.packageName !in trustedMap) {
- return
- }
-
- val appAttr = getPackageAttr(access)
- val packageName = access.packageName
-
- val labelResId = attributionLabelLiveDatas[Triple(access.attributionTag,
- access.packageName, access.user)]?.value ?: 0
- val label = try {
- context.createPackageContext(packageName, 0)
- .getString(labelResId)
- } catch (e: Exception) {
- return
- }
- if (trustedMap[packageName] == label) {
- toSetMap[appAttr] = label
+ val appAttr = getPackageAttr(access)
+ val packageName = access.packageName
+
+ val labelResId =
+ attributionLabelLiveDatas[
+ Triple(access.attributionTag, access.packageName, access.user)]
+ ?.value
+ ?: 0
+ val label =
+ try {
+ context.createPackageContext(packageName, 0).getString(labelResId)
+ } catch (e: Exception) {
+ return
+ }
+ if (trustedMap[packageName] == label) {
+ toSetMap[appAttr] = label
+ }
}
}
- }
/**
* Get all chains of proxy usages. A proxy chain is defined as one usage at the root, then
* further proxy usages, where the app and attribution tag of the proxy matches the previous
* usage in the chain.
*/
- private val proxyChainsLiveData = object : SmartUpdateMediatorLiveData<Set<List<OpAccess>>>() {
- init {
- addSource(permGroupUsages) {
- update()
- }
- }
- override fun onUpdate() {
- if (!permGroupUsages.isInitialized) {
- return
- }
- val usages = permGroupUsages.value?.get(MICROPHONE) ?: emptyList()
- // a map of chain start -> in progress chain
- val proxyChains = mutableMapOf<PackageAttribution, MutableList<OpAccess>>()
-
- val remainingProxyChainUsages = mutableMapOf<PackageAttribution, OpAccess>()
- for (usage in usages) {
- remainingProxyChainUsages[getPackageAttr(usage)] = usage
+ private val proxyChainsLiveData =
+ object : SmartUpdateMediatorLiveData<Set<List<OpAccess>>>() {
+ init {
+ addSource(permGroupUsages) { update() }
}
- // find all one-link chains (that is, all proxied apps whose proxy is not included in
- // the usage list)
- for (usage in usages) {
- val usageAttr = getPackageAttr(usage)
- val proxyAttr = getPackageAttr(usage.proxyAccess ?: continue)
- if (!usages.any { getPackageAttr(it) == proxyAttr }) {
- proxyChains[usageAttr] = mutableListOf(usage)
- remainingProxyChainUsages.remove(usageAttr)
+ override fun onUpdate() {
+ if (!permGroupUsages.isInitialized) {
+ return
}
- }
+ val usages = permGroupUsages.value?.get(MICROPHONE) ?: emptyList()
+ // a map of chain start -> in progress chain
+ val proxyChains = mutableMapOf<PackageAttribution, MutableList<OpAccess>>()
- // find all possible starting points for chains
- for ((usageAttr, usage) in remainingProxyChainUsages.toMap()) {
- // If this usage has a proxy, but is not a proxy, it is the start of a chain.
- // If it has no proxy, and isn't a proxy, remove it.
- if (!remainingProxyChainUsages.values.any { it.proxyAccess != null &&
- getPackageAttr(it.proxyAccess) == usageAttr }) {
- if (usage.proxyAccess != null) {
+ val remainingProxyChainUsages = mutableMapOf<PackageAttribution, OpAccess>()
+ for (usage in usages) {
+ remainingProxyChainUsages[getPackageAttr(usage)] = usage
+ }
+ // find all one-link chains (that is, all proxied apps whose proxy is not included
+ // in
+ // the usage list)
+ for (usage in usages) {
+ val usageAttr = getPackageAttr(usage)
+ val proxyAttr = getPackageAttr(usage.proxyAccess ?: continue)
+ if (!usages.any { getPackageAttr(it) == proxyAttr }) {
proxyChains[usageAttr] = mutableListOf(usage)
- } else {
remainingProxyChainUsages.remove(usageAttr)
}
}
- }
- // assemble the chains
- for ((startUsageAttr, proxyChain) in proxyChains) {
- var currentUsage = remainingProxyChainUsages[startUsageAttr] ?: continue
- while (currentUsage.proxyAccess != null) {
- val currPackageAttr = getPackageAttr(currentUsage.proxyAccess!!)
- currentUsage = remainingProxyChainUsages[currPackageAttr] ?: break
- if (proxyChain.any { it == currentUsage }) {
- // we have a cycle, and should break
- break
+ // find all possible starting points for chains
+ for ((usageAttr, usage) in remainingProxyChainUsages.toMap()) {
+ // If this usage has a proxy, but is not a proxy, it is the start of a chain.
+ // If it has no proxy, and isn't a proxy, remove it.
+ if (
+ !remainingProxyChainUsages.values.any {
+ it.proxyAccess != null && getPackageAttr(it.proxyAccess) == usageAttr
+ }
+ ) {
+ if (usage.proxyAccess != null) {
+ proxyChains[usageAttr] = mutableListOf(usage)
+ } else {
+ remainingProxyChainUsages.remove(usageAttr)
+ }
}
- proxyChain.add(currentUsage)
}
- // invert the lists, so the element without a proxy is first on the list
- proxyChain.reverse()
- }
- value = proxyChains.values.toSet()
+ // assemble the chains
+ for ((startUsageAttr, proxyChain) in proxyChains) {
+ var currentUsage = remainingProxyChainUsages[startUsageAttr] ?: continue
+ while (currentUsage.proxyAccess != null) {
+ val currPackageAttr = getPackageAttr(currentUsage.proxyAccess!!)
+ currentUsage = remainingProxyChainUsages[currPackageAttr] ?: break
+ if (proxyChain.any { it == currentUsage }) {
+ // we have a cycle, and should break
+ break
+ }
+ proxyChain.add(currentUsage)
+ }
+ // invert the lists, so the element without a proxy is first on the list
+ proxyChain.reverse()
+ }
+
+ value = proxyChains.values.toSet()
+ }
}
- }
/** Phone call usages */
private val callOpUsageLiveData =
object : SmartUpdateMediatorLiveData<Collection<String>>() {
- private val rawOps = LoadAndFreezeLifeData(state, CALL_OP_USAGE_KEY,
- OpUsageLiveData[listOf(PHONE_CALL, VIDEO_CALL),
- System.currentTimeMillis() - startTime])
+ private val rawOps =
+ LoadAndFreezeLifeData(
+ state,
+ CALL_OP_USAGE_KEY,
+ OpUsageLiveData[
+ listOf(PHONE_CALL, VIDEO_CALL), System.currentTimeMillis() - startTime]
+ )
init {
- addSource(rawOps) {
- update()
- }
+ addSource(rawOps) { update() }
- addSource(isMicMuted) {
- update()
- }
+ addSource(isMicMuted) { update() }
}
override fun onUpdate() {
@@ -394,145 +455,152 @@ class ReviewOngoingUsageViewModel(
return
}
- value = if (isMicMuted.value == true) {
- rawOps.value!!.keys.filter { it != PHONE_CALL }
- } else {
- rawOps.value!!.keys
- }
+ value =
+ if (isMicMuted.value == true) {
+ rawOps.value!!.keys.filter { it != PHONE_CALL }
+ } else {
+ rawOps.value!!.keys
+ }
}
}
/** App, system, and call usages in a single, nice, handy package */
- val usages = object : SmartAsyncMediatorLiveData<Usages>() {
- private val app = PermissionControllerApplication.get()
+ val usages =
+ object : SmartAsyncMediatorLiveData<Usages>() {
+ private val app = PermissionControllerApplication.get()
- init {
- addSource(appUsagesLiveData) {
- update()
- }
-
- addSource(callOpUsageLiveData) {
- update()
- }
-
- addSource(trustedAttrsLiveData) {
- update()
- }
+ init {
+ addSource(appUsagesLiveData) { update() }
- addSource(proxyChainsLiveData) {
- update()
- }
- }
+ addSource(callOpUsageLiveData) { update() }
- override suspend fun loadDataAndPostValue(job: Job) {
- if (job.isCancelled) {
- return
- }
+ addSource(trustedAttrsLiveData) { update() }
- if (!callOpUsageLiveData.isInitialized || !appUsagesLiveData.isInitialized ||
- !trustedAttrsLiveData.isInitialized || !proxyChainsLiveData.isInitialized) {
- return
+ addSource(proxyChainsLiveData) { update() }
}
- val callOpUsages = callOpUsageLiveData.value?.toMutableSet()
- val appUsages = appUsagesLiveData.value?.toMutableMap()
- val approvedAttrs = trustedAttrsLiveData.value?.toMutableMap() ?: mutableMapOf()
- val proxyChains = proxyChainsLiveData.value ?: emptySet()
+ override suspend fun loadDataAndPostValue(job: Job) {
+ if (job.isCancelled) {
+ return
+ }
- if (callOpUsages == null || appUsages == null) {
- postValue(null)
- return
- }
+ if (
+ !callOpUsageLiveData.isInitialized ||
+ !appUsagesLiveData.isInitialized ||
+ !trustedAttrsLiveData.isInitialized ||
+ !proxyChainsLiveData.isInitialized
+ ) {
+ return
+ }
- // If there is nothing to show the dialog should be closed, hence return a "invalid"
- // value
- if (appUsages.isEmpty() && callOpUsages.isEmpty()) {
- postValue(null)
- return
- }
+ val callOpUsages = callOpUsageLiveData.value?.toMutableSet()
+ val appUsages = appUsagesLiveData.value?.toMutableMap()
+ val approvedAttrs = trustedAttrsLiveData.value?.toMutableMap() ?: mutableMapOf()
+ val proxyChains = proxyChainsLiveData.value ?: emptySet()
- // If we are in a VOIP call (aka MODE_IN_COMMUNICATION), and have a carrier privileged
- // app using the mic, hide phone usage.
- val audioManager = app.getSystemService(AudioManager::class.java)!!
- if (callOpUsages.isNotEmpty() && audioManager.mode == MODE_IN_COMMUNICATION) {
- val telephonyManager = app.getSystemService(TelephonyManager::class.java)!!
- for ((pkg, usages) in appUsages) {
- if (telephonyManager.checkCarrierPrivilegesForPackage(pkg.packageName) ==
- CARRIER_PRIVILEGE_STATUS_HAS_ACCESS && usages.contains(MICROPHONE)) {
- callOpUsages.clear()
- continue
- }
+ if (callOpUsages == null || appUsages == null) {
+ postValue(null)
+ return
}
- }
- // Find labels for proxies, and assign them to the proper app, removing other usages
- val approvedLabels = mutableMapOf<PackageAttribution, List<CharSequence>>()
- for (chain in proxyChains) {
- // if the final link in the chain is not user sensitive, do not show the chain
- if (getPackageAttr(chain[chain.size - 1]) !in appUsages) {
- continue
+ // If there is nothing to show the dialog should be closed, hence return a "invalid"
+ // value
+ if (appUsages.isEmpty() && callOpUsages.isEmpty()) {
+ postValue(null)
+ return
}
- // if the proxy access is missing, for some reason, do not show the proxy
- if (chain.size == 1) {
- continue
+ // If we are in a VOIP call (aka MODE_IN_COMMUNICATION), and have a carrier
+ // privileged
+ // app using the mic, hide phone usage.
+ val audioManager = app.getSystemService(AudioManager::class.java)!!
+ if (callOpUsages.isNotEmpty() && audioManager.mode == MODE_IN_COMMUNICATION) {
+ val telephonyManager = app.getSystemService(TelephonyManager::class.java)!!
+ for ((pkg, usages) in appUsages) {
+ if (
+ telephonyManager.checkCarrierPrivilegesForPackage(pkg.packageName) ==
+ CARRIER_PRIVILEGE_STATUS_HAS_ACCESS && usages.contains(MICROPHONE)
+ ) {
+ callOpUsages.clear()
+ continue
+ }
+ }
}
- val labels = mutableListOf<CharSequence>()
- for ((idx, opAccess) in chain.withIndex()) {
- val appAttr = getPackageAttr(opAccess)
- // If this is the last link in the proxy chain, assign it the series of labels
- // Else, if it has a special label, add that label
- // Else, if there are no other apps in the remaining part of the chain which
- // have the same package name, add the app label
- // If it is not the last link in the chain, remove its attribution
- if (idx == chain.size - 1) {
- approvedLabels[appAttr] = labels
+ // Find labels for proxies, and assign them to the proper app, removing other usages
+ val approvedLabels = mutableMapOf<PackageAttribution, List<CharSequence>>()
+ for (chain in proxyChains) {
+ // if the final link in the chain is not user sensitive, do not show the chain
+ if (getPackageAttr(chain[chain.size - 1]) !in appUsages) {
continue
- } else if (appAttr in approvedAttrs) {
- labels.add(approvedAttrs[appAttr]!!)
- approvedAttrs.remove(appAttr)
- } else if (chain.subList(idx + 1, chain.size).all {
- it.packageName != opAccess.packageName } &&
- opAccess.packageName != SYSTEM_PKG) {
- labels.add(KotlinUtils.getPackageLabel(app, opAccess.packageName,
- opAccess.user))
}
- appUsages.remove(appAttr)
+
+ // if the proxy access is missing, for some reason, do not show the proxy
+ if (chain.size == 1) {
+ continue
+ }
+
+ val labels = mutableListOf<CharSequence>()
+ for ((idx, opAccess) in chain.withIndex()) {
+ val appAttr = getPackageAttr(opAccess)
+ // If this is the last link in the proxy chain, assign it the series of
+ // labels
+ // Else, if it has a special label, add that label
+ // Else, if there are no other apps in the remaining part of the chain which
+ // have the same package name, add the app label
+ // If it is not the last link in the chain, remove its attribution
+ if (idx == chain.size - 1) {
+ approvedLabels[appAttr] = labels
+ continue
+ } else if (appAttr in approvedAttrs) {
+ labels.add(approvedAttrs[appAttr]!!)
+ approvedAttrs.remove(appAttr)
+ } else if (
+ chain.subList(idx + 1, chain.size).all {
+ it.packageName != opAccess.packageName
+ } && opAccess.packageName != SYSTEM_PKG
+ ) {
+ labels.add(
+ KotlinUtils.getPackageLabel(
+ app,
+ opAccess.packageName,
+ opAccess.user
+ )
+ )
+ }
+ appUsages.remove(appAttr)
+ }
}
- }
- // Any remaining truested attributions must be for non-proxy usages, so add them
- for ((packageAttr, label) in approvedAttrs) {
- approvedLabels[packageAttr] = listOf(label)
- }
+ // Any remaining truested attributions must be for non-proxy usages, so add them
+ for ((packageAttr, label) in approvedAttrs) {
+ approvedLabels[packageAttr] = listOf(label)
+ }
- removeDuplicates(appUsages, approvedLabels.keys)
+ removeDuplicates(appUsages, approvedLabels.keys)
- postValue(Usages(appUsages, callOpUsages, approvedLabels))
- }
+ postValue(Usages(appUsages, callOpUsages, approvedLabels))
+ }
- /**
- * Merge any usages for the same app which don't have a special attribution
- */
- private fun removeDuplicates(
- appUsages: MutableMap<PackageAttribution, Set<String>>,
- approvedUsages: Collection<PackageAttribution>
- ) {
- // Iterate over all non-special attribution keys
- for (packageAttr in appUsages.keys.minus(approvedUsages)) {
- var groupSet = appUsages[packageAttr] ?: continue
-
- for (otherAttr in appUsages.keys.minus(approvedUsages)) {
- if (otherAttr.pkgEq(packageAttr)) {
- groupSet = groupSet.plus(appUsages[otherAttr] ?: emptySet())
- appUsages.remove(otherAttr)
+ /** Merge any usages for the same app which don't have a special attribution */
+ private fun removeDuplicates(
+ appUsages: MutableMap<PackageAttribution, Set<String>>,
+ approvedUsages: Collection<PackageAttribution>
+ ) {
+ // Iterate over all non-special attribution keys
+ for (packageAttr in appUsages.keys.minus(approvedUsages)) {
+ var groupSet = appUsages[packageAttr] ?: continue
+
+ for (otherAttr in appUsages.keys.minus(approvedUsages)) {
+ if (otherAttr.pkgEq(packageAttr)) {
+ groupSet = groupSet.plus(appUsages[otherAttr] ?: emptySet())
+ appUsages.remove(otherAttr)
+ }
}
+ appUsages[packageAttr] = groupSet
}
- appUsages[packageAttr] = groupSet
}
}
- }
private fun getPackageAttr(usage: OpAccess): PackageAttribution {
return PackageAttribution(usage.attributionTag, usage.packageName, usage.user)
@@ -551,10 +619,14 @@ class ReviewOngoingUsageViewModelFactory(
owner: SavedStateRegistryOwner,
defaultArgs: Bundle
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
- override fun <T : ViewModel> create(p0: String, p1: Class<T>, state: SavedStateHandle): T {
- state.set(FIRST_OPENED_KEY, state.get<Long>(FIRST_OPENED_KEY)
+ override fun <T : ViewModel> create(
+ key: String,
+ modelClass: Class<T>,
+ handle: SavedStateHandle
+ ): T {
+ handle.set(FIRST_OPENED_KEY, handle.get<Long>(FIRST_OPENED_KEY)
?: System.currentTimeMillis())
@Suppress("UNCHECKED_CAST")
- return ReviewOngoingUsageViewModel(state, extraDurationMillis) as T
+ return ReviewOngoingUsageViewModel(handle, extraDurationMillis) as T
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionDecisionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionDecisionsViewModel.kt
index 69709264b..bb3c44675 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionDecisionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionDecisionsViewModel.kt
@@ -28,7 +28,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.permissioncontroller.DumpableLog
import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData
import com.android.permissioncontroller.permission.data.UserPackageInfosLiveData
import com.android.permissioncontroller.permission.data.v33.PermissionDecision
@@ -37,9 +36,10 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightPack
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
import com.android.permissioncontroller.permission.ui.auto.AutoReviewPermissionDecisionsFragment
import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.StringUtils
-import kotlinx.coroutines.Job
import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.Job
/** Viewmodel for [ReviewPermissionDecisionsFragment] */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@@ -50,82 +50,94 @@ class ReviewPermissionDecisionsViewModel(val app: Application, val user: UserHan
private val recentPermissionsLiveData = RecentPermissionDecisionsLiveData()
private val userPackageInfosLiveData = UserPackageInfosLiveData[user]
- val recentPermissionDecisionsLiveData = object
- : SmartAsyncMediatorLiveData<List<PermissionDecision>>(
- alwaysUpdateOnActive = false
- ) {
+ val recentPermissionDecisionsLiveData =
+ object :
+ SmartAsyncMediatorLiveData<List<PermissionDecision>>(alwaysUpdateOnActive = false) {
- init {
- addSource(recentPermissionsLiveData) {
- onUpdate()
- }
+ init {
+ addSource(recentPermissionsLiveData) { onUpdate() }
- addSource(userPackageInfosLiveData) {
- onUpdate()
- }
- }
-
- override suspend fun loadDataAndPostValue(job: Job) {
- if (!recentPermissionsLiveData.isInitialized ||
- !userPackageInfosLiveData.isInitialized) {
- return
+ addSource(userPackageInfosLiveData) { onUpdate() }
}
- // create package info lookup map for performance
- val packageToLightPackageInfo: MutableMap<String, LightPackageInfo> = mutableMapOf()
- for (lightPackageInfo in userPackageInfosLiveData.value!!) {
- packageToLightPackageInfo[lightPackageInfo.packageName] = lightPackageInfo
- }
-
- // verify that permission state is still correct. Will also filter out any apps that
- // were uninstalled
- val decisionsToReview: MutableList<PermissionDecision> = mutableListOf()
- for (recentDecision in recentPermissionsLiveData.value!!) {
- val lightPackageInfo = packageToLightPackageInfo[recentDecision.packageName]
- if (lightPackageInfo == null) {
- DumpableLog.e(LOG_TAG, "Package $recentDecision.packageName " +
- "is no longer installed")
- continue
+ override suspend fun loadDataAndPostValue(job: Job) {
+ if (
+ !recentPermissionsLiveData.isInitialized ||
+ !userPackageInfosLiveData.isInitialized
+ ) {
+ return
}
- val grantedGroups: List<String?> = lightPackageInfo.grantedPermissions.map {
- PermissionMapping.getGroupOfPermission(
- app.packageManager.getPermissionInfo(it, /* flags= */ 0))
+
+ // create package info lookup map for performance
+ val packageToLightPackageInfo: MutableMap<String, LightPackageInfo> = mutableMapOf()
+ for (lightPackageInfo in userPackageInfosLiveData.value!!) {
+ packageToLightPackageInfo[lightPackageInfo.packageName] = lightPackageInfo
}
- val currentlyGranted = grantedGroups.contains(recentDecision.permissionGroupName)
- if (currentlyGranted && recentDecision.isGranted) {
- decisionsToReview.add(recentDecision)
- } else if (!currentlyGranted && !recentDecision.isGranted) {
- decisionsToReview.add(recentDecision)
- } else {
- // It's okay for this to happen - the state could change due to role changes,
- // app hibernation, or other non-user-driven actions.
- DumpableLog.d(LOG_TAG,
- "Permission decision grant state (${recentDecision.isGranted}) " +
- "for ${recentDecision.packageName} access to " +
- "${recentDecision.permissionGroupName} does not match current " +
- "grant state $currentlyGranted")
+
+ // verify that permission state is still correct. Will also filter out any apps that
+ // were uninstalled
+ val decisionsToReview: MutableList<PermissionDecision> = mutableListOf()
+ for (recentDecision in recentPermissionsLiveData.value!!) {
+ val lightPackageInfo = packageToLightPackageInfo[recentDecision.packageName]
+ if (lightPackageInfo == null) {
+ DumpableLog.e(
+ LOG_TAG,
+ "Package $recentDecision.packageName " + "is no longer installed"
+ )
+ continue
+ }
+ val grantedGroups: List<String?> =
+ lightPackageInfo.grantedPermissions.map {
+ PermissionMapping.getGroupOfPermission(
+ app.packageManager.getPermissionInfo(it, /* flags= */ 0)
+ )
+ }
+ val currentlyGranted =
+ grantedGroups.contains(recentDecision.permissionGroupName)
+ if (currentlyGranted && recentDecision.isGranted) {
+ decisionsToReview.add(recentDecision)
+ } else if (!currentlyGranted && !recentDecision.isGranted) {
+ decisionsToReview.add(recentDecision)
+ } else {
+ // It's okay for this to happen - the state could change due to role
+ // changes,
+ // app hibernation, or other non-user-driven actions.
+ DumpableLog.d(
+ LOG_TAG,
+ "Permission decision grant state (${recentDecision.isGranted}) " +
+ "for ${recentDecision.packageName} access to " +
+ "${recentDecision.permissionGroupName} does not match current " +
+ "grant state $currentlyGranted"
+ )
+ }
}
- }
- postValue(decisionsToReview)
+ postValue(decisionsToReview)
+ }
}
- }
fun getAppIcon(packageName: String): Drawable? {
return KotlinUtils.getBadgedPackageIcon(app, packageName, user)
}
fun createPreferenceTitle(permissionDecision: PermissionDecision): String {
- val packageLabel = BidiFormatter.getInstance().unicodeWrap(
- KotlinUtils.getPackageLabel(app, permissionDecision.packageName, user))
- val permissionGroupLabel = KotlinUtils.getPermGroupLabel(app,
- permissionDecision.permissionGroupName).toString()
+ val packageLabel =
+ BidiFormatter.getInstance()
+ .unicodeWrap(KotlinUtils.getPackageLabel(app, permissionDecision.packageName, user))
+ val permissionGroupLabel =
+ KotlinUtils.getPermGroupLabel(app, permissionDecision.permissionGroupName).toString()
return if (permissionDecision.isGranted) {
- app.getString(R.string.granted_permission_decision, packageLabel,
- UCharacter.toLowerCase(permissionGroupLabel))
+ app.getString(
+ R.string.granted_permission_decision,
+ packageLabel,
+ UCharacter.toLowerCase(permissionGroupLabel)
+ )
} else {
- app.getString(R.string.denied_permission_decision, packageLabel,
- UCharacter.toLowerCase(permissionGroupLabel))
+ app.getString(
+ R.string.denied_permission_decision,
+ packageLabel,
+ UCharacter.toLowerCase(permissionGroupLabel)
+ )
}
}
@@ -134,8 +146,10 @@ class ReviewPermissionDecisionsViewModel(val app: Application, val user: UserHan
putExtra(Intent.EXTRA_PACKAGE_NAME, permissionDecision.packageName)
putExtra(Intent.EXTRA_PERMISSION_NAME, permissionDecision.permissionGroupName)
putExtra(Intent.EXTRA_USER, user)
- putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME,
- AutoReviewPermissionDecisionsFragment::class.java.name)
+ putExtra(
+ ManagePermissionsActivity.EXTRA_CALLER_NAME,
+ AutoReviewPermissionDecisionsFragment::class.java.name
+ )
}
}
@@ -146,15 +160,12 @@ class ReviewPermissionDecisionsViewModel(val app: Application, val user: UserHan
}
}
-/**
- * Factory for a [ReviewPermissionDecisionsViewModel]
- */
+/** Factory for a [ReviewPermissionDecisionsViewModel] */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class ReviewPermissionDecisionsViewModelFactory(val app: Application, val user: UserHandle) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
- @Suppress("UNCHECKED_CAST")
- return ReviewPermissionDecisionsViewModel(app, user) as T
+ @Suppress("UNCHECKED_CAST") return ReviewPermissionDecisionsViewModel(app, user) as T
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt
index 304dc3db4..7bc0e1e1f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt
@@ -32,14 +32,14 @@ import androidx.lifecycle.ViewModelProvider
import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.PermissionControllerStatsLog
import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED
-import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__INSTALL_SOURCE
import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__HELP_CENTER
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__INSTALL_SOURCE
import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_SETTINGS
import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_VIEWED
import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
import com.android.permissioncontroller.permission.data.get
+import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT
@@ -75,8 +75,8 @@ class PermissionRationaleViewModel(
/**
* Should be invoked by base activity when a valid onActivityResult is received
*
- * @param data [Intent] which may contain result data from a started Activity
- * (various data can be attached to Intent "extras")
+ * @param data [Intent] which may contain result data from a started Activity (various data
+ * can be attached to Intent "extras")
* @return {@code true} if Activity should finish after processing this result
*/
fun shouldFinishActivityForResult(data: Intent?): Boolean
@@ -88,12 +88,12 @@ class PermissionRationaleViewModel(
* should be shown with it.
*/
data class PermissionRationaleInfo(
- val groupName: String,
- val isPreloadedApp: Boolean,
- val installSourcePackageName: String?,
- val installSourceLabel: String?,
- val purposeSet: Set<Int>
- )
+ val groupName: String,
+ val isPreloadedApp: Boolean,
+ val installSourcePackageName: String?,
+ val installSourceLabel: String?,
+ val purposeSet: Set<Int>
+ )
/** A [LiveData] which holds the currently pending PermissionRationaleInfo */
val permissionRationaleInfoLiveData =
@@ -126,8 +126,11 @@ class PermissionRationaleViewModel(
KotlinUtils.getPackageLabel(app, it, Process.myUserHandle())
}
- val purposes = SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(
- safetyLabelInfo.safetyLabel, permissionGroupName)
+ val purposes =
+ SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(
+ safetyLabelInfo.safetyLabel,
+ permissionGroupName
+ )
if (value == null) {
logPermissionRationaleDialogViewed(purposes)
}
@@ -137,7 +140,8 @@ class PermissionRationaleViewModel(
safetyLabelInfo.installSourceInfo.isPreloadedApp,
installSourcePackageName,
installSourceLabel,
- purposes)
+ purposes
+ )
}
}
@@ -147,7 +151,8 @@ class PermissionRationaleViewModel(
fun sendToAppStore(context: Context, installSourcePackageName: String) {
logPermissionRationaleDialogActionReported(
- PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__INSTALL_SOURCE)
+ PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__INSTALL_SOURCE
+ )
val storeIntent = getAppStoreIntent(context, installSourcePackageName, packageName)
context.startActivity(storeIntent)
}
@@ -162,14 +167,17 @@ class PermissionRationaleViewModel(
if (activityResultCallback != null) {
return
}
- activityResultCallback = object : ActivityResultCallback {
- override fun shouldFinishActivityForResult(data: Intent?): Boolean {
- val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED)
- return (returnGroupName != null) && data.hasExtra(EXTRA_RESULT_PERMISSION_RESULT)
+ activityResultCallback =
+ object : ActivityResultCallback {
+ override fun shouldFinishActivityForResult(data: Intent?): Boolean {
+ val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED)
+ return (returnGroupName != null) &&
+ data.hasExtra(EXTRA_RESULT_PERMISSION_RESULT)
+ }
}
- }
logPermissionRationaleDialogActionReported(
- PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_SETTINGS)
+ PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_SETTINGS
+ )
startAppPermissionFragment(activity, groupName)
}
@@ -192,11 +200,13 @@ class PermissionRationaleViewModel(
// Add in some extra locale query parameters
val fullUri =
HelpUtils.uriWithAddedParameters(activity, Uri.parse(getHelpCenterUrlString(activity)))
- val intent = Intent(Intent.ACTION_VIEW, fullUri).apply {
- setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
- }
+ val intent =
+ Intent(Intent.ACTION_VIEW, fullUri).apply {
+ setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ }
logPermissionRationaleDialogActionReported(
- PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__HELP_CENTER)
+ PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__HELP_CENTER
+ )
try {
activity.startActivity(intent)
} catch (e: ActivityNotFoundException) {
@@ -206,14 +216,17 @@ class PermissionRationaleViewModel(
}
private fun startAppPermissionFragment(activity: Activity, groupName: String) {
- val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSION)
- .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
- .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
- .putExtra(Intent.EXTRA_USER, user)
- .putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME,
- PermissionRationaleActivity::class.java.name)
- .putExtra(Constants.EXTRA_SESSION_ID, sessionId)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ val intent =
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSION)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+ .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
+ .putExtra(Intent.EXTRA_USER, user)
+ .putExtra(
+ ManagePermissionsActivity.EXTRA_CALLER_NAME,
+ PermissionRationaleActivity::class.java.name
+ )
+ .putExtra(Constants.EXTRA_SESSION_ID, sessionId)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE)
}
@@ -225,14 +238,24 @@ class PermissionRationaleViewModel(
purposes.forEach { purposeInt ->
purposesPresented = purposesPresented or 1.shl(purposeInt)
}
- PermissionControllerStatsLog.write(PERMISSION_RATIONALE_DIALOG_VIEWED, sessionId, uid,
- permissionGroupName, purposesPresented)
+ PermissionControllerStatsLog.write(
+ PERMISSION_RATIONALE_DIALOG_VIEWED,
+ sessionId,
+ uid,
+ permissionGroupName,
+ purposesPresented
+ )
}
fun logPermissionRationaleDialogActionReported(buttonPressed: Int) {
val uid = KotlinUtils.getPackageUid(app, packageName, user) ?: return
- PermissionControllerStatsLog.write(PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED, sessionId,
- uid, permissionGroupName, buttonPressed)
+ PermissionControllerStatsLog.write(
+ PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED,
+ sessionId,
+ uid,
+ permissionGroupName,
+ buttonPressed
+ )
}
private fun getHelpCenterUrlString(context: Context): String? {
@@ -257,7 +280,12 @@ class PermissionRationaleViewModelFactory(
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return PermissionRationaleViewModel(
- app, packageName, permissionGroupName, sessionId, savedState)
+ app,
+ packageName,
+ permissionGroupName,
+ sessionId,
+ savedState
+ )
as T
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java
index e2df47009..399a694d0 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java
@@ -289,7 +289,7 @@ public class AppPermissionFragment extends SettingsWithHeader
});
mAllowAlwaysButton.setOnPreferenceClickListener((v) -> {
if (mIsStorageGroup) {
- showConfirmDialog(ChangeRequest.GRANT_All_FILE_ACCESS,
+ showConfirmDialog(ChangeRequest.GRANT_ALL_FILE_ACCESS,
R.string.special_file_access_dialog, -1, false);
} else {
mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH,
@@ -464,7 +464,7 @@ public class AppPermissionFragment extends SettingsWithHeader
public Dialog onCreateDialog(Bundle savedInstanceState) {
AppPermissionFragment fragment = (AppPermissionFragment) getTargetFragment();
boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST)
- == ChangeRequest.GRANT_All_FILE_ACCESS;
+ == ChangeRequest.GRANT_ALL_FILE_ACCESS;
int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway;
if (isGrantFileAccess) {
positiveButtonStringResId = R.string.grant_dialog_button_allow;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt
index 07e2ab08f..6797f5dff 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt
@@ -27,16 +27,14 @@ import com.android.permissioncontroller.hibernation.isHibernationEnabled
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY
-/**
- * TV wrapper, with customizations, around [UnusedAppsFragment].
- */
-class TvUnusedAppsFragment : SettingsWithHeader(),
- UnusedAppsFragment.Parent<TvUnusedAppsPreference> {
+/** TV wrapper, with customizations, around [UnusedAppsFragment]. */
+class TvUnusedAppsFragment :
+ SettingsWithHeader(), UnusedAppsFragment.Parent<TvUnusedAppsPreference> {
companion object {
private const val UNUSED_PREFERENCE_KEY = "unused_pref_row_key"
- /** Create a new instance of this fragment. */
+ /** Create a new instance of this fragment. */
@JvmStatic
fun newInstance(): TvUnusedAppsFragment {
return TvUnusedAppsFragment()
@@ -50,15 +48,12 @@ class TvUnusedAppsFragment : SettingsWithHeader(),
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null) {
- val fragment:
- UnusedAppsFragment<TvUnusedAppsFragment, TvUnusedAppsPreference> =
+ val fragment: UnusedAppsFragment<TvUnusedAppsFragment, TvUnusedAppsPreference> =
UnusedAppsFragment.newInstance()
fragment.arguments = arguments
// child fragment does not have its own UI - it will add to the preferences of this
// parent fragment
- childFragmentManager.beginTransaction()
- .add(fragment, null)
- .commit()
+ childFragmentManager.beginTransaction().add(fragment, null).commit()
}
}
@@ -67,8 +62,7 @@ class TvUnusedAppsFragment : SettingsWithHeader(),
if (isHibernationEnabled()) {
preference.summary = getString(R.string.unused_apps_page_tv_summary)
} else {
- preference.summary =
- getString(R.string.auto_revoked_apps_page_summary)
+ preference.summary = getString(R.string.auto_revoked_apps_page_summary)
}
preference.setIcon(R.drawable.ic_info_outline)
preference.isSelectable = false
@@ -93,9 +87,9 @@ class TvUnusedAppsFragment : SettingsWithHeader(),
override fun setEmptyState(empty: Boolean) {
val infoMsgCategory =
- preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
+ preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
val noUnusedAppsPreference: Preference? =
- infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY)
+ infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY)
if (empty && noUnusedAppsPreference == null) {
infoMsgCategory.addPreference(createNoUnusedAppsPreference())
} else if (noUnusedAppsPreference != null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsPreference.kt
index 905aa30d1..bc29e928f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsPreference.kt
@@ -16,10 +16,10 @@
package com.android.permissioncontroller.permission.ui.television
-import androidx.preference.Preference
import android.app.Application
import android.content.Context
import android.os.UserHandle
+import androidx.preference.Preference
import com.android.permissioncontroller.permission.ui.RemovablePref
import com.android.permissioncontroller.permission.utils.KotlinUtils
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt
index b841f3aeb..a2505312d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt
@@ -28,8 +28,7 @@ data class AdvancedConfirmDialogArgs(
@StringRes val messageId: Int,
@StringRes val negativeButtonTextId: Int,
@StringRes val positiveButtonTextId: Int,
-
val changeRequest: AppPermissionViewModel.ChangeRequest? = null,
val setOneTime: Boolean? = null,
val buttonClicked: Int? = null
-) \ No newline at end of file
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/widget/SafetyProtectionSectionView.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/widget/SafetyProtectionSectionView.kt
index daea1c198..9f91f3c18 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/widget/SafetyProtectionSectionView.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/widget/SafetyProtectionSectionView.kt
@@ -49,11 +49,12 @@ class SafetyProtectionSectionView : LinearLayout {
init {
gravity = Gravity.CENTER
orientation = HORIZONTAL
- visibility = if (KotlinUtils.shouldShowSafetyProtectionResources(context)) {
- View.VISIBLE
- } else {
- View.GONE
- }
+ visibility =
+ if (KotlinUtils.shouldShowSafetyProtectionResources(context)) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
}
override fun onFinishInflate() {
@@ -62,8 +63,9 @@ class SafetyProtectionSectionView : LinearLayout {
LayoutInflater.from(context).inflate(R.layout.safety_protection_section, this)
val safetyProtectionDisplayTextView =
requireViewById<TextView>(R.id.safety_protection_display_text)
- safetyProtectionDisplayTextView.setText(Html.fromHtml(
- context.getString(android.R.string.safety_protection_display_text), 0))
+ safetyProtectionDisplayTextView.setText(
+ Html.fromHtml(context.getString(android.R.string.safety_protection_display_text), 0)
+ )
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt
index 0b7537136..02f5e9d41 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt
@@ -38,8 +38,8 @@ interface PermissionRationaleViewHandler {
}
/**
- * Listener interface for getting notified when the user responds to a permission rationale
- * user action.
+ * Listener interface for getting notified when the user responds to a permission rationale user
+ * action.
*/
interface ResultListener {
fun onPermissionRationaleResult(groupName: String?, @Result result: Int)
@@ -60,7 +60,7 @@ interface PermissionRationaleViewHandler {
* @param groupName the name of the permission group
* @param title the title for the dialog
* @param dataSharingSourceMessage the data sharing source data usage comes from message to
- * display the user
+ * display the user
* @param purposeTitle the data usage purposes title to display the user
* @param purposeMessage the data usage purposes message to display the user
* @param learnMoreMessage the more info about safety labels message to display the user
@@ -91,9 +91,7 @@ interface PermissionRationaleViewHandler {
/** Gives a chance for handling the back key. */
fun onBackPressed()
- /**
- * Handles cancel event for the permission rationale dialog.
- */
+ /** Handles cancel event for the permission rationale dialog. */
fun onCancelled() {}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt
index 6a6623da7..a5f78aa53 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt
@@ -26,19 +26,19 @@ import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.Looper
import android.os.UserHandle
-import kotlinx.coroutines.asCoroutineDispatcher
import java.util.concurrent.Executors
+import kotlinx.coroutines.asCoroutineDispatcher
-/**
- * Gets an [Application] instance from a regular [Context]
- */
-val Context.application: Application get() = when (this) {
- is Application -> this
- is Activity -> application
- is Service -> application
- is ContextWrapper -> baseContext.application
- else -> applicationContext as Application
-}
+/** Gets an [Application] instance from a regular [Context] */
+val Context.application: Application
+ get() =
+ when (this) {
+ is Application -> this
+ is Activity -> application
+ is Service -> application
+ is ContextWrapper -> baseContext.application
+ else -> applicationContext as Application
+ }
/**
* The number of threads in the IPC thread pool. Set to the maximum number of binder threads allowed
@@ -46,21 +46,16 @@ val Context.application: Application get() = when (this) {
*/
const val IPC_THREAD_POOL_COUNT = 8
-/**
- * A coroutine dispatcher with a fixed thread pool size, to be used for background tasks
- */
+/** A coroutine dispatcher with a fixed thread pool size, to be used for background tasks */
val IPC = Executors.newFixedThreadPool(IPC_THREAD_POOL_COUNT).asCoroutineDispatcher()
-/**
- * Assert that an operation is running on main thread
- */
-fun ensureMainThread() = check(Looper.myLooper() == Looper.getMainLooper()) {
- "Only meant to be used on the main thread"
-}
+/** Assert that an operation is running on main thread */
+fun ensureMainThread() =
+ check(Looper.myLooper() == Looper.getMainLooper()) {
+ "Only meant to be used on the main thread"
+ }
-/**
- * A more readable version of [PackageManager.updatePermissionFlags]
- */
+/** A more readable version of [PackageManager.updatePermissionFlags] */
fun PackageManager.updatePermissionFlags(
permissionName: String,
packageName: String,
@@ -68,18 +63,14 @@ fun PackageManager.updatePermissionFlags(
vararg flags: Pair<Int, Boolean>
) {
val mask = flags.fold(0, { mask, (flag, _) -> mask or flag })
- val value = flags.fold(0,
- { mask2, (flag, flagValue) -> if (flagValue) mask2 or flag else mask2 })
+ val value =
+ flags.fold(0, { mask2, (flag, flagValue) -> if (flagValue) mask2 or flag else mask2 })
updatePermissionFlags(permissionName, packageName, mask, value, user)
}
-/**
- * Gets a [ComponentInfo] from a [ResolveInfo]
- */
+/** Gets a [ComponentInfo] from a [ResolveInfo] */
val ResolveInfo.componentInfo: ComponentInfo
get() {
return (activityInfo as ComponentInfo?)
- ?: serviceInfo
- ?: providerInfo
- ?: throw IllegalStateException("Missing ComponentInfo!")
- } \ No newline at end of file
+ ?: serviceInfo ?: providerInfo ?: throw IllegalStateException("Missing ComponentInfo!")
+ }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt
index cf8a69b40..96d634ded 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt
@@ -25,24 +25,21 @@ import java.util.Collections.reverse
*/
fun shortStackTrace() = permissionsStackTrace().toShortString()
-/**
- * [StackTraceElement]s of only the permission-related frames
- */
-fun permissionsStackTrace() = stackTraceWithin("com.android.permissioncontroller")
- .dropLastWhile { it.className.contains(".DebugUtils") }
+/** [StackTraceElement]s of only the permission-related frames */
+fun permissionsStackTrace() =
+ stackTraceWithin("com.android.permissioncontroller").dropLastWhile {
+ it.className.contains(".DebugUtils")
+ }
/**
* [StackTraceElement]s of only frames who's [full class name][StackTraceElement.getClassName]
* starts with [pkgPrefix]
*/
-fun stackTraceWithin(pkgPrefix: String) = Thread
- .currentThread()
- .stackTrace
- .dropWhile {
- !it.className.startsWith(pkgPrefix)
- }.takeWhile {
- it.className.startsWith(pkgPrefix)
- }
+fun stackTraceWithin(pkgPrefix: String) =
+ Thread.currentThread()
+ .stackTrace
+ .dropWhile { !it.className.startsWith(pkgPrefix) }
+ .takeWhile { it.className.startsWith(pkgPrefix) }
/**
* Renders a stack trace slice to a short-ish single-line string.
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
index fb188fca1..fcd7ef7b9 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
@@ -25,6 +25,7 @@ import android.Manifest.permission.POST_NOTIFICATIONS
import android.Manifest.permission.READ_MEDIA_IMAGES
import android.Manifest.permission.READ_MEDIA_VIDEO
import android.Manifest.permission_group.NOTIFICATIONS
+import android.app.Activity
import android.app.ActivityManager
import android.app.AppOpsManager
import android.app.AppOpsManager.MODE_ALLOWED
@@ -59,8 +60,10 @@ import android.health.connect.HealthConnectManager
import android.os.Build
import android.os.Bundle
import android.os.UserHandle
+import android.os.UserManager
import android.permission.PermissionManager
import android.provider.DeviceConfig
+import android.provider.MediaStore
import android.provider.Settings
import android.safetylabel.SafetyLabelConstants.PERMISSION_RATIONALE_ENABLED
import android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED
@@ -86,7 +89,7 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightPerm
import com.android.permissioncontroller.permission.model.livedatatypes.PermState
import com.android.permissioncontroller.permission.service.LocationAccessCheck
import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
import java.time.Duration
import java.util.concurrent.atomic.AtomicReference
import kotlin.coroutines.Continuation
@@ -106,13 +109,14 @@ object KotlinUtils {
private const val LOG_TAG = "PermissionController Utils"
- private const val PERMISSION_CONTROLLER_CHANGED_FLAG_MASK = FLAG_PERMISSION_USER_SET or
- FLAG_PERMISSION_USER_FIXED or
- FLAG_PERMISSION_ONE_TIME or
- FLAG_PERMISSION_REVOKED_COMPAT or
- FLAG_PERMISSION_ONE_TIME or
- FLAG_PERMISSION_REVIEW_REQUIRED or
- FLAG_PERMISSION_AUTO_REVOKED
+ private const val PERMISSION_CONTROLLER_CHANGED_FLAG_MASK =
+ FLAG_PERMISSION_USER_SET or
+ FLAG_PERMISSION_USER_FIXED or
+ FLAG_PERMISSION_ONE_TIME or
+ FLAG_PERMISSION_REVOKED_COMPAT or
+ FLAG_PERMISSION_ONE_TIME or
+ FLAG_PERMISSION_REVIEW_REQUIRED or
+ FLAG_PERMISSION_AUTO_REVOKED
private const val KILL_REASON_APP_OP_CHANGE = "Permission related app op changed"
private const val SAFETY_PROTECTION_RESOURCES_ENABLED = "safety_protection_enabled"
@@ -131,30 +135,27 @@ object KotlinUtils {
private val ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE =
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
- /** Whether to show the Permissions Hub. */
+ /** Whether to show the Permissions Hub. */
private const val PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled"
- /** Whether to show the mic and camera icons. */
+ /** Whether to show the mic and camera icons. */
private const val PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"
/** Whether to show the location indicators. */
private const val PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"
- /** Whether location accuracy feature is enabled */
- private const val PROPERTY_LOCATION_ACCURACY_ENABLED = "location_accuracy_enabled"
-
- /** Whether to show 7-day toggle in privacy hub. */
+ /** Whether to show 7-day toggle in privacy hub. */
private const val PRIVACY_DASHBOARD_7_DAY_TOGGLE = "privacy_dashboard_7_day_toggle"
/** Default location precision */
private const val PROPERTY_LOCATION_PRECISION = "location_precision"
- /** Whether to show the photo picker option in permission prompts. */
+ /** Whether to show the photo picker option in permission prompts. */
private const val PROPERTY_PHOTO_PICKER_PROMPT_ENABLED = "photo_picker_prompt_enabled"
/**
- * The minimum amount of time to wait, after scheduling the safety label changes job, before
- * the job actually runs for the first time.
+ * The minimum amount of time to wait, after scheduling the safety label changes job, before the
+ * job actually runs for the first time.
*/
private const val PROPERTY_SAFETY_LABEL_CHANGES_JOB_DELAY_MILLIS =
"safety_label_changes_job_delay_millis"
@@ -178,8 +179,12 @@ object KotlinUtils {
*/
@ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
fun isPermissionsHub2FlagEnabled(): Boolean {
- return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_PERMISSIONS_HUB_2_ENABLED, false)
+ return SdkLevel.isAtLeastS() &&
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_PERMISSIONS_HUB_2_ENABLED,
+ false
+ )
}
/**
* Whether to show the Permissions Dashboard
@@ -198,13 +203,17 @@ object KotlinUtils {
*/
@ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
fun isCameraMicIconsFlagEnabled(): Boolean {
- return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_CAMERA_MIC_ICONS_ENABLED, true)
+ return SdkLevel.isAtLeastS() &&
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_CAMERA_MIC_ICONS_ENABLED,
+ true
+ )
}
/**
- * Whether to show Camera and Mic Icons. They should be shown if the permission hub, or the icons
- * specifically, are enabled.
+ * Whether to show Camera and Mic Icons. They should be shown if the permission hub, or the
+ * icons specifically, are enabled.
*
* @return whether to show the icons.
*/
@@ -220,36 +229,37 @@ object KotlinUtils {
*/
@ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
fun isLocationIndicatorsFlagEnabled(): Boolean {
- return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_LOCATION_INDICATORS_ENABLED, false)
+ return SdkLevel.isAtLeastS() &&
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_LOCATION_INDICATORS_ENABLED,
+ false
+ )
}
/**
- * Whether to show the location indicators. The location indicators are enable if the
- * permission hub, or location indicator specifically are enabled.
+ * Whether to show the location indicators. The location indicators are enable if the permission
+ * hub, or location indicator specifically are enabled.
*/
@ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
fun shouldShowLocationIndicators(): Boolean {
return isLocationIndicatorsFlagEnabled() || isPermissionsHub2FlagEnabled()
}
- /**
- * Whether the location accuracy feature is enabled
- */
+ /** Whether the location accuracy feature is enabled */
@ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
fun isLocationAccuracyEnabled(): Boolean {
- return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_LOCATION_ACCURACY_ENABLED, true)
+ return SdkLevel.isAtLeastS()
}
- /**
- * Default state of location precision
- * true: default is FINE.
- * false: default is COARSE.
- */
+ /** Default state of location precision true: default is FINE. false: default is COARSE. */
fun getDefaultPrecision(): Boolean {
- return !SdkLevel.isAtLeastS() || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_LOCATION_PRECISION, true)
+ return !SdkLevel.isAtLeastS() ||
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_LOCATION_PRECISION,
+ true
+ )
}
/**
@@ -259,8 +269,12 @@ object KotlinUtils {
*/
@ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
fun is7DayToggleEnabled(): Boolean {
- return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PRIVACY_DASHBOARD_7_DAY_TOGGLE, false)
+ return SdkLevel.isAtLeastS() &&
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PRIVACY_DASHBOARD_7_DAY_TOGGLE,
+ false
+ )
}
/**
@@ -270,11 +284,22 @@ object KotlinUtils {
*/
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
fun isPhotoPickerPromptEnabled(): Boolean {
+ return isPhotoPickerPromptSupported() &&
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_PHOTO_PICKER_PROMPT_ENABLED,
+ true
+ )
+ }
+
+ /** Whether the Photo Picker Prompt is supported by the device */
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
+ fun isPhotoPickerPromptSupported(): Boolean {
val app = PermissionControllerApplication.get()
- return SdkLevel.isAtLeastU() && !DeviceUtils.isAuto(app) &&
- !DeviceUtils.isTelevision(app) && !DeviceUtils.isWear(app) &&
- DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_PHOTO_PICKER_PROMPT_ENABLED, true)
+ return SdkLevel.isAtLeastU() &&
+ !DeviceUtils.isAuto(app) &&
+ !DeviceUtils.isTelevision(app) &&
+ !DeviceUtils.isWear(app)
}
/*
@@ -284,8 +309,12 @@ object KotlinUtils {
*/
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
fun isPermissionRationaleEnabled(): Boolean {
- return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PERMISSION_RATIONALE_ENABLED, true)
+ return SdkLevel.isAtLeastU() &&
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PERMISSION_RATIONALE_ENABLED,
+ true
+ )
}
/**
@@ -293,8 +322,12 @@ object KotlinUtils {
*/
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
fun isSafetyLabelChangeNotificationsEnabled(context: Context): Boolean {
- return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true) &&
+ return SdkLevel.isAtLeastU() &&
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED,
+ true
+ ) &&
!DeviceUtils.isAuto(context) &&
!DeviceUtils.isTelevision(context) &&
!DeviceUtils.isWear(context)
@@ -306,8 +339,12 @@ object KotlinUtils {
*/
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
fun safetyLabelChangesJobServiceKillSwitch(): Boolean {
- return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_SAFETY_LABEL_CHANGES_JOB_SERVICE_KILL_SWITCH, false)
+ return SdkLevel.isAtLeastU() &&
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_SAFETY_LABEL_CHANGES_JOB_SERVICE_KILL_SWITCH,
+ false
+ )
}
/** How often the safety label changes job will run. */
@@ -316,7 +353,8 @@ object KotlinUtils {
return DeviceConfig.getLong(
DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_SAFETY_LABEL_CHANGES_JOB_INTERVAL_MILLIS,
- Duration.ofDays(30).toMillis())
+ Duration.ofDays(30).toMillis()
+ )
}
/** Whether the safety label changes job should only be run when the device is idle. */
@@ -325,19 +363,19 @@ object KotlinUtils {
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_SAFETY_LABEL_CHANGES_JOB_RUN_WHEN_IDLE,
- true)
+ true
+ )
}
/**
- * Given a Map, and a List, determines which elements are in the list, but not the map, and
- * vice versa. Used primarily for determining which liveDatas are already being watched, and
- * which need to be removed or added
+ * Given a Map, and a List, determines which elements are in the list, but not the map, and vice
+ * versa. Used primarily for determining which liveDatas are already being watched, and which
+ * need to be removed or added
*
* @param oldValues A map of key type K, with any value type
* @param newValues A list of type K
- *
* @return A pair, where the first value is all items in the list, but not the map, and the
- * second is all keys in the map, but not the list
+ * second is all keys in the map, but not the list
*/
fun <K> getMapAndListDifferences(
newValues: Collection<K>,
@@ -359,7 +397,7 @@ object KotlinUtils {
*
* @param compare The function comparing two preferences, which will be used to sort
* @param hasHeader Whether the group contains a LargeHeaderPreference, which will be kept at
- * the top of the list
+ * the top of the list
*/
fun sortPreferenceGroup(
group: PreferenceGroup,
@@ -372,15 +410,17 @@ object KotlinUtils {
}
if (hasHeader) {
- preferences.sortWith(Comparator { lhs, rhs ->
- if (lhs is SettingsWithLargeHeader.LargeHeaderPreference) {
- -1
- } else if (rhs is SettingsWithLargeHeader.LargeHeaderPreference) {
- 1
- } else {
- compare(lhs, rhs)
+ preferences.sortWith(
+ Comparator { lhs, rhs ->
+ if (lhs is SettingsWithLargeHeader.LargeHeaderPreference) {
+ -1
+ } else if (rhs is SettingsWithLargeHeader.LargeHeaderPreference) {
+ 1
+ } else {
+ compare(lhs, rhs)
+ }
}
- })
+ )
} else {
preferences.sortWith(Comparator(compare))
}
@@ -395,17 +435,15 @@ object KotlinUtils {
*
* @param context The context from which to get the icon
* @param groupName The name of the permission group whose icon we want
- *
* @return The permission group's icon, the ic_perm_device_info icon if the group has no icon,
- * or the group does not exist
+ * or the group does not exist
*/
@JvmOverloads
fun getPermGroupIcon(context: Context, groupName: String, tint: Int? = null): Drawable? {
val groupInfo = Utils.getGroupInfo(groupName, context)
var icon: Drawable? = null
if (groupInfo != null && groupInfo.icon != 0) {
- icon = Utils.loadDrawable(context.packageManager, groupInfo.packageName,
- groupInfo.icon)
+ icon = Utils.loadDrawable(context.packageManager, groupInfo.packageName, groupInfo.icon)
}
if (icon == null) {
@@ -425,13 +463,15 @@ object KotlinUtils {
*
* @param context The context from which to get the label
* @param groupName The name of the permission group whose label we want
- *
* @return The permission group's label, or the group name, if the group is invalid
*/
fun getPermGroupLabel(context: Context, groupName: String): CharSequence {
val groupInfo = Utils.getGroupInfo(groupName, context) ?: return groupName
- return groupInfo.loadSafeLabel(context.packageManager, 0f,
- TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM)
+ return groupInfo.loadSafeLabel(
+ context.packageManager,
+ 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ )
}
/**
@@ -439,9 +479,8 @@ object KotlinUtils {
*
* @param context The context from which to get the description
* @param groupName The name of the permission group whose description we want
- *
* @return The permission group's description, or an empty string, if the group is invalid, or
- * its description does not exist
+ * its description does not exist
*/
fun getPermGroupDescription(context: Context, groupName: String): CharSequence {
val groupInfo = Utils.getGroupInfo(groupName, context)
@@ -457,15 +496,20 @@ object KotlinUtils {
/**
* Gets a permission's label from the system.
+ *
* @param context The context from which to get the label
* @param permName The name of the permission whose label we want
- *
* @return The permission's label, or the permission name, if the permission is invalid
*/
fun getPermInfoLabel(context: Context, permName: String): CharSequence {
return try {
- context.packageManager.getPermissionInfo(permName, 0).loadSafeLabel(
- context.packageManager, 20000.toFloat(), TextUtils.SAFE_STRING_FLAG_TRIM)
+ context.packageManager
+ .getPermissionInfo(permName, 0)
+ .loadSafeLabel(
+ context.packageManager,
+ 20000.toFloat(),
+ TextUtils.SAFE_STRING_FLAG_TRIM
+ )
} catch (e: PackageManager.NameNotFoundException) {
permName
}
@@ -473,19 +517,23 @@ object KotlinUtils {
/**
* Gets a permission's icon from the system.
+ *
* @param context The context from which to get the icon
* @param permName The name of the permission whose icon we want
- *
- * @return The permission's icon, or the permission's group icon if the icon isn't set, or
- * the ic_perm_device_info icon if the permission is invalid.
+ * @return The permission's icon, or the permission's group icon if the icon isn't set, or the
+ * ic_perm_device_info icon if the permission is invalid.
*/
fun getPermInfoIcon(context: Context, permName: String): Drawable? {
return try {
val permInfo = context.packageManager.getPermissionInfo(permName, 0)
var icon: Drawable? = null
if (permInfo.icon != 0) {
- icon = Utils.applyTint(context, permInfo.loadUnbadgedIcon(context.packageManager),
- android.R.attr.colorControlNormal)
+ icon =
+ Utils.applyTint(
+ context,
+ permInfo.loadUnbadgedIcon(context.packageManager),
+ android.R.attr.colorControlNormal
+ )
}
if (icon == null) {
@@ -495,8 +543,11 @@ object KotlinUtils {
icon
} catch (e: PackageManager.NameNotFoundException) {
- Utils.applyTint(context, context.getDrawable(R.drawable.ic_perm_device_info),
- android.R.attr.colorControlNormal)
+ Utils.applyTint(
+ context,
+ context.getDrawable(R.drawable.ic_perm_device_info),
+ android.R.attr.colorControlNormal
+ )
}
}
@@ -505,9 +556,8 @@ object KotlinUtils {
*
* @param context The context from which to get the description
* @param permName The name of the permission whose description we want
- *
- * @return The permission's description, or an empty string, if the group is invalid, or
- * its description does not exist
+ * @return The permission's description, or an empty string, if the group is invalid, or its
+ * description does not exist
*/
fun getPermInfoDescription(context: Context, permName: String): CharSequence {
return try {
@@ -524,14 +574,9 @@ object KotlinUtils {
* @param app The current application
* @param packageName The name of the package whose icon we want
* @param user The user for whom we want the package icon
- *
* @return The package's icon, or null, if the package does not exist
*/
- fun getBadgedPackageIcon(
- app: Application,
- packageName: String,
- user: UserHandle
- ): Drawable? {
+ fun getBadgedPackageIcon(app: Application, packageName: String, user: UserHandle): Drawable? {
return try {
val userContext = Utils.getUserContext(app, user)
val appInfo = userContext.packageManager.getApplicationInfo(packageName, 0)
@@ -547,7 +592,6 @@ object KotlinUtils {
* @param app The current application
* @param packageName The name of the package whose label we want
* @param user The user for whom we want the package label
- *
* @return The package's label
*/
fun getPackageLabel(app: Application, packageName: String, user: UserHandle): String {
@@ -561,8 +605,12 @@ object KotlinUtils {
}
fun convertToBitmap(pkgIcon: Drawable): Bitmap {
- val pkgIconBmp = Bitmap.createBitmap(pkgIcon.intrinsicWidth, pkgIcon.intrinsicHeight,
- Bitmap.Config.ARGB_8888)
+ val pkgIconBmp =
+ Bitmap.createBitmap(
+ pkgIcon.intrinsicWidth,
+ pkgIcon.intrinsicHeight,
+ Bitmap.Config.ARGB_8888
+ )
// Draw the icon so it can be displayed.
val canvas = Canvas(pkgIconBmp)
pkgIcon.setBounds(0, 0, pkgIcon.intrinsicWidth, pkgIcon.intrinsicHeight)
@@ -577,13 +625,13 @@ object KotlinUtils {
* @param app The current application
* @param packageName The name of the package whose uid we want
* @param user The user we want the package uid for
- *
* @return The package's UID, or null if the package or user is invalid
*/
fun getPackageUid(app: Application, packageName: String, user: UserHandle): Int? {
val liveData = LightPackageInfoLiveData[packageName, user]
val liveDataUid = liveData.value?.uid
- return if (liveDataUid != null && liveData.hasActiveObservers()) liveDataUid else {
+ return if (liveDataUid != null && liveData.hasActiveObservers()) liveDataUid
+ else {
val userContext = Utils.getUserContext(app, user)
try {
val appInfo = userContext.packageManager.getApplicationInfo(packageName, 0)
@@ -594,9 +642,31 @@ object KotlinUtils {
}
}
- /**
- * Return a specific MIME type, if a set of permissions is associated with one
- */
+ fun openPhotoPickerForApp(
+ activity: Activity,
+ uid: Int,
+ requestedPermissions: List<String>,
+ 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
+ val appUser = UserHandle.getUserHandleForUid(uid)
+ val userManager =
+ activity.createContextAsUser(appUser, 0).getSystemService(UserManager::class.java)!!
+ val user =
+ if (userManager.isCloneProfile) {
+ userManager.getProfileParent(appUser) ?: appUser
+ } else {
+ appUser
+ }
+ val pickerIntent =
+ Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP)
+ .putExtra(Intent.EXTRA_UID, uid)
+ .setType(getMimeTypeForPermissions(requestedPermissions))
+ activity.startActivityForResultAsUser(pickerIntent, requestCode, user)
+ }
+
+ /** Return a specific MIME type, if a set of permissions is associated with one */
fun getMimeTypeForPermissions(permissions: List<String>): String? {
if (permissions.contains(READ_MEDIA_IMAGES) && !permissions.contains(READ_MEDIA_VIDEO)) {
return "image/*"
@@ -614,30 +684,33 @@ object KotlinUtils {
* @param app The currenct application
* @param packageName The package name to check
* @param user The user whose package we want to check
- *
* @return true if the package is R+ (and not a work profile) or has auto revoke enabled
*/
fun isROrAutoRevokeEnabled(app: Application, packageName: String, user: UserHandle): Boolean {
val userContext = Utils.getUserContext(app, user)
val liveDataValue = LightPackageInfoLiveData[packageName, user].value
- val (targetSdk, uid) = if (liveDataValue != null) {
- liveDataValue.targetSdkVersion to liveDataValue.uid
- } else {
- val appInfo = userContext.packageManager.getApplicationInfo(packageName, 0)
- appInfo.targetSdkVersion to appInfo.uid
- }
+ val (targetSdk, uid) =
+ if (liveDataValue != null) {
+ liveDataValue.targetSdkVersion to liveDataValue.uid
+ } else {
+ val appInfo = userContext.packageManager.getApplicationInfo(packageName, 0)
+ appInfo.targetSdkVersion to appInfo.uid
+ }
if (targetSdk <= Build.VERSION_CODES.Q) {
val opsManager = app.getSystemService(AppOpsManager::class.java)!!
- return opsManager.unsafeCheckOpNoThrow(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid,
- packageName) == MODE_ALLOWED
+ return opsManager.unsafeCheckOpNoThrow(
+ OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ uid,
+ packageName
+ ) == MODE_ALLOWED
}
return true
}
/**
- * Determine if the given permission should be treated as split from a
- * non-runtime permission for an application targeting the given SDK level.
+ * Determine if the given permission should be treated as split from a non-runtime permission
+ * for an application targeting the given SDK level.
*/
private fun isPermissionSplitFromNonRuntime(
app: Application,
@@ -664,8 +737,7 @@ object KotlinUtils {
* @param group: The LightAppPermGroup whose permission flags we wish to set
* @param flags: Pairs of <FlagInt, ShouldSetFlag>
* @param filterPermissions: A list of permissions to filter by. Only the filtered permissions
- * will be set
- *
+ * will be set
* @return A new LightAppPermGroup with the flags set.
*/
fun setGroupFlags(
@@ -690,14 +762,29 @@ object KotlinUtils {
}
// Check if flags need to be updated
if (flagMask and (perm.flags xor flagsToSet) != 0) {
- app.packageManager.updatePermissionFlags(permName, group.packageName,
- group.userHandle, *flags)
+ app.packageManager.updatePermissionFlags(
+ permName,
+ group.packageName,
+ group.userHandle,
+ *flags
+ )
}
- newPerms[permName] = LightPermission(group.packageInfo, perm.permInfo,
- perm.isGranted, perm.flags or flagsToSet, perm.foregroundPerms)
+ newPerms[permName] =
+ LightPermission(
+ group.packageInfo,
+ perm.permInfo,
+ perm.isGranted,
+ perm.flags or flagsToSet,
+ perm.foregroundPerms
+ )
}
- return LightAppPermGroup(group.packageInfo, group.permGroupInfo, newPerms,
- group.hasInstallToRuntimeSplit, group.specialLocationGrant)
+ return LightAppPermGroup(
+ group.packageInfo,
+ group.permGroupInfo,
+ newPerms,
+ group.hasInstallToRuntimeSplit,
+ group.specialLocationGrant
+ )
}
/**
@@ -708,9 +795,7 @@ object KotlinUtils {
* @param app The current application
* @param group The group whose permissions should be granted
* @param filterPermissions If not specified, all permissions of the group will be granted.
- * Otherwise only permissions in {@code filterPermissions} will be
- * granted.
- *
+ * Otherwise only permissions in {@code filterPermissions} will be granted.
* @return a new LightAppPermGroup, reflecting the new state
*/
@JvmOverloads
@@ -722,8 +807,15 @@ object KotlinUtils {
userFixed: Boolean = false,
withoutAppOps: Boolean = false,
): LightAppPermGroup {
- return grantRuntimePermissions(app, group, false, isOneTime, userFixed,
- withoutAppOps, filterPermissions)
+ return grantRuntimePermissions(
+ app,
+ group,
+ false,
+ isOneTime,
+ userFixed,
+ withoutAppOps,
+ filterPermissions
+ )
}
/**
@@ -734,9 +826,7 @@ object KotlinUtils {
* @param app The current application
* @param group The group whose permissions should be granted
* @param filterPermissions If not specified, all permissions of the group will be granted.
- * Otherwise only permissions in {@code filterPermissions} will be
- * granted.
- *
+ * Otherwise only permissions in {@code filterPermissions} will be granted.
* @return a new LightAppPermGroup, reflecting the new state
*/
@JvmOverloads
@@ -745,8 +835,7 @@ object KotlinUtils {
group: LightAppPermGroup,
filterPermissions: List<String> = group.permissions.keys.toList()
): LightAppPermGroup {
- return grantRuntimePermissions(app, group, true, false, false, false,
- filterPermissions)
+ return grantRuntimePermissions(app, group, true, false, false, false, filterPermissions)
}
private fun grantRuntimePermissions(
@@ -764,8 +853,8 @@ object KotlinUtils {
val perm = group.permissions[permName] ?: continue
val isBackgroundPerm = permName in group.backgroundPermNames
if (isBackgroundPerm == grantBackground) {
- val (newPerm, shouldKill) = grantRuntimePermission(app, perm, group, isOneTime,
- userFixed, withoutAppOps)
+ val (newPerm, shouldKill) =
+ grantRuntimePermission(app, perm, group, isOneTime, userFixed, withoutAppOps)
newPerms[newPerm.name] = newPerm
shouldKillForAnyPermission = shouldKillForAnyPermission || shouldKill
}
@@ -776,32 +865,48 @@ object KotlinUtils {
var permFlags = groupPerm.flags
permFlags = permFlags.clearFlag(FLAG_PERMISSION_AUTO_REVOKED)
if (groupPerm.flags != permFlags) {
- app.packageManager.updatePermissionFlags(groupPerm.name,
- group.packageInfo.packageName, PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
- permFlags, user)
+ app.packageManager.updatePermissionFlags(
+ groupPerm.name,
+ group.packageInfo.packageName,
+ PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
+ permFlags,
+ user
+ )
}
}
}
if (shouldKillForAnyPermission) {
(app.getSystemService(ActivityManager::class.java) as ActivityManager).killUid(
- group.packageInfo.uid, KILL_REASON_APP_OP_CHANGE)
+ group.packageInfo.uid,
+ KILL_REASON_APP_OP_CHANGE
+ )
}
- val newGroup = LightAppPermGroup(group.packageInfo, group.permGroupInfo, newPerms,
- group.hasInstallToRuntimeSplit, group.specialLocationGrant)
+ val newGroup =
+ LightAppPermGroup(
+ group.packageInfo,
+ group.permGroupInfo,
+ newPerms,
+ group.hasInstallToRuntimeSplit,
+ 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 }) {
if (SdkLevel.isAtLeastT()) {
app.getSystemService(PermissionManager::class.java)!!.startOneTimePermissionSession(
- group.packageName, Utils.getOneTimePermissionsTimeout(),
- Utils.getOneTimePermissionsKilledDelay(false),
- ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
- ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE)
+ group.packageName,
+ Utils.getOneTimePermissionsTimeout(),
+ Utils.getOneTimePermissionsKilledDelay(false),
+ ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
+ ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE
+ )
} else {
app.getSystemService(PermissionManager::class.java)!!.startOneTimePermissionSession(
- group.packageName, Utils.getOneTimePermissionsTimeout(),
- ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
- ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE)
+ group.packageName,
+ Utils.getOneTimePermissionsTimeout(),
+ ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
+ ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE
+ )
}
}
return newGroup
@@ -813,14 +918,13 @@ object KotlinUtils {
* @param app The current application
* @param perm The permission which should be granted.
* @param group An app permission group in which to look for background or foreground
- * @param isOneTime Whether this is a one-time permission grant
- * permissions
+ * @param isOneTime Whether this is a one-time permission grant permissions
* @param userFixed Whether to mark the permissions as user fixed when granted
* @param withoutAppOps If these permission have app ops associated, and this value is true,
- * then do not grant the app op when the permission is granted, and add the REVOKED_COMPAT flag.
- *
+ * then do not grant the app op when the permission is granted, and add the REVOKED_COMPAT
+ * flag.
* @return a LightPermission and boolean pair <permission with updated state (or the original
- * state, if it wasn't changed), should kill app>
+ * state, if it wasn't changed), should kill app>
*/
private fun grantRuntimePermission(
app: Application,
@@ -833,8 +937,9 @@ object KotlinUtils {
val pkgInfo = group.packageInfo
val user = UserHandle.getUserHandleForUid(pkgInfo.uid)
val supportsRuntime = pkgInfo.targetSdkVersion >= Build.VERSION_CODES.M
- val isGrantingAllowed = (!pkgInfo.isInstantApp || perm.isInstantPerm) &&
- (supportsRuntime || !perm.isRuntimeOnly)
+ val isGrantingAllowed =
+ (!pkgInfo.isInstantApp || perm.isInstantPerm) &&
+ (supportsRuntime || !perm.isRuntimeOnly)
// Do not touch permissions fixed by the system, or permissions that cannot be granted
if (!isGrantingAllowed || perm.isSystemFixed) {
return perm to false
@@ -855,8 +960,13 @@ object KotlinUtils {
// flag, so that the PermissionPolicyService doesn't reset the app op state
if (affectsAppOp && withoutAppOps) {
oldFlags = oldFlags.setFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
- app.packageManager.updatePermissionFlags(perm.name, group.packageName,
- PERMISSION_CONTROLLER_CHANGED_FLAG_MASK, oldFlags, user)
+ app.packageManager.updatePermissionFlags(
+ perm.name,
+ group.packageName,
+ PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
+ oldFlags,
+ user
+ )
disallowAppOp(app, perm, group)
}
app.packageManager.grantRuntimePermission(group.packageName, perm.name, user)
@@ -869,11 +979,12 @@ object KotlinUtils {
shouldKill = true
isGranted = true
}
- newFlags = if (affectsAppOp && withoutAppOps) {
- newFlags.setFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
- } else {
- newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
- }
+ newFlags =
+ if (affectsAppOp && withoutAppOps) {
+ newFlags.setFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
+ } else {
+ newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
+ }
newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
// If this permission affects an app op, ensure the permission app op is enabled
@@ -898,11 +1009,12 @@ object KotlinUtils {
}
newFlags = newFlags.clearFlag(FLAG_PERMISSION_AUTO_REVOKED)
- newFlags = if (isOneTime) {
- newFlags.setFlag(FLAG_PERMISSION_ONE_TIME)
- } else {
- newFlags.clearFlag(FLAG_PERMISSION_ONE_TIME)
- }
+ newFlags =
+ if (isOneTime) {
+ newFlags.setFlag(FLAG_PERMISSION_ONE_TIME)
+ } else {
+ newFlags.clearFlag(FLAG_PERMISSION_ONE_TIME)
+ }
// If we newly grant background access to the fine location, double-guess the user some
// time later if this was really the right choice.
@@ -922,13 +1034,18 @@ object KotlinUtils {
}
if (oldFlags != newFlags) {
- app.packageManager.updatePermissionFlags(perm.name, group.packageInfo.packageName,
- PERMISSION_CONTROLLER_CHANGED_FLAG_MASK, newFlags, user)
+ app.packageManager.updatePermissionFlags(
+ perm.name,
+ group.packageInfo.packageName,
+ PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
+ newFlags,
+ user
+ )
}
val newState = PermState(newFlags, isGranted)
- return LightPermission(perm.pkgInfo, perm.permInfo, newState,
- perm.foregroundPerms) to shouldKill
+ return LightPermission(perm.pkgInfo, perm.permInfo, newState, perm.foregroundPerms) to
+ shouldKill
}
/**
@@ -941,9 +1058,7 @@ object KotlinUtils {
* @param userFixed If the user requested that they do not want to be asked again
* @param oneTime If the permission should be mark as one-time
* @param filterPermissions If not specified, all permissions of the group will be revoked.
- * Otherwise only permissions in {@code filterPermissions} will be
- * revoked.
- *
+ * Otherwise only permissions in {@code filterPermissions} will be revoked.
* @return a LightAppPermGroup representing the new state
*/
@JvmOverloads
@@ -955,8 +1070,15 @@ object KotlinUtils {
forceRemoveRevokedCompat: Boolean = false,
filterPermissions: List<String> = group.permissions.keys.toList()
): LightAppPermGroup {
- return revokeRuntimePermissions(app, group, false, userFixed, oneTime,
- forceRemoveRevokedCompat, filterPermissions)
+ return revokeRuntimePermissions(
+ app,
+ group,
+ false,
+ userFixed,
+ oneTime,
+ forceRemoveRevokedCompat,
+ filterPermissions
+ )
}
/**
@@ -968,9 +1090,7 @@ object KotlinUtils {
* @param group The group whose permissions should be revoked
* @param userFixed If the user requested that they do not want to be asked again
* @param filterPermissions If not specified, all permissions of the group will be revoked.
- * Otherwise only permissions in {@code filterPermissions} will be
- * revoked.
- *
+ * Otherwise only permissions in {@code filterPermissions} will be revoked.
* @return a LightAppPermGroup representing the new state
*/
@JvmOverloads
@@ -982,8 +1102,15 @@ object KotlinUtils {
forceRemoveRevokedCompat: Boolean = false,
filterPermissions: List<String> = group.permissions.keys.toList()
): LightAppPermGroup {
- return revokeRuntimePermissions(app, group, true, userFixed, oneTime,
- forceRemoveRevokedCompat, filterPermissions)
+ return revokeRuntimePermissions(
+ app,
+ group,
+ true,
+ userFixed,
+ oneTime,
+ forceRemoveRevokedCompat,
+ filterPermissions
+ )
}
private fun revokeRuntimePermissions(
@@ -1003,8 +1130,14 @@ object KotlinUtils {
val isBackgroundPerm = permName in group.backgroundPermNames
if (isBackgroundPerm == revokeBackground) {
val (newPerm, shouldKill) =
- revokeRuntimePermission(app, perm, userFixed, oneTime, forceRemoveRevokedCompat,
- group)
+ revokeRuntimePermission(
+ app,
+ perm,
+ userFixed,
+ oneTime,
+ forceRemoveRevokedCompat,
+ group
+ )
newPerms[newPerm.name] = newPerm
shouldKillForAnyPermission = shouldKillForAnyPermission || shouldKill
}
@@ -1012,15 +1145,24 @@ object KotlinUtils {
if (shouldKillForAnyPermission && !shouldSkipKillForGroup(app, group)) {
(app.getSystemService(ActivityManager::class.java) as ActivityManager).killUid(
- group.packageInfo.uid, KILL_REASON_APP_OP_CHANGE)
+ group.packageInfo.uid,
+ KILL_REASON_APP_OP_CHANGE
+ )
}
- val newGroup = LightAppPermGroup(group.packageInfo, group.permGroupInfo, newPerms,
- group.hasInstallToRuntimeSplit, group.specialLocationGrant)
+ val newGroup =
+ LightAppPermGroup(
+ group.packageInfo,
+ group.permGroupInfo,
+ newPerms,
+ group.hasInstallToRuntimeSplit,
+ group.specialLocationGrant
+ )
if (wasOneTime && !anyPermsOfPackageOneTimeGranted(app, newGroup.packageInfo, newGroup)) {
app.getSystemService(PermissionManager::class.java)!!.stopOneTimePermissionSession(
- group.packageName)
+ group.packageName
+ )
}
return newGroup
}
@@ -1042,8 +1184,9 @@ object KotlinUtils {
postRevokeHandler: Runnable?
) {
GlobalScope.launch(Dispatchers.Main) {
- val group = LightAppPermGroupLiveData[packageName, permissionGroupName, user]
- .getInitializedValue()
+ val group =
+ LightAppPermGroupLiveData[packageName, permissionGroupName, user]
+ .getInitializedValue()
if (group != null) {
revokeBackgroundRuntimePermissions(context.application, group)
}
@@ -1059,7 +1202,6 @@ object KotlinUtils {
* @param app The current application
* @param packageInfo The packageInfo we wish to examine
* @param group Optional, the current app permission group we are examining
- *
* @return true if any permission in the package is granted for one time, false otherwise
*/
private fun anyPermsOfPackageOneTimeGranted(
@@ -1075,11 +1217,12 @@ object KotlinUtils {
if (permName in group?.permissions ?: emptyMap()) {
continue
}
- val flags = app.packageManager.getPermissionFlags(permName, packageInfo.packageName,
- user) and FLAG_PERMISSION_ONE_TIME
- val granted = packageInfo.requestedPermissionsFlags[idx] ==
- PackageManager.PERMISSION_GRANTED &&
- (flags and FLAG_PERMISSION_REVOKED_COMPAT) == 0
+ val flags =
+ app.packageManager.getPermissionFlags(permName, packageInfo.packageName, user) and
+ FLAG_PERMISSION_ONE_TIME
+ val granted =
+ packageInfo.requestedPermissionsFlags[idx] == PackageManager.PERMISSION_GRANTED &&
+ (flags and FLAG_PERMISSION_REVOKED_COMPAT) == 0
if (granted && (flags and FLAG_PERMISSION_ONE_TIME) != 0) {
return true
}
@@ -1093,10 +1236,9 @@ object KotlinUtils {
* @param perm The permission which should be revoked.
* @param userFixed If the user requested that they do not want to be asked again
* @param group An optional app permission group in which to look for background or foreground
- * permissions
- *
+ * permissions
* @return a LightPermission and boolean pair <permission with updated state (or the original
- * state, if it wasn't changed), should kill app>
+ * state, if it wasn't changed), should kill app>
*/
private fun revokeRuntimePermission(
app: Application,
@@ -1120,11 +1262,20 @@ object KotlinUtils {
val affectsAppOp = permissionToOp(perm.name) != null || perm.isBackgroundPermission
if (perm.isGranted || (perm.isCompatRevoked && forceRemoveRevokedCompat)) {
- if (supportsRuntime && !isPermissionSplitFromNonRuntime(app, perm.name,
- group.packageInfo.targetSdkVersion)) {
+ if (
+ supportsRuntime &&
+ !isPermissionSplitFromNonRuntime(
+ app,
+ perm.name,
+ group.packageInfo.targetSdkVersion
+ )
+ ) {
// Revoke the permission if needed.
- app.packageManager.revokeRuntimePermission(group.packageInfo.packageName,
- perm.name, user)
+ app.packageManager.revokeRuntimePermission(
+ group.packageInfo.packageName,
+ perm.name,
+ user
+ )
isGranted = false
if (forceRemoveRevokedCompat) {
newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
@@ -1149,18 +1300,26 @@ object KotlinUtils {
// Update the permission flags.
// Take a note that the user fixed the permission, if applicable.
- newFlags = if (userFixed) newFlags.setFlag(PackageManager.FLAG_PERMISSION_USER_FIXED)
- else newFlags.clearFlag(PackageManager.FLAG_PERMISSION_USER_FIXED)
- newFlags = if (oneTime) newFlags.clearFlag(PackageManager.FLAG_PERMISSION_USER_SET)
- else newFlags.setFlag(PackageManager.FLAG_PERMISSION_USER_SET)
- newFlags = if (oneTime) newFlags.setFlag(PackageManager.FLAG_PERMISSION_ONE_TIME)
- else newFlags.clearFlag(PackageManager.FLAG_PERMISSION_ONE_TIME)
+ newFlags =
+ if (userFixed) newFlags.setFlag(PackageManager.FLAG_PERMISSION_USER_FIXED)
+ else newFlags.clearFlag(PackageManager.FLAG_PERMISSION_USER_FIXED)
+ newFlags =
+ if (oneTime) newFlags.clearFlag(PackageManager.FLAG_PERMISSION_USER_SET)
+ else newFlags.setFlag(PackageManager.FLAG_PERMISSION_USER_SET)
+ newFlags =
+ if (oneTime) newFlags.setFlag(PackageManager.FLAG_PERMISSION_ONE_TIME)
+ else newFlags.clearFlag(PackageManager.FLAG_PERMISSION_ONE_TIME)
newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_AUTO_REVOKED)
newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED)
if (perm.flags != newFlags) {
- app.packageManager.updatePermissionFlags(perm.name, group.packageInfo.packageName,
- PERMISSION_CONTROLLER_CHANGED_FLAG_MASK, newFlags, user)
+ app.packageManager.updatePermissionFlags(
+ perm.name,
+ group.packageInfo.packageName,
+ PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
+ newFlags,
+ user
+ )
}
// If we revoke background access to the fine location, we trigger a check to remove
@@ -1176,17 +1335,18 @@ object KotlinUtils {
}
if (cancelLocationAccessWarning) {
// cancel location access warning notification
- LocationAccessCheck(app, null).cancelBackgroundAccessWarningNotification(
- group.packageInfo.packageName,
- user,
- true
- )
+ LocationAccessCheck(app, null)
+ .cancelBackgroundAccessWarningNotification(
+ group.packageInfo.packageName,
+ user,
+ true
+ )
}
}
val newState = PermState(newFlags, isGranted)
- return LightPermission(perm.pkgInfo, perm.permInfo, newState,
- perm.foregroundPerms) to shouldKill
+ return LightPermission(perm.pkgInfo, perm.permInfo, newState, perm.foregroundPerms) to
+ shouldKill
}
private fun Int.setFlag(flagToSet: Int): Int {
@@ -1200,27 +1360,20 @@ object KotlinUtils {
/**
* Allow the app op for a permission/uid.
*
- * <p>There are three cases:
- * <dl>
- * <dt>The permission is not split into foreground/background</dt>
- * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}</dd>
- * <dt>The permission is a foreground permission:</dt>
- * <dd><dl><dt>The background permission permission is granted</dt>
- * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}</dd>
- * <dt>The background permission permission is <u>not</u> granted</dt>
- * <dd>The app op matching the permission will be set to
- * {@link AppOpsManager#MODE_FOREGROUND}</dd>
- * </dl></dd>
- * <dt>The permission is a background permission:</dt>
- * <dd>All granted foreground permissions for this background permission will be set to
- * {@link AppOpsManager#MODE_ALLOWED}</dd>
- * </dl>
+ * <p>There are three cases: <dl> <dt>The permission is not split into
+ * foreground/background</dt> <dd>The app op matching the permission will be set to {@link
+ * AppOpsManager#MODE_ALLOWED}</dd> <dt>The permission is a foreground permission:</dt>
+ * <dd><dl><dt>The background permission permission is granted</dt> <dd>The app op matching the
+ * permission will be set to {@link AppOpsManager#MODE_ALLOWED}</dd> <dt>The background
+ * permission permission is <u>not</u> granted</dt> <dd>The app op matching the permission will
+ * be set to {@link AppOpsManager#MODE_FOREGROUND}</dd> </dl></dd> <dt>The permission is a
+ * background permission:</dt> <dd>All granted foreground permissions for this background
+ * permission will be set to {@link AppOpsManager#MODE_ALLOWED}</dd> </dl>
*
* @param app The current application
* @param perm The LightPermission whose app op should be allowed
- * @param group The LightAppPermGroup which will be looked in for foreground or
- * background LightPermission objects
- *
+ * @param group The LightAppPermGroup which will be looked in for foreground or background
+ * LightPermission objects
* @return {@code true} iff app-op was changed
*/
private fun allowAppOp(
@@ -1239,25 +1392,29 @@ object KotlinUtils {
val appOpName = permissionToOp(foregroundPermName) ?: continue
if (fgPerm != null && fgPerm.isGranted) {
- wasChanged = setOpMode(appOpName, uid, packageName, MODE_ALLOWED,
- appOpsManager) || wasChanged
+ wasChanged =
+ setOpMode(appOpName, uid, packageName, MODE_ALLOWED, appOpsManager) ||
+ wasChanged
}
}
} else {
val appOpName = permissionToOp(perm.name) ?: return false
if (perm.backgroundPermission != null) {
- wasChanged = if (group.permissions.containsKey(perm.backgroundPermission)) {
- val bgPerm = group.permissions[perm.backgroundPermission]
- val mode = if (bgPerm != null && bgPerm.isGranted) MODE_ALLOWED
- else MODE_FOREGROUND
-
- setOpMode(appOpName, uid, packageName, mode, appOpsManager)
- } else {
- // The app requested a permission that has a background permission but it did
- // not request the background permission, hence it can never get background
- // access
- setOpMode(appOpName, uid, packageName, MODE_FOREGROUND, appOpsManager)
- }
+ wasChanged =
+ if (group.permissions.containsKey(perm.backgroundPermission)) {
+ val bgPerm = group.permissions[perm.backgroundPermission]
+ val mode =
+ if (bgPerm != null && bgPerm.isGranted) MODE_ALLOWED
+ else MODE_FOREGROUND
+
+ setOpMode(appOpName, uid, packageName, mode, appOpsManager)
+ } else {
+ // The app requested a permission that has a background permission but it
+ // did
+ // not request the background permission, hence it can never get background
+ // access
+ setOpMode(appOpName, uid, packageName, MODE_FOREGROUND, appOpsManager)
+ }
} else {
wasChanged = setOpMode(appOpName, uid, packageName, MODE_ALLOWED, appOpsManager)
}
@@ -1268,22 +1425,17 @@ object KotlinUtils {
/**
* Disallow the app op for a permission/uid.
*
- * <p>There are three cases:
- * <dl>
- * <dt>The permission is not split into foreground/background</dt>
- * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}</dd>
- * <dt>The permission is a foreground permission:</dt>
- * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}</dd>
- * <dt>The permission is a background permission:</dt>
- * <dd>All granted foreground permissions for this background permission will be set to
- * {@link AppOpsManager#MODE_FOREGROUND}</dd>
- * </dl>
+ * <p>There are three cases: <dl> <dt>The permission is not split into
+ * foreground/background</dt> <dd>The app op matching the permission will be set to {@link
+ * AppOpsManager#MODE_IGNORED}</dd> <dt>The permission is a foreground permission:</dt> <dd>The
+ * app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}</dd> <dt>The
+ * permission is a background permission:</dt> <dd>All granted foreground permissions for this
+ * background permission will be set to {@link AppOpsManager#MODE_FOREGROUND}</dd> </dl>
*
* @param app The current application
* @param perm The LightPermission whose app op should be allowed
- * @param group The LightAppPermGroup which will be looked in for foreground or
- * background LightPermission objects
- *
+ * @param group The LightAppPermGroup which will be looked in for foreground or background
+ * LightPermission objects
* @return {@code true} iff app-op was changed
*/
private fun disallowAppOp(
@@ -1301,8 +1453,9 @@ object KotlinUtils {
val fgPerm = group.permissions[foregroundPermName]
if (fgPerm != null && fgPerm.isGranted) {
val appOpName = permissionToOp(foregroundPermName) ?: return false
- wasChanged = wasChanged || setOpMode(appOpName, uid, packageName,
- MODE_FOREGROUND, appOpsManager)
+ wasChanged =
+ wasChanged ||
+ setOpMode(appOpName, uid, packageName, MODE_FOREGROUND, appOpsManager)
}
}
} else {
@@ -1320,7 +1473,6 @@ object KotlinUtils {
* @param packageName The package the app-op belongs to
* @param mode The new mode
* @param manager The app ops manager to use to change the app op
- *
* @return {@code true} iff app-op was changed
*/
private fun setOpMode(
@@ -1343,22 +1495,25 @@ object KotlinUtils {
return false
}
- return shouldSkipKillOnPermDeny(app, POST_NOTIFICATIONS, group.packageName,
- group.userHandle)
+ return shouldSkipKillOnPermDeny(
+ app,
+ POST_NOTIFICATIONS,
+ group.packageName,
+ group.userHandle
+ )
}
/**
* Determine if the usual "kill app on permission denial" should be skipped. It should be
- * skipped if the permission is POST_NOTIFICATIONS, the app holds the BACKUP permission, and
- * a backup restore is currently in progress.
+ * skipped if the permission is POST_NOTIFICATIONS, the app holds the BACKUP permission, and a
+ * backup restore is currently in progress.
*
* @param app the current application
* @param permission the permission being denied
* @param packageName the package the permission was denied for
* @param user the user whose package the permission was denied for
- *
* @return true if the permission denied was POST_NOTIFICATIONS, the app is a backup app, and a
- * backup restore is in progress, false otherwise
+ * backup restore is in progress, false otherwise
*/
fun shouldSkipKillOnPermDeny(
app: Application,
@@ -1367,17 +1522,27 @@ object KotlinUtils {
user: UserHandle
): Boolean {
val userContext: Context = Utils.getUserContext(app, user)
- if (permission != POST_NOTIFICATIONS || userContext.packageManager
- .checkPermission(BACKUP, packageName) != PackageManager.PERMISSION_GRANTED) {
+ if (
+ permission != POST_NOTIFICATIONS ||
+ userContext.packageManager.checkPermission(BACKUP, packageName) !=
+ PackageManager.PERMISSION_GRANTED
+ ) {
return false
}
return try {
- val isInSetup = Settings.Secure.getInt(userContext.contentResolver,
- Settings.Secure.USER_SETUP_COMPLETE, user.identifier) == 0
- val isInDeferredSetup = Settings.Secure.getInt(userContext.contentResolver,
- Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, user.identifier) ==
- Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
+ val isInSetup =
+ Settings.Secure.getInt(
+ userContext.contentResolver,
+ Settings.Secure.USER_SETUP_COMPLETE,
+ user.identifier
+ ) == 0
+ val isInDeferredSetup =
+ Settings.Secure.getInt(
+ userContext.contentResolver,
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STATE,
+ user.identifier
+ ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
isInSetup || isInDeferredSetup
} catch (e: Settings.SettingNotFoundException) {
Log.w(LOG_TAG, "Failed to check if the user is in restore: $e")
@@ -1391,22 +1556,27 @@ object KotlinUtils {
*
* @param context: The context from which to retrieve the package
* @param packageName: The package name to check
- *
* @return whether or not the given package has a launch intent
*/
fun packageHasLaunchIntent(context: Context, packageName: String): Boolean {
val intentToResolve = Intent(ACTION_MAIN)
intentToResolve.addCategory(CATEGORY_INFO)
intentToResolve.setPackage(packageName)
- var resolveInfos = context.packageManager.queryIntentActivities(intentToResolve,
- MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE)
+ var resolveInfos =
+ context.packageManager.queryIntentActivities(
+ intentToResolve,
+ MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE
+ )
if (resolveInfos.size <= 0) {
intentToResolve.removeCategory(CATEGORY_INFO)
intentToResolve.addCategory(CATEGORY_LAUNCHER)
intentToResolve.setPackage(packageName)
- resolveInfos = context.packageManager.queryIntentActivities(intentToResolve,
- MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE)
+ resolveInfos =
+ context.packageManager.queryIntentActivities(
+ intentToResolve,
+ MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE
+ )
}
return resolveInfos.size > 0
}
@@ -1424,39 +1594,55 @@ object KotlinUtils {
isFineSelected: Boolean
) {
if (isFineSelected) {
- setGroupFlags(app, group,
+ setGroupFlags(
+ app,
+ group,
PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY to true,
- filterPermissions = listOf(ACCESS_FINE_LOCATION))
- setGroupFlags(app, group,
+ filterPermissions = listOf(ACCESS_FINE_LOCATION)
+ )
+ setGroupFlags(
+ app,
+ group,
PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY to false,
- filterPermissions = listOf(Manifest.permission.ACCESS_COARSE_LOCATION))
+ filterPermissions = listOf(Manifest.permission.ACCESS_COARSE_LOCATION)
+ )
} else {
- setGroupFlags(app, group,
+ setGroupFlags(
+ app,
+ group,
PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY to false,
- filterPermissions = listOf(ACCESS_FINE_LOCATION))
- setGroupFlags(app, group,
+ 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)
+ )
}
}
/**
- * Determines whether we should show the safety protection resources.
- * We show the resources only if
- * (1) the build version is T or after and
- * (2) the feature flag safety_protection_enabled is enabled and
- * (3) the config value config_safetyProtectionEnabled is enabled/true and
- * (4) the resources exist (currently the resources only exist on GMS devices)
+ * Determines whether we should show the safety protection resources. We show the resources only
+ * if (1) the build version is T or after and (2) the feature flag safety_protection_enabled is
+ * enabled and (3) the config value config_safetyProtectionEnabled is enabled/true and (4) the
+ * resources exist (currently the resources only exist on GMS devices)
*/
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
fun shouldShowSafetyProtectionResources(context: Context): Boolean {
return try {
SdkLevel.isAtLeastT() &&
DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_PRIVACY, SAFETY_PROTECTION_RESOURCES_ENABLED, false) &&
- context.getResources().getBoolean(
- Resources.getSystem()
- .getIdentifier("config_safetyProtectionEnabled", "bool", "android")) &&
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SAFETY_PROTECTION_RESOURCES_ENABLED,
+ false
+ ) &&
+ context
+ .getResources()
+ .getBoolean(
+ Resources.getSystem()
+ .getIdentifier("config_safetyProtectionEnabled", "bool", "android")
+ ) &&
context.getDrawable(android.R.drawable.ic_safety_protection) != null &&
!context.getString(android.R.string.safety_protection_display_text).isNullOrEmpty()
} catch (e: Resources.NotFoundException) {
@@ -1480,8 +1666,7 @@ object KotlinUtils {
installerPackageName: String?,
packageName: String?
): Intent? {
- val intent: Intent = Intent(Intent.ACTION_SHOW_APP_INFO)
- .setPackage(installerPackageName)
+ val intent: Intent = Intent(Intent.ACTION_SHOW_APP_INFO).setPackage(installerPackageName)
val result: Intent? = resolveActivityForIntent(context, intent)
if (result != null) {
result.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
@@ -1512,9 +1697,10 @@ object KotlinUtils {
val color: Int
// If U resources are available, and this is a U+ device, use those
if (SdkLevel.isAtLeastU()) {
- val scContext = SafetyCenterResourcesContext(context)
- val uIcon = scContext.getIconByDrawableName("ic_notification_badge_general")
- val uColor = scContext.getColorByName("notification_tint_normal")
+ val safetyCenterResourcesApk = SafetyCenterResourcesApk(context)
+ val uIcon =
+ safetyCenterResourcesApk.getIconByDrawableName("ic_notification_badge_general")
+ val uColor = safetyCenterResourcesApk.getColorByName("notification_tint_normal")
if (uIcon != null && uColor != null) {
appLabel = context.getString(R.string.safety_privacy_qs_tile_title)
return NotificationResources(appLabel, uIcon, uColor)
@@ -1523,47 +1709,45 @@ object KotlinUtils {
// Use PbA branding if available, otherwise default to more generic branding
if (shouldShowSafetyProtectionResources(context)) {
- appLabel = Html.fromHtml(context.getString(
- android.R.string.safety_protection_display_text), 0).toString()
- smallIcon =
- Icon.createWithResource(context, android.R.drawable.ic_safety_protection)
+ appLabel =
+ Html.fromHtml(context.getString(android.R.string.safety_protection_display_text), 0)
+ .toString()
+ smallIcon = Icon.createWithResource(context, android.R.drawable.ic_safety_protection)
color = context.getColor(R.color.safety_center_info)
} else {
appLabel = context.getString(R.string.safety_center_notification_app_label)
- smallIcon =
- Icon.createWithResource(context, R.drawable.ic_settings_notification)
+ smallIcon = Icon.createWithResource(context, R.drawable.ic_settings_notification)
color = context.getColor(android.R.color.system_notification_accent_color)
}
return NotificationResources(appLabel, smallIcon, color)
}
}
-/**
- * Get the [value][LiveData.getValue], suspending until [isInitialized] if not yet so
- */
+/** 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) },
- isInitialized: LD.() -> Boolean = { value != null }
+ isValueInitialized: LD.() -> Boolean = { value != null }
): T {
- return if (isInitialized()) {
- value!!
+ return if (isValueInitialized()) {
+ @Suppress("UNCHECKED_CAST")
+ value as T
} else {
suspendCoroutine { continuation: Continuation<T> ->
val observer = AtomicReference<Observer<T>>()
- observer.set(Observer { newValue ->
- if (isInitialized()) {
- GlobalScope.launch(Dispatchers.Main) {
- observer.getAndSet(null)?.let { observerSnapshot ->
- removeObserver(observerSnapshot)
- continuation.resume(newValue)
+ observer.set(
+ Observer { newValue ->
+ if (isValueInitialized()) {
+ GlobalScope.launch(Dispatchers.Main) {
+ observer.getAndSet(null)?.let { observerSnapshot ->
+ removeObserver(observerSnapshot)
+ continuation.resume(newValue)
+ }
}
}
}
- })
+ )
- GlobalScope.launch(Dispatchers.Main) {
- observe(observer.get())
- }
+ GlobalScope.launch(Dispatchers.Main) { observe(observer.get()) }
}
}
}
@@ -1571,8 +1755,8 @@ suspend fun <T, LD : LiveData<T>> LD.getInitializedValue(
/**
* A parallel equivalent of [map]
*
- * Starts the given suspending function for each item in the collection without waiting for
- * previous ones to complete, then suspends until all the started operations finish.
+ * Starts the given suspending function for each item in the collection without waiting for previous
+ * ones to complete, then suspends until all the started operations finish.
*/
suspend inline fun <T, R> Iterable<T>.mapInParallel(
context: CoroutineContext,
@@ -1594,8 +1778,8 @@ suspend inline fun <T> Iterable<T>.forEachInParallel(
}
/**
- * Check that we haven't already started transitioning to a given destination. If we haven't,
- * start navigating to that destination.
+ * Check that we haven't already started transitioning to a given destination. If we haven't, start
+ * navigating to that destination.
*
* @param destResId The ID of the desired destination
* @param args The optional bundle of args to be passed to the destination
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
index b06a09b28..840a033c3 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
@@ -23,10 +23,9 @@ import android.content.pm.PackageManager
import android.content.pm.PermissionInfo
import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP
import android.util.Log
-
import com.android.modules.utils.build.SdkLevel
-import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
import com.android.permission.safetylabel.DataCategoryConstants
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
/**
* This file contains the canonical mapping of permission to permission group, used in the
@@ -36,15 +35,16 @@ object PermissionMapping {
private val LOG_TAG = "PermissionMapping"
- private val PERMISSION_GROUPS_TO_DATA_CATEGORIES: Map<String, List<String>> = mapOf(
- Manifest.permission_group.LOCATION to listOf(DataCategoryConstants.CATEGORY_LOCATION))
+ private val PERMISSION_GROUPS_TO_DATA_CATEGORIES: Map<String, List<String>> =
+ mapOf(Manifest.permission_group.LOCATION to listOf(DataCategoryConstants.CATEGORY_LOCATION))
@JvmField
val SENSOR_DATA_PERMISSIONS: List<String> =
listOf(
Manifest.permission_group.LOCATION,
Manifest.permission_group.CAMERA,
- Manifest.permission_group.MICROPHONE)
+ Manifest.permission_group.MICROPHONE
+ )
@JvmField
val STORAGE_SUPERGROUP_PERMISSIONS: List<String> =
@@ -53,7 +53,8 @@ object PermissionMapping {
listOf(
Manifest.permission_group.STORAGE,
Manifest.permission_group.READ_MEDIA_AURAL,
- Manifest.permission_group.READ_MEDIA_VISUAL)
+ Manifest.permission_group.READ_MEDIA_VISUAL
+ )
val PARTIAL_MEDIA_PERMISSIONS: MutableSet<String> = mutableSetOf()
@@ -68,7 +69,6 @@ object PermissionMapping {
private val HEALTH_PERMISSIONS_SET: MutableSet<String> = mutableSetOf()
-
init {
PLATFORM_PERMISSIONS[Manifest.permission.READ_CONTACTS] = Manifest.permission_group.CONTACTS
PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CONTACTS] =
@@ -202,7 +202,6 @@ object PermissionMapping {
* platform permission.
*
* @param permission the permission to resolve
- *
* @return The group the permission belongs to
*/
@JvmStatic
@@ -214,7 +213,6 @@ object PermissionMapping {
* Get name of the permission group a permission belongs to.
*
* @param permission the [info][PermissionInfo] of the permission to resolve
- *
* @return The group the permission belongs to
*/
@JvmStatic
@@ -230,9 +228,8 @@ object PermissionMapping {
* Get the names for all platform permissions belonging to a group.
*
* @param group the group
- *
* @return The permission names or an empty list if the group does not have platform runtime
- * permissions
+ * permissions
*/
@JvmStatic
fun getPlatformPermissionNamesOfGroup(group: String): List<String> {
@@ -245,19 +242,19 @@ object PermissionMapping {
*
* @param pm Package manager to use to resolve permission infos
* @param group the group
- *
* @return The infos for platform permissions belonging to the group or an empty list if the
- * group does not have platform runtime permissions
+ * group does not have platform runtime permissions
*/
@JvmStatic
fun getPlatformPermissionsOfGroup(pm: PackageManager, group: String): List<PermissionInfo> {
val permInfos = mutableListOf<PermissionInfo>()
for (permName in PLATFORM_PERMISSION_GROUPS[group] ?: emptyList()) {
- val permInfo: PermissionInfo = try {
+ val permInfo: PermissionInfo =
+ try {
pm.getPermissionInfo(permName, 0)
- } catch (e: PackageManager.NameNotFoundException) {
- throw IllegalStateException("$permName not defined by platform", e)
- }
+ } catch (e: PackageManager.NameNotFoundException) {
+ throw IllegalStateException("$permName not defined by platform", e)
+ }
permInfos.add(permInfo)
}
return permInfos
@@ -300,6 +297,7 @@ object PermissionMapping {
/**
* Whether the permission group supports one-time
+ *
* @param permissionGroup The permission group to check
* @return `true` iff the group supports one-time
*/
@@ -308,9 +306,7 @@ object PermissionMapping {
return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup)
}
- /**
- * Adds health permissions as platform permissions.
- */
+ /** Adds health permissions as platform permissions. */
@JvmStatic
fun addHealthPermissionsToPlatform(permissions: Set<String>) {
if (permissions.isEmpty()) {
@@ -334,8 +330,13 @@ object PermissionMapping {
* grant. Otherwise, ACCESS_MEDIA_LOCATION is considered a full grant (for compatibility).
*/
fun getPartialStorageGrantPermissionsForGroup(group: LightAppPermGroup): Set<String> {
- val appSupportsPickerPrompt = group
- .permissions[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit == false
+ if (!KotlinUtils.isPhotoPickerPromptSupported()) {
+ return emptySet()
+ }
+
+ val appSupportsPickerPrompt =
+ group.permissions[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit ==
+ false
return if (appSupportsPickerPrompt) {
PARTIAL_MEDIA_PERMISSIONS
@@ -344,9 +345,7 @@ object PermissionMapping {
}
}
- /**
- * Returns true if the given permission is a health platform permission.
- */
+ /** Returns true if the given permission is a health platform permission. */
@JvmStatic
fun isHealthPermission(permissionName: String): Boolean {
return HEALTH_PERMISSIONS_SET.contains(permissionName)
@@ -382,8 +381,8 @@ object PermissionMapping {
/**
* Get the SafetyLabel categories pertaining to a specified permission group.
*
- * @return The categories, or an empty list if the group does not have a supported mapping
- * to safety label category
+ * @return The categories, or an empty list if the group does not have a supported mapping to
+ * safety label category
*/
fun getDataCategoriesForPermissionGroup(permissionGroupName: String): List<String> {
return if (isSafetyLabelAwarePermissionGroup(permissionGroupName)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/SystemTimeSource.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/SystemTimeSource.kt
index dc05ededc..116c498be 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/SystemTimeSource.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/SystemTimeSource.kt
@@ -18,9 +18,7 @@ package com.android.permissioncontroller.permission.utils
import android.os.SystemClock
-/**
- * Time source that uses the system time.
- */
+/** Time source that uses the system time. */
class SystemTimeSource : TimeSource {
override fun currentTimeMillis(): Long {
@@ -30,4 +28,4 @@ class SystemTimeSource : TimeSource {
override fun elapsedRealtime(): Long {
return SystemClock.elapsedRealtime()
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/TimeSource.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/TimeSource.kt
index 00b93f405..29883e1f3 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/TimeSource.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/TimeSource.kt
@@ -16,18 +16,12 @@
package com.android.permissioncontroller.permission.utils
-/**
- * Interface for system time components.
- */
+/** Interface for system time components. */
interface TimeSource {
- /**
- * Returns the current time in milliseconds.
- */
+ /** Returns the current time in milliseconds. */
fun currentTimeMillis(): Long
- /**
- * Returns milliseconds since boot, including time spent in sleep.
- */
+ /** Returns milliseconds since boot, including time spent in sleep. */
fun elapsedRealtime(): Long
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/UserSensitiveFlagsUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/UserSensitiveFlagsUtils.kt
index 3cd9891ee..50cacf876 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/UserSensitiveFlagsUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/UserSensitiveFlagsUtils.kt
@@ -25,9 +25,9 @@ import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.data.UserSensitivityLiveData
import com.android.permissioncontroller.permission.model.livedatatypes.UidSensitivityState
import com.android.permissioncontroller.permission.utils.Utils.FLAGS_ALWAYS_USER_SENSITIVE
+import java.lang.IllegalStateException
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
-import java.lang.IllegalStateException
private const val LOG_TAG = "UserSensitiveFlagsUtils"
@@ -35,10 +35,9 @@ private const val LOG_TAG = "UserSensitiveFlagsUtils"
* Update the [PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED] and
* [PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED] for all apps of this user.
*
- * @see UserSensitivityLiveData.loadDataAndPostValue
- *
* @param user The user for whom packages will be updated
* @param callback A callback which will be executed when finished
+ * @see UserSensitivityLiveData.loadDataAndPostValue
*/
fun updateUserSensitiveForUser(user: UserHandle, callback: Runnable) {
GlobalScope.launch(IPC) {
@@ -47,7 +46,8 @@ fun updateUserSensitiveForUser(user: UserHandle, callback: Runnable) {
if (uidUserSensitivity == null) {
callback.run()
throw IllegalStateException(
- "All uids sensitivity liveData should not be null if initialized")
+ "All uids sensitivity liveData should not be null if initialized"
+ )
}
updateUserSensitiveForUidsInternal(uidUserSensitivity, user, callback)
}
@@ -62,28 +62,38 @@ private fun updateUserSensitiveForUidsInternal(
val pm = userContext.packageManager
for ((uid, uidState) in uidsUserSensitivity) {
- for (pkg in uidState.packages) {
- for (perm in pkg.requestedPermissions) {
- var flags = uidState.permStates[perm] ?: continue
+ for (pkg in uidState.packages) {
+ for (perm in pkg.requestedPermissions) {
+ var flags = uidState.permStates[perm] ?: continue
- try {
- val oldFlags = pm.getPermissionFlags(perm, pkg.packageName, user) and
+ try {
+ val oldFlags =
+ pm.getPermissionFlags(perm, pkg.packageName, user) and
FLAGS_ALWAYS_USER_SENSITIVE
- if (flags != oldFlags) {
- pm.updatePermissionFlags(perm, pkg.packageName,
- FLAGS_ALWAYS_USER_SENSITIVE, flags, user)
- }
- } catch (e: IllegalArgumentException) {
- if (e.message?.startsWith("Unknown permission: ") == false) {
- Log.e(LOG_TAG, "Unexpected exception while updating flags for " +
- "${pkg.packageName} (uid $uid) permission $perm", e)
- } else {
- // Unknown permission - ignore
- }
+ if (flags != oldFlags) {
+ pm.updatePermissionFlags(
+ perm,
+ pkg.packageName,
+ FLAGS_ALWAYS_USER_SENSITIVE,
+ flags,
+ user
+ )
+ }
+ } catch (e: IllegalArgumentException) {
+ if (e.message?.startsWith("Unknown permission: ") == false) {
+ Log.e(
+ LOG_TAG,
+ "Unexpected exception while updating flags for " +
+ "${pkg.packageName} (uid $uid) permission $perm",
+ e
+ )
+ } else {
+ // Unknown permission - ignore
}
}
}
}
+ }
callback?.run()
}
@@ -98,8 +108,11 @@ fun updateUserSensitiveForUid(uid: Int, callback: Runnable? = null) {
GlobalScope.launch(IPC) {
val uidSensitivityState = UserSensitivityLiveData[uid].getInitializedValue()
if (uidSensitivityState != null) {
- updateUserSensitiveForUidsInternal(uidSensitivityState,
- UserHandle.getUserHandleForUid(uid), callback)
+ updateUserSensitiveForUidsInternal(
+ uidSensitivityState,
+ UserHandle.getUserHandleForUid(uid),
+ callback
+ )
} else {
Log.e(LOG_TAG, "No packages associated with uid $uid, not updating flags")
callback?.run()
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java
index d4354bd72..6b3dc98ad 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java
@@ -42,6 +42,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_W
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.health.connect.HealthConnectManager.ACTION_MANAGE_HEALTH_PERMISSIONS;
+import static android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP;
import static android.os.UserHandle.myUserId;
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
@@ -73,6 +74,7 @@ import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.SensorPrivacyManager;
+import android.health.connect.HealthConnectManager;
import android.os.Binder;
import android.os.Build;
import android.os.Parcelable;
@@ -109,6 +111,8 @@ import com.android.permissioncontroller.permission.model.AppPermissionGroup;
import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup;
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo;
+import kotlin.Triple;
+
import java.lang.annotation.Retention;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
@@ -120,8 +124,6 @@ import java.util.Locale;
import java.util.Random;
import java.util.Set;
-import kotlin.Triple;
-
public final class Utils {
@Retention(SOURCE)
@@ -943,6 +945,70 @@ public final class Utils {
}
/**
+ * Returns true if the group name passed is that of the Platform health group.
+ * @param permGroupName name of the group that needs to be checked.
+ */
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static Boolean isHealthPermissionGroup(String permGroupName) {
+ return SdkLevel.isAtLeastU() && HEALTH_PERMISSION_GROUP.equals(permGroupName);
+ }
+
+ /**
+ * Return whether health permission setting entry should be shown or not
+ *
+ * Should not show Health permissions preference if the package doesn't handle
+ * VIEW_PERMISSION_USAGE_INTENT.
+ *
+ * Will show if above is true AND permission is already granted.
+ *
+ * @param packageInfo the {@link PackageInfo} app which uses the permission
+ * @param permGroupName the health permission group name to show
+ * @return {@code TRUE} iff health permission should be shown
+ */
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static Boolean shouldShowHealthPermission(LightPackageInfo packageInfo,
+ String permGroupName) {
+ if (!isHealthPermissionGroup(permGroupName)) {
+ return false;
+ }
+
+ PermissionControllerApplication app = PermissionControllerApplication.get();
+ PackageManager pm = app.getPackageManager();
+ Context context = getUserContext(app, UserHandle.getUserHandleForUid(packageInfo.getUid()));
+
+ List<PermissionInfo> permissions = new ArrayList<>();
+ try {
+ permissions.addAll(getPermissionInfosForGroup(pm, permGroupName));
+ } catch (NameNotFoundException e) {
+ Log.e(LOG_TAG, "No permissions found for permission group " + permGroupName);
+ return false;
+ }
+
+ // Check in permission is already granted as we should not hide it in the UX at that point.
+ List<String> grantedPermissions = packageInfo.getGrantedPermissions();
+ for (PermissionInfo permission : permissions) {
+ boolean isCurrentlyGranted = grantedPermissions.contains(permission.name);
+ if (isCurrentlyGranted) {
+ Log.d(LOG_TAG, "At least one Health permission group permission is granted, "
+ + "show permission group entry");
+ return true;
+ }
+ }
+
+ Intent viewUsageIntent = new Intent(Intent.ACTION_VIEW_PERMISSION_USAGE);
+ viewUsageIntent.addCategory(HealthConnectManager.CATEGORY_HEALTH_PERMISSIONS);
+ viewUsageIntent.setPackage(packageInfo.getPackageName());
+
+ ResolveInfo resolveInfo = pm.resolveActivity(viewUsageIntent, PackageManager.MATCH_ALL);
+ if (resolveInfo == null) {
+ Log.e(LOG_TAG, "Package that asks for Health permission must also handle "
+ + "VIEW_PERMISSION_USAGE_INTENT.");
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Get a device protected storage based shared preferences. Avoid storing sensitive data in it.
*
* @param context the context to get the shared preferences
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt
index 5dbe203f9..9fd8ab916 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt
@@ -32,8 +32,8 @@ object SafetyLabelUtils {
groupName: String
): Set<Int> {
val purposeSet = mutableSetOf<Int>()
- val categoriesForPermission = PermissionMapping
- .getDataCategoriesForPermissionGroup(groupName)
+ val categoriesForPermission =
+ PermissionMapping.getDataCategoriesForPermissionGroup(groupName)
categoriesForPermission.forEach categoryLoop@{ category ->
val dataCategory: DataCategory? = safetyLabel.dataLabel.dataShared[category]
if (dataCategory == null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt
index 59dbf635d..f9079c41b 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt
@@ -32,24 +32,28 @@ object AccessibilitySettingsUtil {
private val LOG_TAG = AccessibilitySettingsUtil::class.java.simpleName
private val lock = Mutex()
- /**
- * Changes an accessibility component's state.
- */
+ /** Changes an accessibility component's state. */
suspend fun disableAccessibilityService(context: Context, serviceToBeDisabled: ComponentName) {
lock.withLock {
val settingsEnabledA11yServices = getEnabledServicesFromSettings(context)
- if (settingsEnabledA11yServices.isEmpty() ||
- !settingsEnabledA11yServices.contains(serviceToBeDisabled)
+ if (
+ settingsEnabledA11yServices.isEmpty() ||
+ !settingsEnabledA11yServices.contains(serviceToBeDisabled)
) {
- Log.w(LOG_TAG, "${serviceToBeDisabled.toShortString()} is already disabled " +
- "or not installed.")
+ Log.w(
+ LOG_TAG,
+ "${serviceToBeDisabled.toShortString()} is already disabled " +
+ "or not installed."
+ )
return
}
settingsEnabledA11yServices.remove(serviceToBeDisabled)
- val updatedEnabledServices = settingsEnabledA11yServices.map { it.flattenToString() }
- .joinToString(separator = ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR.toString())
+ val updatedEnabledServices =
+ settingsEnabledA11yServices
+ .map { it.flattenToString() }
+ .joinToString(separator = ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR.toString())
Settings.Secure.putString(
context.contentResolver,
@@ -59,27 +63,24 @@ object AccessibilitySettingsUtil {
}
}
- /**
- * @return the mutable set of enabled accessibility services.
- */
+ /** @return the mutable set of enabled accessibility services. */
fun getEnabledServicesFromSettings(context: Context): MutableSet<ComponentName> {
- val enabledServicesSetting = Settings.Secure.getString(
- context.contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
- )
+ val enabledServicesSetting =
+ Settings.Secure.getString(
+ context.contentResolver,
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
+ )
val enabledServices = mutableSetOf<ComponentName>()
if (TextUtils.isEmpty(enabledServicesSetting)) {
return enabledServices
}
- val colonSplitter: TextUtils.StringSplitter = TextUtils.SimpleStringSplitter(
- ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR
- )
+ val colonSplitter: TextUtils.StringSplitter =
+ TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR)
colonSplitter.setString(enabledServicesSetting)
for (componentNameString in colonSplitter) {
- val enabledService = ComponentName.unflattenFromString(
- componentNameString
- )
+ val enabledService = ComponentName.unflattenFromString(componentNameString)
if (enabledService != null) {
enabledServices.add(enabledService)
} else {
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt
index 1d5c9c9fa..5f29e87fa 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt
@@ -35,6 +35,8 @@ import android.os.Bundle
import android.provider.DeviceConfig
import android.provider.Settings
import android.safetycenter.SafetyCenterManager
+import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID
+import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID
import android.safetycenter.SafetyEvent
import android.safetycenter.SafetySourceData
import android.safetycenter.SafetySourceIssue
@@ -78,8 +80,7 @@ import kotlinx.coroutines.sync.withLock
const val PROPERTY_SC_ACCESSIBILITY_SOURCE_ENABLED = "sc_accessibility_source_enabled"
const val PROPERTY_SC_ACCESSIBILITY_LISTENER_ENABLED = "sc_accessibility_listener_enabled"
const val SC_ACCESSIBILITY_SOURCE_ID = "AndroidAccessibility"
-const val SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID =
- "revoke_accessibility_app_access"
+const val SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID = "revoke_accessibility_app_access"
private const val DEBUG = false
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
@@ -95,9 +96,7 @@ fun isAccessibilitySourceEnabled(): Boolean {
)
}
-/**
- * cts test needs to disable the listener.
- */
+/** cts test needs to disable the listener. */
fun isAccessibilityListenerEnabled(): Boolean {
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
@@ -108,24 +107,21 @@ fun isAccessibilityListenerEnabled(): Boolean {
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun isSafetyCenterEnabled(context: Context): Boolean {
- return getSystemServiceSafe(context, SafetyCenterManager::class.java)
- .isSafetyCenterEnabled
+ return getSystemServiceSafe(context, SafetyCenterManager::class.java).isSafetyCenterEnabled
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
-class AccessibilitySourceService(
- val context: Context,
- val random: Random = Random()
-) : PrivacySource {
+class AccessibilitySourceService(val context: Context, val random: Random = Random()) :
+ PrivacySource {
private val parentUserContext = Utils.getParentUserContext(context)
private val packageManager = parentUserContext.packageManager
- private val sharedPrefs: SharedPreferences = parentUserContext.getSharedPreferences(
- ACCESSIBILITY_PREFERENCES_FILE, Context.MODE_PRIVATE)
- private val notificationsManager = getSystemServiceSafe(parentUserContext,
- NotificationManager::class.java)
- private val safetyCenterManager = getSystemServiceSafe(parentUserContext,
- SafetyCenterManager::class.java)
+ private val sharedPrefs: SharedPreferences =
+ parentUserContext.getSharedPreferences(ACCESSIBILITY_PREFERENCES_FILE, Context.MODE_PRIVATE)
+ private val notificationsManager =
+ getSystemServiceSafe(parentUserContext, NotificationManager::class.java)
+ private val safetyCenterManager =
+ getSystemServiceSafe(parentUserContext, SafetyCenterManager::class.java)
@WorkerThread
internal suspend fun processAccessibilityJob(
@@ -155,15 +151,15 @@ class AccessibilitySourceService(
val lastShownNotification =
sharedPrefs.getLong(KEY_LAST_ACCESSIBILITY_NOTIFICATION_SHOWN, 0)
- val showNotification = ((System.currentTimeMillis() - lastShownNotification) >
- getNotificationsIntervalMillis()) && getCurrentNotification() == null
+ val showNotification =
+ ((System.currentTimeMillis() - lastShownNotification) >
+ getNotificationsIntervalMillis()) && getCurrentNotification() == null
if (showNotification) {
val alreadyNotifiedServices = getNotifiedServices()
- val toBeNotifiedServices = a11yServiceList.filter {
- !alreadyNotifiedServices.contains(it.id)
- }
+ val toBeNotifiedServices =
+ a11yServiceList.filter { !alreadyNotifiedServices.contains(it.id) }
if (toBeNotifiedServices.isNotEmpty()) {
if (DEBUG) {
@@ -192,9 +188,7 @@ class AccessibilitySourceService(
}
}
- /**
- * sends a notification for a given accessibility package
- */
+ /** sends a notification for a given accessibility package */
private suspend fun sendNotification(
serviceToBeNotified: AccessibilityServiceInfo,
sessionId: Long
@@ -212,13 +206,13 @@ class AccessibilitySourceService(
identifier = componentName.flattenToString()
}
- val title = parentUserContext.getString(
- R.string.accessibility_access_reminder_notification_title
- )
- val summary = parentUserContext.getString(
- R.string.accessibility_access_reminder_notification_content,
- pkgLabel
- )
+ val title =
+ parentUserContext.getString(R.string.accessibility_access_reminder_notification_title)
+ val summary =
+ parentUserContext.getString(
+ R.string.accessibility_access_reminder_notification_content,
+ pkgLabel
+ )
val (appLabel, smallIcon, color) =
KotlinUtils.getSafetyCenterNotificationResources(parentUserContext)
@@ -234,12 +228,17 @@ class AccessibilitySourceService(
.setAutoCancel(true)
.setDeleteIntent(
PendingIntent.getBroadcast(
- parentUserContext, 0, notificationDeleteIntent,
- PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT or
+ parentUserContext,
+ 0,
+ notificationDeleteIntent,
+ PendingIntent.FLAG_ONE_SHOT or
+ PendingIntent.FLAG_UPDATE_CURRENT or
PendingIntent.FLAG_IMMUTABLE
)
)
- .setContentIntent(getSafetyCenterActivityIntent(context, uid, sessionId))
+ .setContentIntent(
+ getSafetyCenterActivityIntent(context, uid, sessionId, componentName)
+ )
val appNameExtras = Bundle()
appNameExtras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appLabel)
@@ -252,10 +251,10 @@ class AccessibilitySourceService(
)
sharedPrefsLock.withLock {
- sharedPrefs.edit().putLong(
- KEY_LAST_ACCESSIBILITY_NOTIFICATION_SHOWN,
- System.currentTimeMillis()
- ).apply()
+ sharedPrefs
+ .edit()
+ .putLong(KEY_LAST_ACCESSIBILITY_NOTIFICATION_SHOWN, System.currentTimeMillis())
+ .apply()
}
markServiceAsNotified(ComponentName.unflattenFromString(serviceToBeNotified.id)!!)
@@ -273,11 +272,12 @@ class AccessibilitySourceService(
/** Create the channel for a11y notifications */
private fun createPermissionReminderChannel() {
- val permissionReminderChannel = NotificationChannel(
- Constants.PERMISSION_REMINDER_CHANNEL_ID,
- context.getString(R.string.permission_reminders),
- NotificationManager.IMPORTANCE_LOW
- )
+ val permissionReminderChannel =
+ NotificationChannel(
+ Constants.PERMISSION_REMINDER_CHANNEL_ID,
+ context.getString(R.string.permission_reminders),
+ NotificationManager.IMPORTANCE_LOW
+ )
notificationsManager.createNotificationChannel(permissionReminderChannel)
}
@@ -290,36 +290,41 @@ class AccessibilitySourceService(
sessionId: Long
): SafetySourceIssue {
val componentName = ComponentName.unflattenFromString(a11yService.id)!!
- val safetySourceIssueId = "accessibility_${componentName.flattenToString()}"
+ val safetySourceIssueId = getSafetySourceIssueId(componentName)
val pkgLabel = a11yService.resolveInfo.loadLabel(packageManager).toString()
val uid = a11yService.resolveInfo.serviceInfo.applicationInfo.uid
- val removeAccessPendingIntent = getRemoveAccessPendingIntent(
- context,
- componentName,
- safetySourceIssueId,
- uid,
- sessionId
- )
+ val removeAccessPendingIntent =
+ getRemoveAccessPendingIntent(
+ context,
+ componentName,
+ safetySourceIssueId,
+ uid,
+ sessionId
+ )
- val removeAccessAction = SafetySourceIssue.Action.Builder(
- SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID,
- parentUserContext.getString(R.string.accessibility_remove_access_button_label),
- removeAccessPendingIntent
- )
- .setWillResolve(true)
- .setSuccessMessage(parentUserContext.getString(
- R.string.accessibility_remove_access_success_label))
- .build()
+ val removeAccessAction =
+ SafetySourceIssue.Action.Builder(
+ SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID,
+ parentUserContext.getString(R.string.accessibility_remove_access_button_label),
+ removeAccessPendingIntent
+ )
+ .setWillResolve(true)
+ .setSuccessMessage(
+ parentUserContext.getString(R.string.accessibility_remove_access_success_label)
+ )
+ .build()
val accessibilityActivityPendingIntent =
getAccessibilityActivityPendingIntent(context, uid, sessionId)
- val accessibilityActivityAction = SafetySourceIssue.Action.Builder(
- SC_ACCESSIBILITY_SHOW_ACCESSIBILITY_ACTIVITY_ACTION_ID,
- parentUserContext.getString(R.string.accessibility_show_all_apps_button_label),
- accessibilityActivityPendingIntent
- ).build()
+ val accessibilityActivityAction =
+ SafetySourceIssue.Action.Builder(
+ SC_ACCESSIBILITY_SHOW_ACCESSIBILITY_ACTIVITY_ACTION_ID,
+ parentUserContext.getString(R.string.accessibility_show_all_apps_button_label),
+ accessibilityActivityPendingIntent
+ )
+ .build()
val warningCardDismissIntent =
Intent(parentUserContext, AccessibilityWarningCardDismissalReceiver::class.java).apply {
@@ -330,15 +335,19 @@ class AccessibilitySourceService(
putExtra(Intent.EXTRA_UID, uid)
}
- val warningCardDismissPendingIntent = PendingIntent.getBroadcast(
- parentUserContext, 0, warningCardDismissIntent,
- PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT or
- PendingIntent.FLAG_IMMUTABLE
- )
- val title = parentUserContext.getString(
- R.string.accessibility_access_reminder_notification_title)
- val summary = parentUserContext.getString(
- R.string.accessibility_access_warning_card_content)
+ val warningCardDismissPendingIntent =
+ PendingIntent.getBroadcast(
+ parentUserContext,
+ 0,
+ warningCardDismissIntent,
+ PendingIntent.FLAG_ONE_SHOT or
+ PendingIntent.FLAG_UPDATE_CURRENT or
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ val title =
+ parentUserContext.getString(R.string.accessibility_access_reminder_notification_title)
+ val summary =
+ parentUserContext.getString(R.string.accessibility_access_warning_card_content)
return SafetySourceIssue.Builder(
safetySourceIssueId,
@@ -355,9 +364,7 @@ class AccessibilitySourceService(
.build()
}
- /**
- * @return pending intent for remove access button on the warning card.
- */
+ /** @return pending intent for remove access button on the warning card. */
private fun getRemoveAccessPendingIntent(
context: Context,
serviceComponentName: ComponentName,
@@ -368,7 +375,7 @@ class AccessibilitySourceService(
val intent =
Intent(parentUserContext, AccessibilityRemoveAccessHandler::class.java).apply {
putExtra(Intent.EXTRA_COMPONENT_NAME, serviceComponentName)
- putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID, safetySourceIssueId)
+ putExtra(EXTRA_SAFETY_SOURCE_ISSUE_ID, safetySourceIssueId)
putExtra(Constants.EXTRA_SESSION_ID, sessionId)
putExtra(Intent.EXTRA_UID, uid)
flags = Intent.FLAG_RECEIVER_FOREGROUND
@@ -383,9 +390,7 @@ class AccessibilitySourceService(
)
}
- /**
- * @return pending intent for redirecting user to the accessibility page
- */
+ /** @return pending intent for redirecting user to the accessibility page */
private fun getAccessibilityActivityPendingIntent(
context: Context,
uid: Int,
@@ -408,18 +413,19 @@ class AccessibilitySourceService(
)
}
- /**
- * @return pending intent to redirect the user to safety center on notification click
- */
+ /** @return pending intent to redirect the user to safety center on notification click */
private fun getSafetyCenterActivityIntent(
context: Context,
uid: Int,
- sessionId: Long
+ sessionId: Long,
+ componentName: ComponentName
): PendingIntent {
val intent = Intent(Intent.ACTION_SAFETY_CENTER)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.putExtra(Constants.EXTRA_SESSION_ID, sessionId)
intent.putExtra(Intent.EXTRA_UID, uid)
+ intent.putExtra(EXTRA_SAFETY_SOURCE_ID, SC_ACCESSIBILITY_SOURCE_ID)
+ intent.putExtra(EXTRA_SAFETY_SOURCE_ISSUE_ID, getSafetySourceIssueId(componentName))
intent.putExtra(
Constants.EXTRA_PRIVACY_SOURCE,
PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__PRIVACY_SOURCE__A11Y_SERVICE
@@ -432,6 +438,10 @@ class AccessibilitySourceService(
)
}
+ private fun getSafetySourceIssueId(componentName: ComponentName): String {
+ return "accessibility_${componentName.flattenToString()}"
+ }
+
private fun sendIssuesToSafetyCenter(
a11yServiceList: List<AccessibilityServiceInfo>,
sessionId: Long,
@@ -467,9 +477,7 @@ class AccessibilitySourceService(
sendIssuesToSafetyCenter(enabledServices, safetyEvent)
}
- /**
- * If [.cancel] throw an [InterruptedException].
- */
+ /** If [.cancel] throw an [InterruptedException]. */
@Throws(InterruptedException::class)
private fun interruptJobIfCanceled(cancel: BooleanSupplier?) {
if (cancel != null && cancel.asBoolean) {
@@ -477,26 +485,28 @@ class AccessibilitySourceService(
}
}
- private val accessibilityManager = getSystemServiceSafe(parentUserContext,
- AccessibilityManager::class.java)
+ private val accessibilityManager =
+ getSystemServiceSafe(parentUserContext, AccessibilityManager::class.java)
- /**
- * @return enabled 3rd party accessibility services.
- */
+ /** @return enabled 3rd party accessibility services. */
fun getEnabledAccessibilityServices(): List<AccessibilityServiceInfo> {
- val installedServices = accessibilityManager.getInstalledAccessibilityServiceList()
- .associateBy { ComponentName.unflattenFromString(it.id) }
- val enabledServices = AccessibilitySettingsUtil.getEnabledServicesFromSettings(context)
- .map {
+ val installedServices =
+ accessibilityManager.getInstalledAccessibilityServiceList().associateBy {
+ ComponentName.unflattenFromString(it.id)
+ }
+ val enabledServices =
+ AccessibilitySettingsUtil.getEnabledServicesFromSettings(context).map {
if (installedServices[it] == null) {
- Log.e(LOG_TAG, "enabled accessibility service ($it) not found in installed" +
- "services: ${installedServices.keys}")
+ Log.e(
+ LOG_TAG,
+ "enabled accessibility service ($it) not found in installed" +
+ "services: ${installedServices.keys}"
+ )
}
installedServices[it]
}
- return enabledServices.filterNotNull()
- .filter { !it.isAccessibilityTool }
+ return enabledServices.filterNotNull().filter { !it.isAccessibilityTool }
}
/**
@@ -512,13 +522,14 @@ class AccessibilitySourceService(
internal suspend fun removeFromNotifiedServices(a11Service: ComponentName) {
sharedPrefsLock.withLock {
val notifiedServices = getNotifiedServices()
- val filteredServices = notifiedServices.filter {
- it != a11Service.flattenToShortString()
- }.toSet()
+ val filteredServices =
+ notifiedServices.filter { it != a11Service.flattenToShortString() }.toSet()
if (filteredServices.size < notifiedServices.size) {
- sharedPrefs.edit().putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, filteredServices)
- .apply()
+ sharedPrefs
+ .edit()
+ .putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, filteredServices)
+ .apply()
}
}
}
@@ -527,7 +538,9 @@ class AccessibilitySourceService(
sharedPrefsLock.withLock {
val alreadyNotifiedServices = getNotifiedServices()
alreadyNotifiedServices.add(a11Service.flattenToShortString())
- sharedPrefs.edit().putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, alreadyNotifiedServices)
+ sharedPrefs
+ .edit()
+ .putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, alreadyNotifiedServices)
.apply()
}
}
@@ -537,7 +550,9 @@ class AccessibilitySourceService(
val alreadyNotifiedServices = getNotifiedServices()
val services = alreadyNotifiedServices.filter { enabledA11yServices.contains(it) }
if (services.size < alreadyNotifiedServices.size) {
- sharedPrefs.edit().putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, services.toSet())
+ sharedPrefs
+ .edit()
+ .putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, services.toSet())
.apply()
}
}
@@ -552,17 +567,13 @@ class AccessibilitySourceService(
return sharedPrefs
}
- /**
- * Remove notification when safety center feature is turned off
- */
+ /** Remove notification when safety center feature is turned off */
private fun removeAccessibilityNotification() {
val notification: StatusBarNotification = getCurrentNotification() ?: return
cancelNotification(notification.tag)
}
- /**
- * Remove notification (if needed) when an accessibility event occur.
- */
+ /** Remove notification (if needed) when an accessibility event occur. */
fun removeAccessibilityNotification(a11yEnabledComponents: Set<String>) {
val notification = getCurrentNotification() ?: return
if (a11yEnabledComponents.contains(notification.tag)) {
@@ -571,9 +582,7 @@ class AccessibilitySourceService(
cancelNotification(notification.tag)
}
- /**
- * Remove notification when a package is uninstalled.
- */
+ /** Remove notification when a package is uninstalled. */
private fun removeAccessibilityNotification(pkg: String) {
val notification = getCurrentNotification() ?: return
val component = ComponentName.unflattenFromString(notification.tag)
@@ -583,9 +592,7 @@ class AccessibilitySourceService(
cancelNotification(notification.tag)
}
- /**
- * Remove notification for a component, when warning card is dismissed.
- */
+ /** Remove notification for a component, when warning card is dismissed. */
fun removeAccessibilityNotification(component: ComponentName) {
val notification = getCurrentNotification() ?: return
if (component.flattenToShortString() == notification.tag) {
@@ -601,17 +608,19 @@ class AccessibilitySourceService(
internal suspend fun removePackageState(pkg: String) {
sharedPrefsLock.withLock {
removeAccessibilityNotification(pkg)
- val notifiedServices = getNotifiedServices().mapNotNull {
- ComponentName.unflattenFromString(it)
- }
-
- val filteredServices = notifiedServices.filterNot { it.packageName == pkg }
- .map { it.flattenToShortString() }.toSet()
+ val notifiedServices =
+ getNotifiedServices().mapNotNull { ComponentName.unflattenFromString(it) }
+
+ val filteredServices =
+ notifiedServices
+ .filterNot { it.packageName == pkg }
+ .map { it.flattenToShortString() }
+ .toSet()
if (filteredServices.size < notifiedServices.size) {
- sharedPrefs.edit().putStringSet(
- KEY_ALREADY_NOTIFIED_SERVICES,
- filteredServices
- ).apply()
+ sharedPrefs
+ .edit()
+ .putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, filteredServices)
+ .apply()
}
}
}
@@ -629,8 +638,8 @@ class AccessibilitySourceService(
"sc_accessibility_job_interval_millis"
private val DEFAULT_SC_ACCESSIBILITY_JOB_INTERVAL_MILLIS = TimeUnit.DAYS.toMillis(1)
- private val sourceStateChanged = SafetyEvent.Builder(
- SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
+ private val sourceStateChanged =
+ SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
/** lock for processing a job */
internal val lock = Mutex()
@@ -656,7 +665,6 @@ class AccessibilitySourceService(
/**
* Flexibility of the periodic check.
*
- *
* 10% of [.getPeriodicCheckIntervalMillis]
*
* @return The flexibility of the periodic check in milliseconds
@@ -668,7 +676,6 @@ class AccessibilitySourceService(
/**
* Minimum time in between showing two notifications.
*
- *
* This is just small enough so that the periodic check can always show a notification.
*
* @return The minimum time in milliseconds
@@ -705,8 +712,9 @@ class AccessibilityPackageResetHandler : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
- if (action != Intent.ACTION_PACKAGE_DATA_CLEARED &&
- action != Intent.ACTION_PACKAGE_FULLY_REMOVED
+ if (
+ action != Intent.ACTION_PACKAGE_DATA_CLEARED &&
+ action != Intent.ACTION_PACKAGE_FULLY_REMOVED
) {
return
}
@@ -721,9 +729,7 @@ class AccessibilityPackageResetHandler : BroadcastReceiver() {
if (DEBUG) {
Log.v(LOG_TAG, "package reset event occurred for ${data.schemeSpecificPart}")
}
- AccessibilitySourceService(context).run {
- removePackageState(data.schemeSpecificPart)
- }
+ AccessibilitySourceService(context).run { removePackageState(data.schemeSpecificPart) }
}
}
}
@@ -751,9 +757,7 @@ class AccessibilityNotificationDeleteHandler : BroadcastReceiver() {
}
}
-/**
- * Handler for Remove access action (warning cards) in safety center dashboard
- */
+/** Handler for Remove access action (warning cards) in safety center dashboard */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class AccessibilityRemoveAccessHandler : BroadcastReceiver() {
private val LOG_TAG = AccessibilityRemoveAccessHandler::class.java.simpleName
@@ -772,27 +776,27 @@ class AccessibilityRemoveAccessHandler : BroadcastReceiver() {
AccessibilitySourceService.lock.withLock {
val accessibilityService = AccessibilitySourceService(context)
var a11yEnabledServices = accessibilityService.getEnabledAccessibilityServices()
- val builder = try {
- AccessibilitySettingsUtil.disableAccessibilityService(context, a11yService)
- accessibilityService.removeFromNotifiedServices(a11yService)
- a11yEnabledServices = a11yEnabledServices.filter {
- it.id != a11yService.flattenToShortString()
+ val builder =
+ try {
+ AccessibilitySettingsUtil.disableAccessibilityService(context, a11yService)
+ accessibilityService.removeFromNotifiedServices(a11yService)
+ a11yEnabledServices =
+ a11yEnabledServices.filter {
+ it.id != a11yService.flattenToShortString()
+ }
+ SafetyEvent.Builder(
+ SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED
+ )
+ } catch (ex: Exception) {
+ Log.w(LOG_TAG, "error occurred in disabling a11y service.", ex)
+ SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
}
- SafetyEvent.Builder(
- SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED
- )
- } catch (ex: Exception) {
- Log.w(LOG_TAG, "error occurred in disabling a11y service.", ex)
- SafetyEvent.Builder(
- SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED
- )
- }
- val safetySourceIssueId = intent.getStringExtra(
- SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID
- )
- val safetyEvent = builder.setSafetySourceIssueId(safetySourceIssueId)
- .setSafetySourceIssueActionId(SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID)
- .build()
+ val safetySourceIssueId = intent.getStringExtra(EXTRA_SAFETY_SOURCE_ISSUE_ID)
+ val safetyEvent =
+ builder
+ .setSafetySourceIssueId(safetySourceIssueId)
+ .setSafetySourceIssueActionId(SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID)
+ .build()
accessibilityService.sendIssuesToSafetyCenter(a11yEnabledServices, safetyEvent)
}
if (DEBUG) {
@@ -809,9 +813,7 @@ class AccessibilityRemoveAccessHandler : BroadcastReceiver() {
}
}
-/**
- * Handler for accessibility warning cards dismissal in safety center dashboard
- */
+/** Handler for accessibility warning cards dismissal in safety center dashboard */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class AccessibilityWarningCardDismissalReceiver : BroadcastReceiver() {
private val LOG_TAG = AccessibilityWarningCardDismissalReceiver::class.java.simpleName
@@ -846,8 +848,8 @@ class AccessibilityWarningCardDismissalReceiver : BroadcastReceiver() {
}
/**
- * Schedules periodic job to send notifications for third part accessibility services,
- * the job also sends this data to Safety Center.
+ * Schedules periodic job to send notifications for third part accessibility services, the job also
+ * sends this data to Safety Center.
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class AccessibilityOnBootReceiver : BroadcastReceiver() {
@@ -865,15 +867,16 @@ class AccessibilityOnBootReceiver : BroadcastReceiver() {
val jobScheduler = getSystemServiceSafe(context, JobScheduler::class.java)
if (jobScheduler.getPendingJob(Constants.PERIODIC_ACCESSIBILITY_CHECK_JOB_ID) == null) {
- val jobInfo = JobInfo.Builder(
- Constants.PERIODIC_ACCESSIBILITY_CHECK_JOB_ID,
- ComponentName(context, AccessibilityJobService::class.java)
- )
- .setPeriodic(
- AccessibilitySourceService.getJobsIntervalMillis(),
- AccessibilitySourceService.getFlexJobsIntervalMillis()
- )
- .build()
+ val jobInfo =
+ JobInfo.Builder(
+ Constants.PERIODIC_ACCESSIBILITY_CHECK_JOB_ID,
+ ComponentName(context, AccessibilityJobService::class.java)
+ )
+ .setPeriodic(
+ AccessibilitySourceService.getJobsIntervalMillis(),
+ AccessibilitySourceService.getFlexJobsIntervalMillis()
+ )
+ .build()
val status = jobScheduler.schedule(jobInfo)
if (status != JobScheduler.RESULT_SUCCESS) {
@@ -890,8 +893,7 @@ class AccessibilityJobService : JobService() {
private var mSourceService: AccessibilitySourceService? = null
private val mLock = Object()
- @GuardedBy("mLock")
- private var mCurrentJob: Job? = null
+ @GuardedBy("mLock") private var mCurrentJob: Job? = null
override fun onCreate() {
super.onCreate()
@@ -906,24 +908,28 @@ class AccessibilityJobService : JobService() {
Log.v(LOG_TAG, "Accessibility privacy source job already running")
return false
}
- if (!isAccessibilitySourceEnabled() ||
- !isSafetyCenterEnabled(this@AccessibilityJobService)) {
+ if (
+ !isAccessibilitySourceEnabled() ||
+ !isSafetyCenterEnabled(this@AccessibilityJobService)
+ ) {
Log.v(LOG_TAG, "either privacy source or safety center is not enabled")
jobFinished(params, false)
mCurrentJob = null
return false
}
val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
- mCurrentJob = coroutineScope.launch(Dispatchers.Default) {
- mSourceService?.processAccessibilityJob(
- params,
- this@AccessibilityJobService,
- BooleanSupplier {
- val job = mCurrentJob
- return@BooleanSupplier job?.isCancelled ?: false
- }
- ) ?: jobFinished(params, false)
- }
+ mCurrentJob =
+ coroutineScope.launch(Dispatchers.Default) {
+ mSourceService?.processAccessibilityJob(
+ params,
+ this@AccessibilityJobService,
+ BooleanSupplier {
+ val job = mCurrentJob
+ return@BooleanSupplier job?.isCancelled ?: false
+ }
+ )
+ ?: jobFinished(params, false)
+ }
}
return true
}
@@ -931,20 +937,19 @@ class AccessibilityJobService : JobService() {
override fun onStopJob(params: JobParameters?): Boolean {
var job: Job?
synchronized(mLock) {
- job = if (mCurrentJob == null) {
- return false
- } else {
- mCurrentJob
- }
+ job =
+ if (mCurrentJob == null) {
+ return false
+ } else {
+ mCurrentJob
+ }
}
job?.cancel()
return false
}
fun clearJob() {
- synchronized(mLock) {
- mCurrentJob = null
- }
+ synchronized(mLock) { mCurrentJob = null }
}
}
@@ -960,8 +965,9 @@ class SafetyCenterAccessibilityListener(val context: Context) :
return
}
- if (!isAccessibilitySourceEnabled() || !isSafetyCenterEnabled(context) ||
- isProfile(context)) {
+ if (
+ !isAccessibilitySourceEnabled() || !isSafetyCenterEnabled(context) || isProfile(context)
+ ) {
Log.v(LOG_TAG, "accessibility event occurred, safety center feature not enabled.")
return
}
@@ -975,9 +981,13 @@ class SafetyCenterAccessibilityListener(val context: Context) :
val a11ySourceService = AccessibilitySourceService(context)
val a11yEnabledServices = a11ySourceService.getEnabledAccessibilityServices()
a11ySourceService.sendIssuesToSafetyCenter(a11yEnabledServices)
- val enabledComponents = a11yEnabledServices.map { a11yService ->
- ComponentName.unflattenFromString(a11yService.id)!!.flattenToShortString()
- }.toSet()
+ val enabledComponents =
+ a11yEnabledServices
+ .map { a11yService ->
+ ComponentName.unflattenFromString(a11yService.id)!!
+ .flattenToShortString()
+ }
+ .toSet()
a11ySourceService.removeAccessibilityNotification(enabledComponents)
a11ySourceService.updateServiceAsNotified(enabledComponents)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AutoRevokePrivacySource.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AutoRevokePrivacySource.kt
index 0660955ff..93bb376ba 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/AutoRevokePrivacySource.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AutoRevokePrivacySource.kt
@@ -25,9 +25,7 @@ import com.android.permissioncontroller.hibernation.cancelUnusedAppsNotification
import com.android.permissioncontroller.hibernation.rescanAndPushDataToSafetyCenter
import java.util.Random
-/**
- * Privacy source for auto-revoked permissions.
- */
+/** Privacy source for auto-revoked permissions. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class AutoRevokePrivacySource : PrivacySource {
override val shouldProcessProfileRequest: Boolean = false
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/LocationAccessPrivacySource.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/LocationAccessPrivacySource.kt
index df35048e5..97d1f3ebf 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/LocationAccessPrivacySource.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/LocationAccessPrivacySource.kt
@@ -20,7 +20,6 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.annotation.RequiresApi
-
import com.android.permissioncontroller.permission.service.LocationAccessCheck
import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent
@@ -40,4 +39,4 @@ class LocationAccessPrivacySource : PrivacySource {
val safetyRefreshEvent = getSafetyCenterEvent(refreshEvent, intent)
LocationAccessCheck(context, null).rescanAndPushSafetyCenterData(safetyRefreshEvent, null)
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt
index 91a043a6a..4d5adfcc0 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt
@@ -109,7 +109,10 @@ private val DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS = DAYS.toMillis(
private fun isNotificationListenerCheckFlagEnabled(): Boolean {
// TODO: b/249789657 Set default to true after policy exemption + impact analysis
return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED, false)
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED,
+ false
+ )
}
/**
@@ -123,7 +126,8 @@ private fun getPeriodicCheckIntervalMillis(): Long {
return DeviceConfig.getLong(
DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS,
- DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS)
+ DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS
+ )
}
/**
@@ -178,8 +182,8 @@ private fun getSafetySourceIssueIdFromComponentName(componentName: ComponentName
* <p>We rate limit the number of notification we show and only ever show one notification at a
* time.
*
- * <p>As there are many cases why a notification should not been shown, we always schedule a {@link
- * #addNotificationListenerNotificationIfNeeded check} which then might add a notification.
+ * <p>As there are many cases why a notification should not been shown, we always schedule a
+ * {@link #addNotificationListenerNotificationIfNeeded check} which then might add a notification.
*
* @param context Used to resolve managers
* @param shouldCancel If supplied, can be used to interrupt long-running operations
@@ -199,10 +203,11 @@ internal class NotificationListenerCheckInternal(
@VisibleForTesting
val exemptPackagesDelegate = lazy {
getExemptedPackages(
- getSystemServiceSafe(parentUserContext, RoleManager::class.java), parentUserContext)
+ getSystemServiceSafe(parentUserContext, RoleManager::class.java),
+ parentUserContext
+ )
}
- @VisibleForTesting
- val exemptPackages: Set<String> by exemptPackagesDelegate
+ @VisibleForTesting val exemptPackages: Set<String> by exemptPackagesDelegate
companion object {
@VisibleForTesting const val NLS_PREFERENCE_FILE = "nls_preference"
@@ -231,7 +236,8 @@ internal class NotificationListenerCheckInternal(
SYSTEM_AUDIO_INTELLIGENCE,
SYSTEM_NOTIFICATION_INTELLIGENCE,
SYSTEM_TEXT_INTELLIGENCE,
- SYSTEM_VISUAL_INTELLIGENCE)
+ SYSTEM_VISUAL_INTELLIGENCE
+ )
/** Lock required for all public methods */
private val nlsLock = Mutex()
@@ -286,7 +292,8 @@ internal class NotificationListenerCheckInternal(
TAG,
"Found ${enabledComponents.size} enabled notification listeners. " +
"${notifiedComponents.size} already notified. ${unNotifiedComponents.size} " +
- "unnotified, sessionId = $sessionId")
+ "unnotified, sessionId = $sessionId"
+ )
}
throwInterruptedExceptionIfTaskIsCanceled()
@@ -318,7 +325,8 @@ internal class NotificationListenerCheckInternal(
TAG,
"enabledNotificationListeners=$enabledNotificationListeners\n" +
"enabledNotificationListenersExcludingExemptPackages=" +
- "$enabledNotificationListenersExcludingExemptPackages")
+ "$enabledNotificationListenersExcludingExemptPackages"
+ )
}
throwInterruptedExceptionIfTaskIsCanceled()
@@ -421,13 +429,16 @@ internal class NotificationListenerCheckInternal(
val componentsInternal = components.toMutableList()
// Don't show too many notification within certain timespan
- if (currentTimeMillis() - getLastNotificationShownTimeMillis() <
- getInBetweenNotificationsMillis()) {
+ if (
+ currentTimeMillis() - getLastNotificationShownTimeMillis() <
+ getInBetweenNotificationsMillis()
+ ) {
if (DEBUG) {
Log.v(
TAG,
"Notification not posted, within " +
- "$DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS ms")
+ "$DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS ms"
+ )
}
return
}
@@ -458,7 +469,8 @@ internal class NotificationListenerCheckInternal(
if (DEBUG) {
Log.v(
TAG,
- "Attempting to get PackageInfo for " + componentToNotifyFor.packageName)
+ "Attempting to get PackageInfo for " + componentToNotifyFor.packageName
+ )
}
pkgInfo =
Utils.getPackageInfoForComponentName(parentUserContext, componentToNotifyFor)
@@ -483,7 +495,8 @@ internal class NotificationListenerCheckInternal(
NotificationChannel(
Constants.PERMISSION_REMINDER_CHANNEL_ID,
parentUserContext.getString(R.string.permission_reminders),
- NotificationManager.IMPORTANCE_LOW)
+ NotificationManager.IMPORTANCE_LOW
+ )
val notificationManager =
getSystemServiceSafe(parentUserContext, NotificationManager::class.java)
@@ -503,8 +516,7 @@ internal class NotificationListenerCheckInternal(
pkg: PackageInfo,
sessionId: Long
) {
- val pkgLabel =
- Utils.getApplicationLabel(parentUserContext, pkg.applicationInfo)
+ val pkgLabel = Utils.getApplicationLabel(parentUserContext, pkg.applicationInfo)
val uid = pkg.applicationInfo.uid
val deletePendingIntent =
@@ -516,7 +528,9 @@ internal class NotificationListenerCheckInternal(
parentUserContext.getString(R.string.notification_listener_reminder_notification_title)
val text =
parentUserContext.getString(
- R.string.notification_listener_reminder_notification_content, pkgLabel)
+ R.string.notification_listener_reminder_notification_content,
+ pkgLabel
+ )
val (appLabel, smallIcon, color) =
KotlinUtils.getSafetyCenterNotificationResources(parentUserContext)
@@ -543,13 +557,17 @@ internal class NotificationListenerCheckInternal(
val notificationManager =
getSystemServiceSafe(parentUserContext, NotificationManager::class.java)
notificationManager.notify(
- componentName.flattenToString(), NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID, b.build())
+ componentName.flattenToString(),
+ NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID,
+ b.build()
+ )
if (DEBUG) {
Log.v(
TAG,
"Notification listener check notification shown with component=" +
- "${componentName.flattenToString()}, uid=$uid, sessionId=$sessionId")
+ "${componentName.flattenToString()}, uid=$uid, sessionId=$sessionId"
+ )
}
PermissionControllerStatsLog.write(
@@ -557,7 +575,8 @@ internal class NotificationListenerCheckInternal(
PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__PRIVACY_SOURCE__NOTIFICATION_LISTENER,
uid,
PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__ACTION__NOTIFICATION_SHOWN,
- sessionId)
+ sessionId
+ )
updateLastShownNotificationTime()
}
@@ -571,7 +590,8 @@ internal class NotificationListenerCheckInternal(
val intent =
Intent(
parentUserContext,
- NotificationListenerCheckNotificationDeleteHandler::class.java)
+ NotificationListenerCheckNotificationDeleteHandler::class.java
+ )
.apply {
putExtra(EXTRA_COMPONENT_NAME, componentName)
putExtra(Constants.EXTRA_SESSION_ID, sessionId)
@@ -580,7 +600,11 @@ internal class NotificationListenerCheckInternal(
identifier = componentName.flattenToString()
}
return PendingIntent.getBroadcast(
- context, 0, intent, FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
+ context,
+ 0,
+ intent,
+ FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
}
/** @return [PendingIntent] to safety center */
@@ -595,7 +619,8 @@ internal class NotificationListenerCheckInternal(
putExtra(EXTRA_SAFETY_SOURCE_ID, SC_NLS_SOURCE_ID)
putExtra(
EXTRA_SAFETY_SOURCE_ISSUE_ID,
- getSafetySourceIssueIdFromComponentName(componentName))
+ getSafetySourceIssueIdFromComponentName(componentName)
+ )
putExtra(EXTRA_COMPONENT_NAME, componentName)
putExtra(Constants.EXTRA_SESSION_ID, sessionId)
putExtra(Intent.EXTRA_UID, uid)
@@ -603,7 +628,11 @@ internal class NotificationListenerCheckInternal(
identifier = componentName.flattenToString()
}
return PendingIntent.getActivity(
- context, 0, intent, FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
+ context,
+ 0,
+ intent,
+ FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
}
/**
@@ -684,7 +713,7 @@ internal class NotificationListenerCheckInternal(
/**
* @param componentName enabled [NotificationListenerService]
* @return safety source issue, shown as the warning card in safety center. Null if unable to
- * create safety source issue
+ * create safety source issue
*/
@VisibleForTesting
fun createSafetySourceIssue(componentName: ComponentName, sessionId: Long): SafetySourceIssue? {
@@ -703,30 +732,45 @@ internal class NotificationListenerCheckInternal(
val disableNlsPendingIntent =
getDisableNlsPendingIntent(
- parentUserContext, safetySourceIssueId, componentName, uid, sessionId)
+ parentUserContext,
+ safetySourceIssueId,
+ componentName,
+ uid,
+ sessionId
+ )
val disableNlsAction =
SafetySourceIssue.Action.Builder(
SC_NLS_DISABLE_ACTION_ID,
parentUserContext.getString(
- R.string.notification_listener_remove_access_button_label),
- disableNlsPendingIntent)
+ R.string.notification_listener_remove_access_button_label
+ ),
+ disableNlsPendingIntent
+ )
.setWillResolve(true)
.setSuccessMessage(
parentUserContext.getString(
- R.string.notification_listener_remove_access_success_label))
+ R.string.notification_listener_remove_access_success_label
+ )
+ )
.build()
val notificationListenerDetailSettingsPendingIntent =
getNotificationListenerDetailSettingsPendingIntent(
- parentUserContext, componentName, uid, sessionId)
+ parentUserContext,
+ componentName,
+ uid,
+ sessionId
+ )
val showNotificationListenerSettingsAction =
SafetySourceIssue.Action.Builder(
SC_SHOW_NLS_SETTINGS_ACTION_ID,
parentUserContext.getString(
- R.string.notification_listener_review_app_button_label),
- notificationListenerDetailSettingsPendingIntent)
+ R.string.notification_listener_review_app_button_label
+ ),
+ notificationListenerDetailSettingsPendingIntent
+ )
.build()
val actionCardDismissPendingIntent =
@@ -742,7 +786,8 @@ internal class NotificationListenerCheckInternal(
title,
summary,
SafetySourceData.SEVERITY_LEVEL_INFORMATION,
- SC_NLS_ISSUE_TYPE_ID)
+ SC_NLS_ISSUE_TYPE_ID
+ )
.setSubtitle(pkgLabel)
.addAction(disableNlsAction)
.addAction(showNotificationListenerSettingsAction)
@@ -784,7 +829,9 @@ internal class NotificationListenerCheckInternal(
flags = FLAG_ACTIVITY_NEW_TASK
identifier = componentName.flattenToString()
putExtra(
- EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME, componentName.flattenToString())
+ EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME,
+ componentName.flattenToString()
+ )
putExtra(Constants.EXTRA_SESSION_ID, sessionId)
putExtra(Intent.EXTRA_UID, uid)
putExtra(Constants.EXTRA_IS_FROM_SLICE, true)
@@ -843,14 +890,14 @@ class NotificationListenerCheckJobService : JobService() {
val job = addNotificationListenerNotificationIfNeededJob
return@BooleanSupplier job?.isCancelled ?: false
}
- })
+ }
+ )
}
/**
* Starts an asynchronous check if a notification listener notification should be shown.
*
* @param params Not used other than for interacting with job scheduling
- *
* @return `false` if another check is already running, or if SDK Check fails (below T)
*/
override fun onStartJob(params: JobParameters): Boolean {
@@ -869,7 +916,9 @@ class NotificationListenerCheckJobService : JobService() {
GlobalScope.launch(Default) {
notificationListenerCheckInternal
?.getEnabledNotificationListenersAndNotifyIfNeeded(
- params, this@NotificationListenerCheckJobService)
+ params,
+ this@NotificationListenerCheckJobService
+ )
?: jobFinished(params, true)
}
}
@@ -880,7 +929,6 @@ class NotificationListenerCheckJobService : JobService() {
* Abort the check if still running.
*
* @param params ignored
- *
* @return false
*/
override fun onStopJob(params: JobParameters): Boolean {
@@ -922,13 +970,16 @@ class SetupPeriodicNotificationListenerCheck : BroadcastReceiver() {
val job =
JobInfo.Builder(
PERIODIC_NOTIFICATION_LISTENER_CHECK_JOB_ID,
- ComponentName(context, NotificationListenerCheckJobService::class.java))
+ ComponentName(context, NotificationListenerCheckJobService::class.java)
+ )
.setPeriodic(getPeriodicCheckIntervalMillis(), getFlexForPeriodicCheckMillis())
.build()
val scheduleResult = jobScheduler.schedule(job)
if (scheduleResult != JobScheduler.RESULT_SUCCESS) {
Log.e(
- TAG, "Could not schedule periodic notification listener check $scheduleResult")
+ TAG,
+ "Could not schedule periodic notification listener check $scheduleResult"
+ )
} else if (DEBUG) {
Log.i(TAG, "Scheduled periodic notification listener check")
}
@@ -957,14 +1008,16 @@ class NotificationListenerCheckNotificationDeleteHandler : BroadcastReceiver() {
Log.v(
TAG,
"Notification listener check notification declined with component=" +
- "${componentName.flattenToString()} , uid=$uid, sessionId=$sessionId")
+ "${componentName.flattenToString()} , uid=$uid, sessionId=$sessionId"
+ )
}
PermissionControllerStatsLog.write(
PRIVACY_SIGNAL_NOTIFICATION_INTERACTION,
PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__PRIVACY_SOURCE__NOTIFICATION_LISTENER,
uid,
PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__ACTION__DISMISSED,
- sessionId)
+ sessionId
+ )
}
}
@@ -984,7 +1037,8 @@ class DisableNotificationListenerComponentHandler : BroadcastReceiver() {
Log.v(
TAG,
"DisableComponentHandler: disabling $componentName," +
- "uid=$uid, sessionId=$sessionId")
+ "uid=$uid, sessionId=$sessionId"
+ )
}
val safetyEventBuilder =
@@ -993,7 +1047,10 @@ class DisableNotificationListenerComponentHandler : BroadcastReceiver() {
getSystemServiceSafe(context, NotificationManager::class.java)
disallowNlsLock.withLock {
notificationManager.setNotificationListenerAccessGranted(
- componentName, /* granted= */ false, /* userSet= */ true)
+ componentName,
+ /* granted= */ false,
+ /* userSet= */ true
+ )
}
SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED)
@@ -1019,7 +1076,8 @@ class DisableNotificationListenerComponentHandler : BroadcastReceiver() {
PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION__PRIVACY_SOURCE__NOTIFICATION_LISTENER,
uid,
PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION__ACTION__CLICKED_CTA1,
- sessionId)
+ sessionId
+ )
}
}
@@ -1044,7 +1102,8 @@ class NotificationListenerActionCardDismissalReceiver : BroadcastReceiver() {
Log.v(
TAG,
"ActionCardDismissalReceiver: $componentName dismissed," +
- "uid=$uid, sessionId=$sessionId")
+ "uid=$uid, sessionId=$sessionId"
+ )
}
NotificationListenerCheckInternal(context, null).run {
removeNotificationsForComponent(componentName)
@@ -1056,7 +1115,8 @@ class NotificationListenerActionCardDismissalReceiver : BroadcastReceiver() {
PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION__PRIVACY_SOURCE__NOTIFICATION_LISTENER,
uid,
PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION__ACTION__CARD_DISMISSED,
- sessionId)
+ sessionId
+ )
}
}
@@ -1068,8 +1128,10 @@ class NotificationListenerActionCardDismissalReceiver : BroadcastReceiver() {
class NotificationListenerPackageResetHandler : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
- if (action != Intent.ACTION_PACKAGE_DATA_CLEARED &&
- action != Intent.ACTION_PACKAGE_FULLY_REMOVED) {
+ if (
+ action != Intent.ACTION_PACKAGE_DATA_CLEARED &&
+ action != Intent.ACTION_PACKAGE_FULLY_REMOVED
+ ) {
return
}
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerPregrants.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerPregrants.kt
index aaf2d32e6..4063ec8b2 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerPregrants.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerPregrants.kt
@@ -18,32 +18,34 @@ package com.android.permissioncontroller.privacysources
import android.content.Context
import androidx.annotation.VisibleForTesting
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
// (TODO:b/242573074) Remove for Android U.
class NotificationListenerPregrants(private val context: Context) {
@VisibleForTesting
val pregrantedPackagesDelegate = lazy {
hashSetOf(
- "android",
- "com.android.cellbroadcastreceiver",
- "com.android.server.telecom",
- "com.android.settings",
- "com.android.systemui",
- "com.android.launcher3",
- "com.android.dynsystem",
- "com.android.providers.settings",
- "com.android.inputdevices",
- "com.android.keychain",
- "com.android.localtransport",
- "com.android.wallpaperbackup",
- "com.android.location.fused"
- ).also {
- it.addAll(
- SafetyCenterResourcesContext(context)
- .getStringByName("config_NotificationListenerServicePregrants")
- .split(","))
- }
+ "android",
+ "com.android.cellbroadcastreceiver",
+ "com.android.server.telecom",
+ "com.android.settings",
+ "com.android.systemui",
+ "com.android.launcher3",
+ "com.android.dynsystem",
+ "com.android.providers.settings",
+ "com.android.inputdevices",
+ "com.android.keychain",
+ "com.android.localtransport",
+ "com.android.wallpaperbackup",
+ "com.android.location.fused"
+ )
+ .also {
+ it.addAll(
+ SafetyCenterResourcesApk(context)
+ .getStringByName("config_NotificationListenerServicePregrants")
+ .split(",")
+ )
+ }
}
val pregrantedPackages: Set<String> by pregrantedPackagesDelegate
}
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceData.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceData.kt
index f5903dfc2..4abd7129c 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceData.kt
@@ -23,4 +23,4 @@ interface PrivacySourceData {
interface Creator<T> {
fun fromStorageData(data: String): T
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceStorageRepository.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceStorageRepository.kt
index 1eee90e31..621a7219d 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceStorageRepository.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceStorageRepository.kt
@@ -21,4 +21,4 @@ interface PrivacySourceStorageRepository {
fun persistData(dataList: List<PrivacySourceData>)
fun <T> readData(creator: PrivacySourceData.Creator<T>): List<T>
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/TextStorageRepository.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/TextStorageRepository.kt
index 8a8711bb6..a2e5b1376 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/TextStorageRepository.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/TextStorageRepository.kt
@@ -40,14 +40,16 @@ class TextStorageRepository(private val file: File) : PrivacySourceStorageReposi
override fun <T> readData(creator: PrivacySourceData.Creator<T>): List<T> {
try {
BufferedReader(FileReader(file)).useLines { lines ->
- return lines.mapNotNull {
- try {
- creator.fromStorageData(it)
- } catch (ex: Exception) {
- Log.e(LOG_TAG, "corrupted data : $it in file ${file.absolutePath}", ex)
- null
+ return lines
+ .mapNotNull {
+ try {
+ creator.fromStorageData(it)
+ } catch (ex: Exception) {
+ Log.e(LOG_TAG, "corrupted data : $it in file ${file.absolutePath}", ex)
+ null
+ }
}
- }.toList()
+ .toList()
}
} catch (ignored: FileNotFoundException) {
Log.e(LOG_TAG, "Could not find file ${file.absolutePath}")
@@ -66,4 +68,4 @@ class TextStorageRepository(private val file: File) : PrivacySourceStorageReposi
}
}
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/WorkPolicyInfo.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/WorkPolicyInfo.kt
index 2ab6d37f0..bf9f92398 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/WorkPolicyInfo.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/WorkPolicyInfo.kt
@@ -74,7 +74,10 @@ class WorkPolicyInfo(private val workPolicyUtils: WorkPolicyUtils) : PrivacySour
val safetySourceData: SafetySourceData? = createSafetySourceDataForWorkPolicy(context)
safetyCenterManager.setSafetySourceData(
- WORK_POLICY_INFO_SOURCE_ID, safetySourceData, safetyEvent)
+ WORK_POLICY_INFO_SOURCE_ID,
+ safetySourceData,
+ safetyEvent
+ )
}
private fun createSafetySourceDataForWorkPolicy(context: Context): SafetySourceData? {
@@ -84,16 +87,25 @@ class WorkPolicyInfo(private val workPolicyUtils: WorkPolicyUtils) : PrivacySour
when {
deviceOwnerIntent != null -> {
PendingIntent.getActivity(
- context, 0, deviceOwnerIntent, PendingIntent.FLAG_IMMUTABLE)
+ context,
+ 0,
+ deviceOwnerIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
}
profileOwnerIntent != null -> {
val managedProfileContext =
context.createPackageContextAsUser(
context.packageName,
0,
- UserHandle.of(workPolicyUtils.managedProfileUserId))
+ UserHandle.of(workPolicyUtils.managedProfileUserId)
+ )
PendingIntent.getActivity(
- managedProfileContext, 0, profileOwnerIntent, PendingIntent.FLAG_IMMUTABLE)
+ managedProfileContext,
+ 0,
+ profileOwnerIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
}
else -> null
}
@@ -102,10 +114,17 @@ class WorkPolicyInfo(private val workPolicyUtils: WorkPolicyUtils) : PrivacySour
val safetySourceStatus: SafetySourceStatus =
SafetySourceStatus.Builder(
Utils.getEnterpriseString(
- context, WORK_POLICY_TITLE, R.string.work_policy_title),
+ context,
+ WORK_POLICY_TITLE,
+ R.string.work_policy_title
+ ),
Utils.getEnterpriseString(
- context, WORK_POLICY_SUMMARY, R.string.work_policy_summary),
- SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED)
+ context,
+ WORK_POLICY_SUMMARY,
+ R.string.work_policy_summary
+ ),
+ SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
+ )
.setPendingIntent(pendingIntent)
.build()
@@ -120,7 +139,8 @@ class WorkPolicyInfo(private val workPolicyUtils: WorkPolicyUtils) : PrivacySour
RefreshEvent.EVENT_REFRESH_REQUESTED -> {
val refreshBroadcastId =
intent.getStringExtra(
- SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID)
+ SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID
+ )
SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
.setRefreshBroadcastId(refreshBroadcastId)
.build()
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/v34/AppDataSharingUpdatesPrivacySource.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/v34/AppDataSharingUpdatesPrivacySource.kt
index 8bedc3979..ec86a49a4 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/v34/AppDataSharingUpdatesPrivacySource.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/v34/AppDataSharingUpdatesPrivacySource.kt
@@ -71,13 +71,16 @@ class AppDataSharingUpdatesPrivacySource : PrivacySource {
SafetySourceStatus.Builder(
context.getString(R.string.data_sharing_updates_title),
context.getString(R.string.data_sharing_updates_summary),
- SEVERITY_LEVEL_INFORMATION)
+ SEVERITY_LEVEL_INFORMATION
+ )
.setPendingIntent(
PendingIntent.getActivity(
context,
/* requestCode= */ 0,
Intent(ACTION_REVIEW_APP_DATA_SHARING_UPDATES),
- FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE))
+ FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
+ )
.build(),
)
.build()
@@ -88,7 +91,8 @@ class AppDataSharingUpdatesPrivacySource : PrivacySource {
safetyCenterManager.setSafetySourceData(
APP_DATA_SHARING_UPDATES_SOURCE_ID,
safetySourceData,
- createSafetyEventForDataSharingUpdates(refreshEvent, intent))
+ createSafetyEventForDataSharingUpdates(refreshEvent, intent)
+ )
}
private fun createSafetyEventForDataSharingUpdates(
@@ -99,7 +103,8 @@ class AppDataSharingUpdatesPrivacySource : PrivacySource {
EVENT_REFRESH_REQUESTED -> {
val refreshBroadcastId =
intent.getStringExtra(
- SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID)
+ SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID
+ )
SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
.setRefreshBroadcastId(refreshBroadcastId)
.build()
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
index 06e5ed264..a8b16c521 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
@@ -196,20 +196,25 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
@NonNull CharSequence title, boolean checked, @Nullable ApplicationInfo applicationInfo,
@NonNull ArrayMap<String, Preference> oldPreferences,
@NonNull PreferenceScreen preferenceScreen, @NonNull Context context) {
- TwoStatePreference preference = (TwoStatePreference) oldPreferences.get(key);
- if (preference == null) {
- preference = requirePreferenceFragment().createApplicationPreference();
+ RoleApplicationPreference roleApplicationPreference =
+ (RoleApplicationPreference) oldPreferences.get(key);
+ TwoStatePreference preference;
+ if (roleApplicationPreference == null) {
+ roleApplicationPreference = requirePreferenceFragment().createApplicationPreference();
+ preference = roleApplicationPreference.asTwoStatePreference();
preference.setKey(key);
preference.setIcon(icon);
preference.setTitle(title);
preference.setPersistent(false);
preference.setOnPreferenceChangeListener((preference2, newValue) -> false);
preference.setOnPreferenceClickListener(this);
+ } else {
+ preference = roleApplicationPreference.asTwoStatePreference();
}
preference.setChecked(checked);
if (applicationInfo != null) {
- RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, preference,
+ RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, roleApplicationPreference,
applicationInfo, mUser, context);
}
@@ -281,7 +286,7 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
* @return a new preference for an application
*/
@NonNull
- TwoStatePreference createApplicationPreference();
+ RoleApplicationPreference createApplicationPreference();
/**
* Create a new preference for the footer.
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
index 3c8af1136..f9a0193bd 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
@@ -167,15 +167,19 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
RoleItem roleItem = roleItems.get(i);
Role role = roleItem.getRole();
- Preference preference = oldPreferences.get(role.getName());
- if (preference == null) {
- preference = (Preference) preferenceFragment.createPreference();
+ RolePreference rolePreference = (RolePreference) oldPreferences.get(role.getName());
+ Preference preference;
+ if (rolePreference == null) {
+ rolePreference = preferenceFragment.createPreference();
+ preference = rolePreference.asPreference();
preference.setKey(role.getName());
preference.setIconSpaceReserved(true);
preference.setTitle(role.getShortLabelResource());
preference.setPersistent(false);
preference.setOnPreferenceClickListener(listener);
preference.getExtras().putParcelable(Intent.EXTRA_USER, user);
+ } else {
+ preference = rolePreference.asPreference();
}
List<ApplicationInfo> holderApplicationInfos = roleItem.getHolderApplicationInfos();
@@ -187,8 +191,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
preference.setIcon(Utils.getBadgedIcon(context, holderApplicationInfo));
preference.setSummary(Utils.getAppLabel(holderApplicationInfo, context));
}
- RoleUiBehaviorUtils.preparePreferenceAsUser(role, (TwoTargetPreference) preference,
- user, context);
+ RoleUiBehaviorUtils.preparePreferenceAsUser(role, rolePreference, user, context);
preferenceGroup.addPreference(preference);
}
}
@@ -283,7 +286,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
* @return a new preference for a default app
*/
@NonNull
- TwoTargetPreference createPreference();
+ RolePreference createPreference();
/**
* Callback when changes have been made to the {@link PreferenceScreen} of the parent
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java
index 3a4312c00..827d42643 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.Process;
+import android.os.UserManager;
import android.provider.Telephony;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -154,6 +155,16 @@ public class RequestRoleActivity extends FragmentActivity {
return;
}
+ UserManager userManager = getSystemService(UserManager.class);
+ if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DEFAULT_APPS)) {
+ Log.w(LOG_TAG, "Cannot request role due to user restriction"
+ + " DISALLOW_CONFIG_DEFAULT_APPS, role: " + mRoleName);
+ reportRequestResult(PermissionControllerStatsLog
+ .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_RESTRICTION);
+ finish();
+ return;
+ }
+
if (!role.isPackageQualified(mPackageName, this)) {
Log.w(LOG_TAG, "Application doesn't qualify for role, role: " + mRoleName
+ ", package: " + mPackageName);
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java
new file mode 100644
index 000000000..1b5b27971
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java
@@ -0,0 +1,32 @@
+/*
+ * 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.role.ui;
+
+import androidx.annotation.NonNull;
+import androidx.preference.TwoStatePreference;
+
+/**
+ * Preference for application being a candidate or holding a role.
+ */
+public interface RoleApplicationPreference extends UserRestrictionAwarePreference {
+
+ /**
+ * Get instance of {@code this} as {@link TwoStatePreference}.
+ */
+ @NonNull
+ TwoStatePreference asTwoStatePreference();
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java
index b3e34236f..ca059aa32 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java
@@ -20,17 +20,18 @@ import android.content.Context;
import android.icu.text.Collator;
import androidx.annotation.NonNull;
-import androidx.arch.core.util.Function;
+
+import kotlin.jvm.functions.Function1;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
- * A function for {@link androidx.lifecycle.Transformations#map(androidx.lifecycle.LiveData,
- * Function)} that sorts a live data for role list.
+ * A function for {@link androidx.lifecycle#map(androidx.lifecycle.LiveData, Function1)}
+ * that sorts a live data for role list.
*/
-public class RoleListSortFunction implements Function<List<RoleItem>, List<RoleItem>> {
+public class RoleListSortFunction implements Function1<List<RoleItem>, List<RoleItem>> {
@NonNull
private final Comparator<RoleItem> mComparator;
@@ -42,10 +43,9 @@ public class RoleListSortFunction implements Function<List<RoleItem>, List<RoleI
roleItem.getRole().getShortLabelResource()), collator);
}
- @NonNull
@Override
- public List<RoleItem> apply(@NonNull List<RoleItem> input) {
- List<RoleItem> sorted = new ArrayList<>(input);
+ public List<RoleItem> invoke(List<RoleItem> p1) {
+ List<RoleItem> sorted = new ArrayList<>(p1);
sorted.sort(mComparator);
return sorted;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java
new file mode 100644
index 000000000..442963ce6
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java
@@ -0,0 +1,29 @@
+/*
+ * 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.role.ui;
+
+import androidx.preference.Preference;
+
+/**
+ * Preference used by the default apps list UI.
+ */
+public interface RolePreference extends TwoTargetPreference, UserRestrictionAwarePreference {
+ /**
+ * Return this preference as {@link Preference}.
+ */
+ Preference asPreference();
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java
index 5e74d5e89..10db9dbcd 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java
@@ -23,19 +23,20 @@ import android.os.UserHandle;
import android.util.Pair;
import androidx.annotation.NonNull;
-import androidx.arch.core.util.Function;
import com.android.permissioncontroller.permission.utils.Utils;
+import kotlin.jvm.functions.Function1;
+
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
- * A function for {@link androidx.lifecycle.Transformations#map(androidx.lifecycle.LiveData,
- * Function)} that sorts a live data for role.
+ * A function for {@link androidx.lifecycle#map(androidx.lifecycle.LiveData, Function1)}
+ * that sorts a live data for role.
*/
-public class RoleSortFunction implements Function<List<Pair<ApplicationInfo, Boolean>>,
+public class RoleSortFunction implements Function1<List<Pair<ApplicationInfo, Boolean>>,
List<Pair<ApplicationInfo, Boolean>>> {
@NonNull
@@ -51,11 +52,9 @@ public class RoleSortFunction implements Function<List<Pair<ApplicationInfo, Boo
mComparator = labelComparator.thenComparing(userIdComparator);
}
- @NonNull
@Override
- public List<Pair<ApplicationInfo, Boolean>> apply(
- @NonNull List<Pair<ApplicationInfo, Boolean>> input) {
- List<Pair<ApplicationInfo, Boolean>> sorted = new ArrayList<>(input);
+ public List<Pair<ApplicationInfo, Boolean>> invoke(List<Pair<ApplicationInfo, Boolean>> p1) {
+ List<Pair<ApplicationInfo, Boolean>> sorted = new ArrayList<>(p1);
sorted.sort(mComparator);
return sorted;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java
index 23044b833..3a8cd55d3 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java
@@ -18,6 +18,7 @@ package com.android.permissioncontroller.role.ui;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.preference.Preference;
/**
* {@link androidx.preference.Preference} with the widget layout as a separate target.
@@ -38,6 +39,11 @@ public interface TwoTargetPreference {
void setOnSecondTargetClickListener(@Nullable OnSecondTargetClickListener listener);
/**
+ * Return this preference as {@link Preference}.
+ */
+ Preference asPreference();
+
+ /**
* Listener for second target click.
*/
interface OnSecondTargetClickListener {
@@ -49,7 +55,4 @@ public interface TwoTargetPreference {
*/
void onSecondTargetClick(@NonNull TwoTargetPreference preference);
}
-
- /** @see androidx.preference.Preference#setEnabled(boolean) */
- void setEnabled(boolean enabled);
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java
new file mode 100644
index 000000000..e6bc9bab6
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java
@@ -0,0 +1,30 @@
+/*
+ * 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.role.ui;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Preference that is aware of user restrictions that can block them.
+ */
+public interface UserRestrictionAwarePreference {
+
+ /**
+ * Specifies user restriction that blocks this preference.
+ */
+ void setUserRestriction(@Nullable String userRestriction);
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java b/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java
new file mode 100644
index 000000000..033507991
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java
@@ -0,0 +1,69 @@
+/*
+ * 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.role.ui;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Intent;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * Mixin for implementing {@link UserRestrictionAwarePreference}.
+ */
+public class UserRestrictionAwarePreferenceMixin {
+
+ @NonNull
+ private final Preference mPreference;
+ @Nullable
+ private String mUserRestriction = null;
+
+ public UserRestrictionAwarePreferenceMixin(@NonNull Preference preference) {
+ mPreference = preference;
+ }
+
+ /**
+ * Implementation for {@link UserRestrictionAwarePreference#setUserRestriction}.
+ */
+ public void setUserRestriction(@Nullable String userRestriction) {
+ mUserRestriction = userRestriction;
+ mPreference.setEnabled(mUserRestriction == null);
+ }
+
+ /**
+ * Call after {@link Preference#onBindViewHolder} to apply blocking effects.
+ */
+ public void onAfterBindViewHolder(@NonNull PreferenceViewHolder holder) {
+ if (mUserRestriction != null) {
+ // We set the item view to enabled to make the preference row clickable.
+ // Normal disabled preferences have the whole view hierarchy disabled, so by making only
+ // the top-level itemView enabled, we don't change the fact that the whole preference
+ // still "looks" disabled (see Preference.onBindViewHolder).
+ // Preference.onBindViewHolder sets the onClickListener as well on each preference, so
+ // we don't need to unset the listener here (we wouldn't know the correct one anyway).
+ // This approach is used already by com.android.settingslib.RestrictedPreferenceHelper.
+ holder.itemView.setEnabled(true);
+ Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)
+ .putExtra(DevicePolicyManager.EXTRA_RESTRICTION, mUserRestriction);
+ holder.itemView.setOnClickListener(
+ (view) -> holder.itemView.getContext().startActivity(intent));
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java
index dbd4c7c03..dc6c03d09 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java
@@ -23,11 +23,11 @@ import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
-import androidx.preference.TwoStatePreference;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
import com.android.permissioncontroller.role.ui.DefaultAppChildFragment;
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
import com.android.role.controller.model.Role;
/** Screen to pick a default app for a particular {@link Role}. */
@@ -90,8 +90,8 @@ public class AutoDefaultAppFragment extends AutoSettingsFrameFragment implements
@NonNull
@Override
- public TwoStatePreference createApplicationPreference() {
- return new AutoDefaultAppPreference(requireContext());
+ public RoleApplicationPreference createApplicationPreference() {
+ return new AutoRadioPreference(requireContext());
}
@NonNull
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppListFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppListFragment.java
index b74caa7ed..081e3153b 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppListFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppListFragment.java
@@ -24,7 +24,7 @@ import androidx.annotation.Nullable;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
import com.android.permissioncontroller.role.ui.DefaultAppListChildFragment;
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.ui.RolePreference;
/** Shows various roles for which a default app can be picked. */
public class AutoDefaultAppListFragment extends AutoSettingsFrameFragment implements
@@ -57,8 +57,8 @@ public class AutoDefaultAppListFragment extends AutoSettingsFrameFragment implem
@NonNull
@Override
- public TwoTargetPreference createPreference() {
- return new AutoSettingsPreference(requireContext());
+ public RolePreference createPreference() {
+ return new AutoRolePreference(requireContext());
}
@Override
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java
index c91e40561..97a3dad26 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java
@@ -19,16 +19,23 @@ package com.android.permissioncontroller.role.ui.auto;
import android.content.Context;
import android.widget.RadioButton;
+import androidx.annotation.Nullable;
import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.TwoStatePreference;
import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
+import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin;
/** Preference used to represent apps that can be picked as a default app. */
-public class AutoDefaultAppPreference extends TwoStatePreference {
+public class AutoRadioPreference extends TwoStatePreference implements
+ RoleApplicationPreference {
- public AutoDefaultAppPreference(Context context) {
+ private final UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
+ new UserRestrictionAwarePreferenceMixin(this);
+
+ public AutoRadioPreference(Context context) {
super(context, null, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
android.R.attr.preferenceStyle));
init();
@@ -45,6 +52,18 @@ public class AutoDefaultAppPreference extends TwoStatePreference {
RadioButton radioButton = (RadioButton) holder.findViewById(R.id.radio_button);
radioButton.setChecked(isChecked());
+
+ mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ }
+
+ @Override
+ public void setUserRestriction(@Nullable String userRestriction) {
+ mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ }
+
+ @Override
+ public AutoRadioPreference asTwoStatePreference() {
+ return this;
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSettingsPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java
index ba5b17be9..d2f1b6cde 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSettingsPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java
@@ -22,34 +22,57 @@ import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+import com.android.permissioncontroller.role.ui.RolePreference;
import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin;
/**
* Preference for use in auto lists. Extends {@link TwoTargetPreference} in order to make sure of
* shared logic between phone and auto settings UI.
*/
-public class AutoSettingsPreference extends Preference implements TwoTargetPreference {
+public class AutoRolePreference extends Preference implements RolePreference {
- public AutoSettingsPreference(@NonNull Context context,
+ private UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
+ new UserRestrictionAwarePreferenceMixin(this);
+
+ public AutoRolePreference(@NonNull Context context,
@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
- public AutoSettingsPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+ public AutoRolePreference(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
- public AutoSettingsPreference(@NonNull Context context, @Nullable AttributeSet attrs) {
+ public AutoRolePreference(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
- public AutoSettingsPreference(@NonNull Context context) {
+ public AutoRolePreference(@NonNull Context context) {
super(context);
}
@Override
public void setOnSecondTargetClickListener(@Nullable OnSecondTargetClickListener listener) {
}
+
+ @Override
+ public void setUserRestriction(@Nullable String userRestriction) {
+ mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ }
+
+ @Override
+ public AutoRolePreference asPreference() {
+ return this;
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessFragment.java
index c561420da..c37735427 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessFragment.java
@@ -22,11 +22,10 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
-import androidx.preference.SwitchPreference;
-import androidx.preference.TwoStatePreference;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
import com.android.permissioncontroller.role.ui.specialappaccess.SpecialAppAccessChildFragment;
/** Automotive fragment for displaying special app access for a role. */
@@ -81,8 +80,8 @@ public class AutoSpecialAppAccessFragment extends AutoSettingsFrameFragment impl
@NonNull
@Override
- public TwoStatePreference createApplicationPreference() {
- return new SwitchPreference(requireContext());
+ public RoleApplicationPreference createApplicationPreference() {
+ return new AutoSwitchPreference(requireContext());
}
@NonNull
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessListFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessListFragment.java
index e2dce4a94..59e6766cc 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessListFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessListFragment.java
@@ -24,7 +24,7 @@ import androidx.annotation.Nullable;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.ui.RolePreference;
import com.android.permissioncontroller.role.ui.specialappaccess.SpecialAppAccessListChildFragment;
/** Automotive fragment for the list of role related special app accesses. */
@@ -59,8 +59,8 @@ public class AutoSpecialAppAccessListFragment extends AutoSettingsFrameFragment
@NonNull
@Override
- public TwoTargetPreference createPreference(@NonNull Context context) {
- return new AutoSettingsPreference(context);
+ public RolePreference createPreference(@NonNull Context context) {
+ return new AutoRolePreference(context);
}
@Override
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java
new file mode 100644
index 000000000..900e58551
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java
@@ -0,0 +1,76 @@
+/*
+ * 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.role.ui.auto;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.SwitchPreference;
+
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
+import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin;
+
+/**
+ * Role application preference represented as a switch.
+ */
+public class AutoSwitchPreference extends SwitchPreference
+ implements RoleApplicationPreference {
+
+ private UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
+ new UserRestrictionAwarePreferenceMixin(this);
+
+ public AutoSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public AutoSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+ @StyleRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AutoSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AutoSwitchPreference(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ public void setUserRestriction(@Nullable String userRestriction) {
+ mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ }
+
+ @NonNull
+ @Override
+ public AutoSwitchPreference asTwoStatePreference() {
+ return this;
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java
index 8a5c8bdc7..e6b8dabe1 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java
@@ -40,9 +40,6 @@ public class DialerRoleUiBehavior implements RoleUiBehavior {
public void prepareApplicationPreferenceAsUser(@NonNull Role role,
@NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
@NonNull UserHandle user, @NonNull Context context) {
- RoleUiBehavior.super.prepareApplicationPreferenceAsUser(
- role, preference, applicationInfo, user, context);
-
TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
String systemPackageName = telecomManager.getSystemDialerPackage();
if (Objects.equals(applicationInfo.packageName, systemPackageName)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java
index 36bbd1cb1..d0e7c0eef 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java
@@ -56,8 +56,6 @@ public class HomeRoleUiBehavior implements RoleUiBehavior {
@Override
public void preparePreferenceAsUser(@NonNull Role role, @NonNull TwoTargetPreference preference,
@NonNull UserHandle user, @NonNull Context context) {
- RoleUiBehavior.super.preparePreferenceAsUser(role, preference, user, context);
-
TwoTargetPreference.OnSecondTargetClickListener listener = null;
RoleManager roleManager = context.getSystemService(RoleManager.class);
String packageName = CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser(
@@ -95,9 +93,6 @@ public class HomeRoleUiBehavior implements RoleUiBehavior {
public void prepareApplicationPreferenceAsUser(@NonNull Role role,
@NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
@NonNull UserHandle user, @NonNull Context context) {
- RoleUiBehavior.super.prepareApplicationPreferenceAsUser(
- role, preference, applicationInfo, user, context);
-
boolean missingWorkProfileSupport = isMissingWorkProfileSupport(applicationInfo, context);
if (preference.isEnabled()) {
preference.setEnabled(!missingWorkProfileSupport);
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java
index 13343e926..6e3b47fba 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java
@@ -20,13 +20,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.UserHandle;
-import android.os.UserManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
-import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.role.ui.TwoTargetPreference;
import com.android.role.controller.model.Role;
@@ -75,13 +73,7 @@ public interface RoleUiBehavior {
default void preparePreferenceAsUser(@NonNull Role role,
@NonNull TwoTargetPreference preference,
@NonNull UserHandle user,
- @NonNull Context context) {
- if (SdkLevel.isAtLeastU() && role.isExclusive()) {
- final UserManager userManager = context.getSystemService(UserManager.class);
- preference.setEnabled(!userManager.hasUserRestrictionForUser(
- UserManager.DISALLOW_CONFIG_DEFAULT_APPS, user));
- }
- }
+ @NonNull Context context) {}
/**
* Check whether a qualifying application should be visible to user.
@@ -108,13 +100,7 @@ public interface RoleUiBehavior {
*/
default void prepareApplicationPreferenceAsUser(@NonNull Role role,
@NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
- @NonNull UserHandle user, @NonNull Context context) {
- if (SdkLevel.isAtLeastU() && role.isExclusive()) {
- final UserManager userManager = context.getSystemService(UserManager.class);
- preference.setEnabled(!userManager.hasUserRestrictionForUser(
- UserManager.DISALLOW_CONFIG_DEFAULT_APPS, user));
- }
- }
+ @NonNull UserHandle user, @NonNull Context context) {}
/**
* Get the confirmation message for adding an application as a holder of this role.
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppListPreferenceFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppListPreferenceFragment.java
index b76b8c561..da920ea7f 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppListPreferenceFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppListPreferenceFragment.java
@@ -23,7 +23,7 @@ import androidx.annotation.Nullable;
import androidx.preference.PreferenceFragmentCompat;
import com.android.permissioncontroller.role.ui.DefaultAppListChildFragment;
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.ui.RolePreference;
/**
* Handheld preference fragment for the list of default apps.
@@ -62,8 +62,8 @@ public class HandheldDefaultAppListPreferenceFragment extends PreferenceFragment
@NonNull
@Override
- public TwoTargetPreference createPreference() {
- return new SettingsButtonPreference(requireContext());
+ public RolePreference createPreference() {
+ return new HandheldRolePreference(requireContext());
}
@Override
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppPreferenceFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppPreferenceFragment.java
index 94c07ef67..b8156590a 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppPreferenceFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppPreferenceFragment.java
@@ -24,11 +24,10 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
-import androidx.preference.TwoStatePreference;
import com.android.permissioncontroller.role.ui.DefaultAppChildFragment;
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
import com.android.settingslib.widget.FooterPreference;
-import com.android.settingslib.widget.SelectorWithWidgetPreference;
/**
* Handheld preference fragment for a default app.
@@ -96,8 +95,8 @@ public class HandheldDefaultAppPreferenceFragment extends PreferenceFragmentComp
@NonNull
@Override
- public TwoStatePreference createApplicationPreference() {
- return new SelectorWithWidgetPreference(requireContext());
+ public RoleApplicationPreference createApplicationPreference() {
+ return new HandheldRadioPreference(requireContext());
}
@NonNull
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java
new file mode 100644
index 000000000..d9ef047d6
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java
@@ -0,0 +1,75 @@
+/*
+ * 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.role.ui.handheld;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
+import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin;
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+
+/**
+ * Preference used to represent apps that can be picked as a default app.
+ */
+public class HandheldRadioPreference extends SelectorWithWidgetPreference implements
+ RoleApplicationPreference {
+
+ private final UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
+ new UserRestrictionAwarePreferenceMixin(this);
+
+ public HandheldRadioPreference(@NonNull Context context,
+ @Nullable AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public HandheldRadioPreference(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public HandheldRadioPreference(@NonNull Context context, boolean isCheckbox) {
+ super(context, isCheckbox);
+ }
+
+ public HandheldRadioPreference(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ public void setUserRestriction(
+ @Nullable String userRestriction) {
+ mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ }
+
+ @NonNull
+ @Override
+ public HandheldRadioPreference asTwoStatePreference() {
+ return this;
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/SettingsButtonPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java
index f54c9d95d..978fe7d5a 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/SettingsButtonPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java
@@ -28,7 +28,9 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.ui.RolePreference;
+import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin;
+import com.android.settingslib.widget.TwoTargetPreference;
/**
* {@link Preference} with a settings button.
@@ -36,33 +38,35 @@ import com.android.permissioncontroller.role.ui.TwoTargetPreference;
* @see com.android.settings.widget.GearPreference
*/
// Made public for com.android.permissioncontroller.role.ui.specialappaccess.handheld
-public class SettingsButtonPreference extends com.android.settingslib.widget.TwoTargetPreference
- implements TwoTargetPreference {
+public class HandheldRolePreference extends TwoTargetPreference implements RolePreference {
+
+ private final UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
+ new UserRestrictionAwarePreferenceMixin(this);
@Nullable
private OnSecondTargetClickListener mOnSecondTargetClickListener;
- public SettingsButtonPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+ public HandheldRolePreference(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
- public SettingsButtonPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+ public HandheldRolePreference(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
- public SettingsButtonPreference(@NonNull Context context, @Nullable AttributeSet attrs) {
+ public HandheldRolePreference(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
- public SettingsButtonPreference(@NonNull Context context) {
+ public HandheldRolePreference(@NonNull Context context) {
super(context);
init();
@@ -89,6 +93,11 @@ public class SettingsButtonPreference extends com.android.settingslib.widget.Two
}
@Override
+ public void setUserRestriction(@Nullable String userRestriction) {
+ mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ }
+
+ @Override
public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
@@ -103,5 +112,12 @@ public class SettingsButtonPreference extends com.android.settingslib.widget.Two
}
// Make the settings button enabled even if the preference itself is disabled.
settingsButton.setEnabled(true);
+
+ mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ }
+
+ @Override
+ public HandheldRolePreference asPreference() {
+ return this;
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
index d75747b52..b95440bbd 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
@@ -37,6 +37,7 @@ import androidx.preference.TwoStatePreference;
import com.android.permissioncontroller.permission.utils.Utils;
import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData;
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -143,9 +144,12 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat
String key = qualifyingApplicationInfo.packageName + '_'
+ qualifyingApplicationInfo.uid;
- TwoStatePreference preference = (TwoStatePreference) oldPreferences.get(key);
- if (preference == null) {
- preference = preferenceFragment.createApplicationPreference();
+ RoleApplicationPreference roleApplicationPreference =
+ (RoleApplicationPreference) oldPreferences.get(key);
+ TwoStatePreference preference;
+ if (roleApplicationPreference == null) {
+ roleApplicationPreference = preferenceFragment.createApplicationPreference();
+ preference = roleApplicationPreference.asTwoStatePreference();
preference.setKey(key);
preference.setIcon(Utils.getBadgedIcon(context, qualifyingApplicationInfo));
preference.setTitle(Utils.getFullAppLabel(qualifyingApplicationInfo, context));
@@ -154,11 +158,13 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat
preference.setOnPreferenceClickListener(this);
preference.getExtras().putParcelable(PREFERENCE_EXTRA_APPLICATION_INFO,
qualifyingApplicationInfo);
+ } else {
+ preference = roleApplicationPreference.asTwoStatePreference();
}
preference.setChecked(isHolderPackage);
UserHandle user = UserHandle.getUserHandleForUid(qualifyingApplicationInfo.uid);
- RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, preference,
+ RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, roleApplicationPreference,
qualifyingApplicationInfo, user, context);
preferenceScreen.addPreference(preference);
}
@@ -228,7 +234,7 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat
* @return a new preference for an application
*/
@NonNull
- TwoStatePreference createApplicationPreference();
+ RoleApplicationPreference createApplicationPreference();
/**
* Create a new preference for the footer.
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java
index ec4de84e1..4b256cef0 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java
@@ -33,7 +33,7 @@ import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import com.android.permissioncontroller.role.ui.RoleItem;
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.ui.RolePreference;
import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -102,16 +102,20 @@ public class SpecialAppAccessListChildFragment<PF extends PreferenceFragmentComp
RoleItem roleItem = roleItems.get(i);
Role role = roleItem.getRole();
- Preference preference = oldPreferences.get(role.getName());
- if (preference == null) {
- preference = (Preference) preferenceFragment.createPreference(context);
+ RolePreference rolePreference = (RolePreference) oldPreferences.get(role.getName());
+ Preference preference;
+ if (rolePreference == null) {
+ rolePreference = preferenceFragment.createPreference(context);
+ preference = rolePreference.asPreference();
preference.setKey(role.getName());
preference.setIconSpaceReserved(true);
preference.setTitle(role.getShortLabelResource());
preference.setPersistent(false);
preference.setOnPreferenceClickListener(this);
+ } else {
+ preference = rolePreference.asPreference();
}
- RoleUiBehaviorUtils.preparePreferenceAsUser(role, (TwoTargetPreference) preference,
+ RoleUiBehaviorUtils.preparePreferenceAsUser(role, rolePreference,
Process.myUserHandle(),
context);
preferenceScreen.addPreference(preference);
@@ -153,7 +157,7 @@ public class SpecialAppAccessListChildFragment<PF extends PreferenceFragmentComp
* @return a new preference for a special app access
*/
@NonNull
- TwoTargetPreference createPreference(@NonNull Context context);
+ RolePreference createPreference(@NonNull Context context);
/**
* Callback when changes have been made to the {@link PreferenceScreen} of the parent
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessListPreferenceFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessListPreferenceFragment.java
index e0d7884a1..26d858d72 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessListPreferenceFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessListPreferenceFragment.java
@@ -23,8 +23,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceFragmentCompat;
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
-import com.android.permissioncontroller.role.ui.handheld.SettingsButtonPreference;
+import com.android.permissioncontroller.role.ui.RolePreference;
+import com.android.permissioncontroller.role.ui.handheld.HandheldRolePreference;
import com.android.permissioncontroller.role.ui.specialappaccess.SpecialAppAccessListChildFragment;
/**
@@ -65,8 +65,8 @@ public class HandheldSpecialAppAccessListPreferenceFragment extends PreferenceFr
@NonNull
@Override
- public TwoTargetPreference createPreference(@NonNull Context context) {
- return new SettingsButtonPreference(context);
+ public RolePreference createPreference(@NonNull Context context) {
+ return new HandheldRolePreference(context);
}
@Override
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessPreferenceFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessPreferenceFragment.java
index c1bb0fb23..bfcbefdca 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessPreferenceFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessPreferenceFragment.java
@@ -23,10 +23,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
-import androidx.preference.TwoStatePreference;
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
import com.android.permissioncontroller.role.ui.specialappaccess.SpecialAppAccessChildFragment;
-import com.android.settingslib.widget.AppSwitchPreference;
import com.android.settingslib.widget.FooterPreference;
/**
@@ -90,8 +89,8 @@ public class HandheldSpecialAppAccessPreferenceFragment extends PreferenceFragme
@NonNull
@Override
- public TwoStatePreference createApplicationPreference() {
- return new AppSwitchPreference(requireContext());
+ public RoleApplicationPreference createApplicationPreference() {
+ return new HandheldSwitchPreference(requireContext());
}
@NonNull
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java
new file mode 100644
index 000000000..1b4dd78a4
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java
@@ -0,0 +1,74 @@
+/*
+ * 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.role.ui.specialappaccess.handheld;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
+import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin;
+import com.android.settingslib.widget.AppSwitchPreference;
+
+/** {@link AppSwitchPreference} that is a role application preference. */
+public class HandheldSwitchPreference extends AppSwitchPreference
+ implements RoleApplicationPreference {
+
+ private UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
+ new UserRestrictionAwarePreferenceMixin(this);
+
+ public HandheldSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public HandheldSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public HandheldSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public HandheldSwitchPreference(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ public void setUserRestriction(@Nullable String userRestriction) {
+ mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ }
+
+ @NonNull
+ @Override
+ public HandheldSwitchPreference asTwoStatePreference() {
+ return this;
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java
index e60bc6d76..6081695b5 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java
@@ -21,13 +21,16 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Process;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.preference.Preference;
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
+import com.android.permissioncontroller.role.ui.RolePreference;
+import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreference;
import com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior;
import com.android.role.controller.model.Role;
@@ -100,8 +103,10 @@ public final class RoleUiBehaviorUtils {
* @see RoleUiBehavior#preparePreferenceAsUser
*/
public static void preparePreferenceAsUser(@NonNull Role role,
- @NonNull TwoTargetPreference preference, @NonNull UserHandle user,
+ @NonNull RolePreference preference, @NonNull UserHandle user,
@NonNull Context context) {
+ prepareUserRestrictionAwarePreferenceAsUser(role, preference, user, context);
+
RoleUiBehavior uiBehavior = getUiBehavior(role);
if (uiBehavior == null) {
return;
@@ -126,16 +131,32 @@ public final class RoleUiBehaviorUtils {
* @see RoleUiBehavior#prepareApplicationPreferenceAsUser
*/
public static void prepareApplicationPreferenceAsUser(@NonNull Role role,
- @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
- @NonNull UserHandle user, @NonNull Context context) {
+ @NonNull RoleApplicationPreference preference,
+ @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
+ @NonNull Context context) {
+ prepareUserRestrictionAwarePreferenceAsUser(role, preference, user, context);
+
RoleUiBehavior uiBehavior = getUiBehavior(role);
if (uiBehavior == null) {
return;
}
- uiBehavior.prepareApplicationPreferenceAsUser(role, preference, applicationInfo, user,
+ uiBehavior.prepareApplicationPreferenceAsUser(
+ role, preference.asTwoStatePreference(), applicationInfo, user,
context);
}
+ private static void prepareUserRestrictionAwarePreferenceAsUser(@NonNull Role role,
+ @NonNull UserRestrictionAwarePreference preference, @NonNull UserHandle user,
+ @NonNull Context context) {
+ if (SdkLevel.isAtLeastU() && role.isExclusive()) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ boolean hasDisallowConfigDefaultApps = userManager.hasUserRestrictionForUser(
+ UserManager.DISALLOW_CONFIG_DEFAULT_APPS, user);
+ preference.setUserRestriction(hasDisallowConfigDefaultApps
+ ? UserManager.DISALLOW_CONFIG_DEFAULT_APPS : null);
+ }
+ }
+
/**
* @see RoleUiBehavior#getConfirmationMessage
*/
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java
index e959d20be..d97b5034f 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java
@@ -39,6 +39,8 @@ import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
+import java.time.Duration;
+
/**
* Uses {@link android.app.job.JobScheduler} to schedule periodic calls to {@link
* SafetyCenterManager#refreshSafetySources} after boot completed if safety center is already
@@ -46,7 +48,6 @@ import com.android.modules.utils.build.SdkLevel;
*
* <p>The job waits until the device is in idle mode to minimize impact on system health.
*/
-// TODO(b/243493200): Add tests
public final class SafetyCenterBackgroundRefreshJobService extends JobService {
private static final String TAG = "SafetyCenterBackgroundR";
@@ -76,7 +77,7 @@ public final class SafetyCenterBackgroundRefreshJobService extends JobService {
Context context, @Nullable String actionString) {
if (!isActionStringValid(actionString)) {
- Log.v(TAG, "Ignoring a " + actionString + " broadcast.");
+ Log.i(TAG, "Ignoring a " + actionString + " broadcast.");
return;
}
@@ -104,30 +105,26 @@ public final class SafetyCenterBackgroundRefreshJobService extends JobService {
return;
}
+ Duration periodicBackgroundRefreshInterval =
+ SafetyCenterJobServiceFlags.getPeriodicBackgroundRefreshInterval();
JobInfo jobInfo =
new JobInfo.Builder(
SAFETY_CENTER_BACKGROUND_REFRESH_JOB_ID,
new ComponentName(
context, SafetyCenterBackgroundRefreshJobService.class))
.setRequiresDeviceIdle(true)
- .setRequiresCharging(
- SafetyCenterJobServiceFlags.getBackgroundRefreshRequiresCharging())
- .setPeriodic(
- SafetyCenterJobServiceFlags.getPeriodicBackgroundRefreshInterval()
- .toMillis())
+ .setRequiresCharging(true)
+ .setPeriodic(periodicBackgroundRefreshInterval.toMillis())
.build();
Log.v(
TAG,
- "Scheduling a periodic background refresh with "
- + ", interval="
- + jobInfo.getIntervalMillis()
- + "requires charging="
- + jobInfo.isRequireCharging());
+ "Scheduling a periodic background refresh with interval="
+ + periodicBackgroundRefreshInterval);
int scheduleResult = jobScheduler.schedule(jobInfo);
if (scheduleResult != RESULT_SUCCESS) {
- Log.e(
+ Log.w(
TAG,
"Could not schedule the background refresh job, scheduleResult="
+ scheduleResult);
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterJobServiceFlags.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterJobServiceFlags.java
index bdca4d77d..23e048e7b 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterJobServiceFlags.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterJobServiceFlags.java
@@ -26,8 +26,6 @@ public class SafetyCenterJobServiceFlags {
private static final Duration DEFAULT_PERIODIC_BACKGROUND_REFRESH_INTERVAL = Duration.ofDays(1);
private static final String PROPERTY_BACKGROUND_REFRESH_IS_ENABLED =
"safety_center_background_refresh_is_enabled";
- private static final String PROPERTY_BACKGROUND_REFRESH_REQUIRES_CHARGING =
- "safety_center_background_requires_charging";
private static final String PROPERTY_PERIODIC_BACKGROUND_REFRESH_INTERVAL_MILLIS =
"safety_center_periodic_background_interval_millis";
@@ -47,15 +45,4 @@ public class SafetyCenterJobServiceFlags {
PROPERTY_PERIODIC_BACKGROUND_REFRESH_INTERVAL_MILLIS,
DEFAULT_PERIODIC_BACKGROUND_REFRESH_INTERVAL.toMillis()));
}
-
- /**
- * Returns whether we should constrain background refresh jobs to only run when the device is
- * charging.
- */
- static boolean getBackgroundRefreshRequiresCharging() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_BACKGROUND_REFRESH_REQUIRES_CHARGING,
- true);
- }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterSearchIndexablesProvider.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterSearchIndexablesProvider.kt
index 8e5e63452..88dd8a6e3 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterSearchIndexablesProvider.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterSearchIndexablesProvider.kt
@@ -52,7 +52,7 @@ import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsVie
import com.android.safetycenter.internaldata.SafetyCenterBundles
import com.android.safetycenter.internaldata.SafetyCenterEntryId
import com.android.safetycenter.internaldata.SafetyCenterIds
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
/** [android.provider.SearchIndexablesProvider] for Safety Center. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@@ -67,7 +67,7 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
val context = requireContext()
val safetyCenterManager =
context.getSystemService(SafetyCenterManager::class.java) ?: return cursor
- val resourcesContext = SafetyCenterResourcesContext(context)
+ val safetyCenterResourcesApk = SafetyCenterResourcesApk(context)
val screenTitle = context.getString(R.string.safety_center_dashboard_page_title)
@@ -76,7 +76,11 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
SdkLevel.isAtLeastU() &&
safetySourcesGroup.type == SAFETY_SOURCES_GROUP_TYPE_STATEFUL
) {
- cursor.addSafetySourcesGroupRow(safetySourcesGroup, resourcesContext, screenTitle)
+ cursor.addSafetySourcesGroupRow(
+ safetySourcesGroup,
+ safetyCenterResourcesApk,
+ screenTitle
+ )
}
safetySourcesGroup.safetySources
.asSequence()
@@ -85,7 +89,7 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
cursor.addSafetySourceRow(
context,
safetySource,
- resourcesContext,
+ safetyCenterResourcesApk,
safetyCenterManager,
screenTitle
)
@@ -139,11 +143,12 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
private fun MatrixCursor.addSafetySourcesGroupRow(
safetySourcesGroups: SafetySourcesGroup,
- resourcesContext: SafetyCenterResourcesContext,
+ safetyCenterResourcesApk: SafetyCenterResourcesApk,
screenTitle: String,
) {
val groupTitle =
- resourcesContext.getNotEmptyStringOrNull(safetySourcesGroups.titleResId) ?: return
+ safetyCenterResourcesApk.getNotEmptyStringOrNull(safetySourcesGroups.titleResId)
+ ?: return
newRow()
.add(COLUMN_RANK, 0)
@@ -157,11 +162,12 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
private fun MatrixCursor.addSafetySourceRow(
context: Context,
safetySource: SafetySource,
- resourcesContext: SafetyCenterResourcesContext,
+ safetyCenterResourcesApk: SafetyCenterResourcesApk,
safetyCenterManager: SafetyCenterManager,
screenTitle: String,
) {
- val searchTerms = resourcesContext.getNotEmptyStringOrNull(safetySource.searchTermsResId)
+ val searchTerms =
+ safetyCenterResourcesApk.getNotEmptyStringOrNull(safetySource.searchTermsResId)
var isPersonalEntryAdded = false
var isWorkEntryAdded = false
@@ -194,19 +200,19 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
}
if (!isPersonalEntryAdded) {
- resourcesContext.getNotEmptyStringOrNull(safetySource.titleResId)?.let {
+ safetyCenterResourcesApk.getNotEmptyStringOrNull(safetySource.titleResId)?.let {
addIndexableRow(title = it, isWorkProfile = false)
}
}
if (!isWorkEntryAdded && safetySource.profile == SafetySource.PROFILE_ALL) {
- resourcesContext.getNotEmptyStringOrNull(safetySource.titleForWorkResId)?.let {
+ safetyCenterResourcesApk.getNotEmptyStringOrNull(safetySource.titleForWorkResId)?.let {
addIndexableRow(title = it, isWorkProfile = true)
}
}
}
- private fun Context.getNotEmptyStringOrNull(resId: Int): String? =
+ private fun SafetyCenterResourcesApk.getNotEmptyStringOrNull(resId: Int): String? =
if (resId != Resources.ID_NULL) {
getString(resId).takeIf { it.isNotEmpty() }
} else {
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableGroupCardHelper.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableGroupCardHelper.kt
index cdc5cc1f3..20ac4c7a5 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableGroupCardHelper.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableGroupCardHelper.kt
@@ -27,7 +27,7 @@ internal class CollapsableGroupCardHelper {
private companion object {
private const val EXPANDED_ENTRY_GROUPS_SAVED_INSTANCE_STATE_KEY =
- "expanded_entry_groups_saved_instance_state_key"
+ "expanded_entry_groups_saved_instance_state_key"
}
fun restoreState(state: Bundle?) {
@@ -39,8 +39,8 @@ internal class CollapsableGroupCardHelper {
fun saveState(outState: Bundle) {
outState.putCharSequenceArray(
- EXPANDED_ENTRY_GROUPS_SAVED_INSTANCE_STATE_KEY,
- expandedGroups.toTypedArray()
+ EXPANDED_ENTRY_GROUPS_SAVED_INSTANCE_STATE_KEY,
+ expandedGroups.toTypedArray()
)
}
@@ -52,6 +52,5 @@ internal class CollapsableGroupCardHelper {
expandedGroups.add(groupId)
}
- fun isGroupExpanded(groupId: CharSequence): Boolean =
- expandedGroups.contains(groupId)
+ fun isGroupExpanded(groupId: CharSequence): Boolean = expandedGroups.contains(groupId)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreference.kt
index 1950a0976..d5c7c3738 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreference.kt
@@ -17,14 +17,15 @@ package com.android.permissioncontroller.safetycenter.ui
import androidx.preference.Preference
-/** Allows comparison with a [Preference] to determine if it has been changed.
+/**
+ * Allows comparison with a [Preference] to determine if it has been changed.
*
* @see SafetyPreferenceComparisonCallback
*/
internal interface ComparablePreference {
- /** Returns true if given Preference represents an item of the same kind. */
+ /** Returns true if given Preference represents an item of the same kind. */
fun isSameItem(preference: Preference): Boolean
- /** Returns true if given Preference contains the same data. */
+ /** Returns true if given Preference contains the same data. */
fun hasSameContents(preference: Preference): Boolean
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt
index 55d27dbb3..7a9d164cf 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt
@@ -25,13 +25,13 @@ import androidx.preference.PreferenceCategory
import com.android.permissioncontroller.R
/** A {@link PreferenceCategory} that implements {@link ComparablePreference} interface. */
-internal class ComparablePreferenceCategory @JvmOverloads constructor(
+internal class ComparablePreferenceCategory
+@JvmOverloads
+constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleAttr: Int = getAttr(
- context,
- R.attr.preferenceCategoryStyle,
- android.R.attr.preferenceCategoryStyle),
+ defStyleAttr: Int =
+ getAttr(context, R.attr.preferenceCategoryStyle, android.R.attr.preferenceCategoryStyle),
defStyleRes: Int = 0
) : PreferenceCategory(context, attrs, defStyleAttr, defStyleRes), ComparablePreference {
@@ -47,8 +47,8 @@ internal class ComparablePreferenceCategory @JvmOverloads constructor(
}
override fun isSameItem(preference: Preference): Boolean =
- preference is ComparablePreferenceCategory && TextUtils.equals(key, preference.key)
+ preference is ComparablePreferenceCategory && TextUtils.equals(key, preference.key)
override fun hasSameContents(preference: Preference): Boolean =
- preference is ComparablePreferenceCategory && TextUtils.equals(title, preference.title)
+ preference is ComparablePreferenceCategory && TextUtils.equals(title, preference.title)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EntriesTopPaddingPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EntriesTopPaddingPreference.kt
new file mode 100644
index 000000000..077ccfa2d
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EntriesTopPaddingPreference.kt
@@ -0,0 +1,13 @@
+package com.android.permissioncontroller.safetycenter.ui
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.preference.Preference
+import com.android.permissioncontroller.R
+
+class EntriesTopPaddingPreference(context: Context, attrs: AttributeSet) :
+ Preference(context, attrs) {
+ init {
+ layoutResource = R.layout.preference_entries_top_padding
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EqualWidthContainer.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EqualWidthContainer.kt
index 6f56874eb..1bdc0dc39 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EqualWidthContainer.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EqualWidthContainer.kt
@@ -43,7 +43,8 @@ class EqualWidthContainer @JvmOverloads constructor(context: Context, attrs: Att
nonSpaceItems.forEach {
it.measure(
MeasureSpec.makeMeasureSpec(neededWidthPerNonSpaceItem, EXACTLY),
- MeasureSpec.makeMeasureSpec(it.measuredHeight, EXACTLY))
+ MeasureSpec.makeMeasureSpec(it.measuredHeight, EXACTLY)
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt
index 58bec87b8..51f550944 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt
@@ -43,10 +43,7 @@ import java.math.BigInteger
import java.security.MessageDigest
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
-class InteractionLogger
-private constructor(
- private val noLogSourceIds: Set<String?>
-) {
+class InteractionLogger private constructor(private val noLogSourceIds: Set<String?>) {
var sessionId: Long = Constants.INVALID_SESSION_ID
var viewType: ViewType = ViewType.UNKNOWN
var navigationSource: NavigationSource = NavigationSource.UNKNOWN
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt
index a0c7c01b9..c83737649 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt
@@ -55,44 +55,47 @@ class IssueCardAnimator(val callback: AnimationCallback) {
// Ensure AVD is reset before transition starts
(resolvedImageView.drawable as AnimatedVectorDrawable).reset()
- val defaultIssueContentGroup = holder.findViewById(R.id.default_issue_content)
- val resolvedIssueContentGroup = holder.findViewById(R.id.resolved_issue_content)
-
- val transitionSet = TransitionSet()
- .setOrdering(TransitionSet.ORDERING_SEQUENTIAL)
- .setInterpolator(linearInterpolator)
- .addTransition(hideIssueContentTransition)
- .addTransition(
- showResolvedImageTransition
- .clone()
- .addListener(
- object : TransitionListenerAdapter() {
- override fun onTransitionEnd(
- transition: Transition
- ) {
- super.onTransitionEnd(transition)
- startIssueResolvedAnimation(
- resolvedIssueContentGroup,
- resolvedImageView
- )
+ val defaultIssueContentGroup = holder.findViewById(R.id.default_issue_content)!!
+ val resolvedIssueContentGroup = holder.findViewById(R.id.resolved_issue_content)!!
+
+ val transitionSet =
+ TransitionSet()
+ .setOrdering(TransitionSet.ORDERING_SEQUENTIAL)
+ .setInterpolator(linearInterpolator)
+ .addTransition(hideIssueContentTransition)
+ .addTransition(
+ showResolvedImageTransition
+ .clone()
+ .addListener(
+ object : TransitionListenerAdapter() {
+ override fun onTransitionEnd(transition: Transition) {
+ super.onTransitionEnd(transition)
+ startIssueResolvedAnimation(
+ resolvedIssueContentGroup,
+ resolvedImageView
+ )
+ }
}
- })
- )
- .addTransition(showResolvedTextTransition)
+ )
+ )
+ .addTransition(showResolvedTextTransition)
// Defer transition so that it's called after the root ViewGroup has been laid out.
holder.itemView.post {
TransitionManager.beginDelayedTransition(
- defaultIssueContentGroup.parent as ViewGroup?, transitionSet
+ defaultIssueContentGroup.parent as ViewGroup?,
+ transitionSet
)
// Setting INVISIBLE rather than GONE to ensure consistent card height between
// view groups.
defaultIssueContentGroup.visibility = View.INVISIBLE
- // These two views are outside of the group since their visibility must be set
+ // These views are outside of the group since their visibility must be set
// independently of the rest of the group, and some frustrating constraints of
// constraint layout's behavior. See b/242705351 for context.
+ makeInvisibleIfVisible(holder.findViewById(R.id.issue_card_attribution_title))
+ makeInvisibleIfVisible(holder.findViewById(R.id.issue_card_dismiss_btn))
makeInvisibleIfVisible(holder.findViewById(R.id.issue_card_subtitle))
makeInvisibleIfVisible(holder.findViewById(R.id.issue_card_protected_by_android))
@@ -111,7 +114,8 @@ class IssueCardAnimator(val callback: AnimationCallback) {
resolvedImageView
)
}
- })
+ }
+ )
}
private fun makeInvisibleIfVisible(view: View?) {
@@ -133,23 +137,27 @@ class IssueCardAnimator(val callback: AnimationCallback) {
super.onAnimationEnd(drawable)
transitionResolvedIssueUiToHiddenAndMarkComplete(resolvedIssueContentGroup)
}
- })
+ }
+ )
animatedDrawable.start()
}
private fun transitionResolvedIssueUiToHiddenAndMarkComplete(resolvedIssueContentGroup: View) {
- val hideTransition = hideResolvedUiTransition
- .clone()
- .setInterpolator(linearInterpolator)
- .addListener(
- object : TransitionListenerAdapter() {
- override fun onTransitionEnd(transition: Transition) {
- super.onTransitionEnd(transition)
- callback.markIssueResolvedUiCompleted()
+ val hideTransition =
+ hideResolvedUiTransition
+ .clone()
+ .setInterpolator(linearInterpolator)
+ .addListener(
+ object : TransitionListenerAdapter() {
+ override fun onTransitionEnd(transition: Transition) {
+ super.onTransitionEnd(transition)
+ callback.markIssueResolvedUiCompleted()
+ }
}
- })
+ )
TransitionManager.beginDelayedTransition(
- resolvedIssueContentGroup.parent as ViewGroup, hideTransition
+ resolvedIssueContentGroup.parent as ViewGroup,
+ hideTransition
)
resolvedIssueContentGroup.visibility = View.GONE
}
@@ -191,10 +199,14 @@ class IssueCardAnimator(val callback: AnimationCallback) {
// Using getter due to reliance on DeviceConfig property modification in tests
private val hideResolvedUiTransitionDelay
- get() = Duration.ofMillis(
- DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_HIDE_RESOLVED_UI_TRANSITION_DELAY_MILLIS,
- 400))
+ get() =
+ Duration.ofMillis(
+ DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_HIDE_RESOLVED_UI_TRANSITION_DELAY_MILLIS,
+ 400
+ )
+ )
private val linearInterpolator = LinearInterpolator()
@@ -207,14 +219,16 @@ class IssueCardAnimator(val callback: AnimationCallback) {
.setDuration(0)
.addTarget(R.id.resolved_issue_image)
- private val showResolvedTextTransition = Fade(Fade.IN)
- .setStartDelay(SHOW_RESOLVED_TEXT_TRANSITION_DELAY.toMillis())
- .setDuration(SHOW_RESOLVED_TEXT_TRANSITION_DURATION.toMillis())
- .addTarget(R.id.resolved_issue_text)
+ private val showResolvedTextTransition =
+ Fade(Fade.IN)
+ .setStartDelay(SHOW_RESOLVED_TEXT_TRANSITION_DELAY.toMillis())
+ .setDuration(SHOW_RESOLVED_TEXT_TRANSITION_DURATION.toMillis())
+ .addTarget(R.id.resolved_issue_text)
private val hideResolvedUiTransition
- get() = Fade(Fade.OUT)
- .setStartDelay(hideResolvedUiTransitionDelay.toMillis())
- .setDuration(HIDE_RESOLVED_UI_TRANSITION_DURATION.toMillis())
+ get() =
+ Fade(Fade.OUT)
+ .setStartDelay(hideResolvedUiTransitionDelay.toMillis())
+ .setDuration(HIDE_RESOLVED_UI_TRANSITION_DURATION.toMillis())
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
index c56cce0a5..7622270b9 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
@@ -30,6 +30,7 @@ import android.os.Bundle;
import android.safetycenter.SafetyCenterIssue;
import android.text.TextUtils;
import android.util.Log;
+import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
@@ -134,9 +135,7 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
configureSafetyProtectionView(holder);
maybeStartResolutionAnimation(holder);
- mSafetyCenterViewModel
- .getInteractionLogger()
- .recordIssueViewed(mIssue, mIsDismissed);
+ mSafetyCenterViewModel.getInteractionLogger().recordIssueViewed(mIssue, mIsDismissed);
}
private void maybeDisplayText(@Nullable CharSequence maybeText, TextView textView) {
@@ -424,8 +423,14 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
ActionButtonBuilder(SafetyCenterIssue.Action action, Context context) {
mAction = action;
mContext = context;
- mContextThemeWrapper =
- new ContextThemeWrapper(context, R.style.Theme_MaterialComponents_DayNight);
+
+ TypedValue buttonThemeValue = new TypedValue();
+ mContext.getTheme()
+ .resolveAttribute(
+ R.attr.scActionButtonTheme,
+ buttonThemeValue,
+ /* resolveRefs= */ false);
+ mContextThemeWrapper = new ContextThemeWrapper(context, buttonThemeValue.data);
}
public ActionButtonBuilder setIndex(int index) {
@@ -540,9 +545,11 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
return;
}
- int margin =
- mContext.getResources()
- .getDimensionPixelSize(R.dimen.sc_action_button_list_margin);
+ int marginRes =
+ mIsLargeScreen
+ ? R.dimen.sc_action_button_list_margin_large_screen
+ : R.dimen.sc_action_button_list_margin;
+ int margin = mContext.getResources().getDimensionPixelSize(marginRes);
Space space = new Space(mContext);
space.setLayoutParams(new ViewGroup.LayoutParams(margin, margin));
buttonList.addView(space);
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt
index 55293f775..9121c76c2 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt
@@ -46,16 +46,19 @@ class MoreIssuesCardAnimator {
super.onAnimationEnd(drawable)
statusIcon.setImageResource(endSeverityLevelResId)
}
- })
+ }
+ )
setStatusIconDrawable.start()
}
}
fun cancelStatusAnimation(statusIcon: ImageView) {
val statusDrawable: Drawable? = statusIcon.drawable
- if (statusDrawable != null &&
- statusDrawable is AnimatedVectorDrawable &&
- statusDrawable.isRunning) {
+ if (
+ statusDrawable != null &&
+ statusDrawable is AnimatedVectorDrawable &&
+ statusDrawable.isRunning
+ ) {
statusDrawable.clearAnimationCallbacks()
statusDrawable.stop()
}
@@ -106,7 +109,10 @@ class MoreIssuesCardAnimator {
Log.e(
MoreIssuesCardPreference.TAG,
String.format(
- "Unexpected SafetyCenterIssue.IssueSeverityLevel: %d", endSeverityLevel))
+ "Unexpected SafetyCenterIssue.IssueSeverityLevel: %d",
+ endSeverityLevel
+ )
+ )
R.drawable.ic_safety_null_state
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ParsedSafetyCenterIntent.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ParsedSafetyCenterIntent.kt
index 6bf4b52e8..377c8d5f5 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ParsedSafetyCenterIntent.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ParsedSafetyCenterIntent.kt
@@ -41,7 +41,10 @@ data class ParsedSafetyCenterIntent(
getParcelableExtra(EXTRA_SAFETY_SOURCE_USER_HANDLE, UserHandle::class.java)
val safetyCenterIssueKey: SafetyCenterIssueKey? =
createSafetyCenterIssueKey(
- safetySourceId, safetySourceIssueId, safetySourceUserHandle)
+ safetySourceId,
+ safetySourceIssueId,
+ safetySourceUserHandle
+ )
// Check if we've navigated from QS or if focusing on single issue and issues should be
// expanded
@@ -56,8 +59,8 @@ data class ParsedSafetyCenterIntent(
*
* @param safetySourceId source ID for a {@link SafetySourceIssue}
* @param safetySourceIssueId an issue ID for a {@link SafetySourceIssue}
- * @param safetySourceUserHandle the specific a {@link android.os.UserHandle} associated with
- * issue
+ * @param safetySourceUserHandle the specific a {@link android.os.UserHandle} associated
+ * with issue
*/
private fun createSafetyCenterIssueKey(
safetySourceId: String?,
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt
index 01e8f8d15..b1c48befd 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt
@@ -51,10 +51,12 @@ internal enum class PositionInCardList(val backgroundDrawableResId: Int) {
fun getTopMargin(context: Context): Int =
when (this) {
- CARD_START, CARD_START_END, CARD_START_LIST_END ->
- context.resources.getDimensionPixelSize(R.dimen.sc_card_margin)
- LIST_START, LIST_START_CARD_END, LIST_START_END ->
- context.resources.getDimensionPixelSize(R.dimen.sc_list_margin_top)
+ CARD_START,
+ CARD_START_END,
+ CARD_START_LIST_END -> context.resources.getDimensionPixelSize(R.dimen.sc_card_margin)
+ LIST_START,
+ LIST_START_CARD_END,
+ LIST_START_END -> context.resources.getDimensionPixelSize(R.dimen.sc_list_margin_top)
else -> 0
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt
index 2acd6b5a3..613493f82 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt
@@ -59,7 +59,7 @@ internal class PreferenceHighlightManager(private val fragment: PreferenceFragme
/** Creates a new [HighlightablePreferenceGroupAdapter] instance */
fun createAdapter(
- preferenceScreen: PreferenceScreen?,
+ preferenceScreen: PreferenceScreen,
): RecyclerView.Adapter<RecyclerView.ViewHolder> {
val intent = fragment.getActivity()?.getIntent()
preferenceGroupAdapter =
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt
index 54ba9560f..9fb17ce03 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt
@@ -114,7 +114,8 @@ class PrivacySubpageFragment : SafetyCenterFragment() {
subpageIssues,
subpageDismissedIssues,
uiData.resolvedIssues,
- requireActivity().getTaskId())
+ requireActivity().getTaskId()
+ )
}
private fun updateSafetyCenterEntries(entryGroup: SafetyCenterEntryGroup) {
@@ -130,9 +131,13 @@ class PrivacySubpageFragment : SafetyCenterFragment() {
SafetySubpageEntryPreference(
requireContext(),
PendingIntentSender.getTaskIdForEntry(
- entryId, sameTaskSourceIds, requireActivity()),
+ entryId,
+ sameTaskSourceIds,
+ requireActivity()
+ ),
entry,
- safetyCenterViewModel)
+ safetyCenterViewModel
+ )
if (sourceId == "AndroidPrivacyControls") {
// No action required here because the privacy controls are rendered separately
@@ -149,7 +154,11 @@ class PrivacySubpageFragment : SafetyCenterFragment() {
fun setSwitchPreference(prefType: Pref) {
val switchPreference: ClickableDisabledSwitchPreference? = findPreference(prefType.key)
switchPreference?.setupState(
- prefStates[prefType], prefType, privacyControlsViewModel, this)
+ prefStates[prefType],
+ prefType,
+ privacyControlsViewModel,
+ this
+ )
}
setSwitchPreference(Pref.MIC)
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt
index bf2d0565c..57e4175ca 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt
@@ -42,7 +42,7 @@ internal class SafetyBrandChipPreference(context: Context, attrs: AttributeSet)
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
- val brandChipButton = holder.findViewById(R.id.brand_chip)
+ val brandChipButton = holder.findViewById(R.id.brand_chip)!!
brandChipButton.setOnClickListener(brandChipClickListener)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
index 8f31da828..5a5245068 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
@@ -30,12 +30,14 @@ import static com.android.permissioncontroller.safetycenter.SafetyCenterConstant
import android.app.ActionBar;
import android.content.Intent;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.provider.Settings;
import android.safetycenter.SafetyCenterManager;
import android.safetycenter.config.SafetyCenterConfig;
import android.safetycenter.config.SafetySource;
import android.safetycenter.config.SafetySourcesGroup;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -107,7 +109,30 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
.commitNow();
}
+ configureHomeButton();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ maybeRedirectIfDisabled();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // We don't set configChanges, but small screen size changes may still be delivered here.
+ super.onConfigurationChanged(newConfig);
+ configureHomeButton();
+ }
+
+ /** Decide whether a home/back button should be shown or not. */
+ private void configureHomeButton() {
ActionBar actionBar = getActionBar();
+ Fragment frag = getSupportFragmentManager().findFragmentById(R.id.content_frame);
+ if (actionBar == null || frag == null) {
+ return;
+ }
+
// Only the homepage can be considered a "second layer" page as it's the only one that
// can be reached from the Settings menu. The other pages are only reachable using
// a direct intent (e.g. notification, "first layer") and/or by navigating within Safety
@@ -115,30 +140,33 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
// Note that the homepage can also be a "first layer" page, but that would only happen
// if the activity is not embedded.
boolean isSecondLayerPage = frag instanceof SafetyCenterScrollWrapperFragment;
- if (actionBar != null
- && ActivityEmbeddingUtils.shouldHideNavigateUpButton(this, isSecondLayerPage)) {
+ if (ActivityEmbeddingUtils.shouldHideNavigateUpButton(this, isSecondLayerPage)) {
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setHomeButtonEnabled(false);
}
}
- @Override
- protected void onStart() {
- super.onStart();
- maybeRedirectIfDisabled();
- }
-
private boolean maybeRedirectIfDisabled() {
if (mSafetyCenterManager == null || !mSafetyCenterManager.isSafetyCenterEnabled()) {
Log.w(TAG, "Safety Center disabled, redirecting to settings page");
startActivity(
- new Intent(Settings.ACTION_SETTINGS).addFlags(FLAG_ACTIVITY_FORWARD_RESULT));
+ new Intent(getActionToRedirectWhenDisabled())
+ .addFlags(FLAG_ACTIVITY_FORWARD_RESULT));
finish();
return true;
}
return false;
}
+ private String getActionToRedirectWhenDisabled() {
+ boolean isPrivacyControls =
+ TextUtils.equals(getIntent().getAction(), PRIVACY_CONTROLS_ACTION);
+ if (isPrivacyControls) {
+ return Settings.ACTION_PRIVACY_SETTINGS;
+ }
+ return Settings.ACTION_SETTINGS;
+ }
+
private boolean maybeRedirectIntoTwoPaneSettings() {
return shouldUseTwoPaneSettings() && tryRedirectTwoPaneSettings();
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
index 940cb2f69..f6d653c99 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
@@ -38,18 +38,22 @@ import android.safetycenter.SafetyCenterIssue;
import android.safetycenter.SafetyCenterStaticEntry;
import android.safetycenter.SafetyCenterStaticEntryGroup;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
+import androidx.recyclerview.widget.RecyclerView;
import com.android.permissioncontroller.R;
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.SafetyCenterResourcesContext;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import kotlin.Unit;
@@ -113,6 +117,7 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
mIssuesGroup = getPreferenceScreen().findPreference(ISSUES_GROUP_KEY);
mEntriesGroup = getPreferenceScreen().findPreference(ENTRIES_GROUP_KEY);
mStaticEntriesGroup = getPreferenceScreen().findPreference(STATIC_ENTRIES_GROUP_KEY);
+
if (mIsQuickSettingsFragment) {
getPreferenceScreen().removePreference(mEntriesGroup);
mEntriesGroup = null;
@@ -126,6 +131,19 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
prerenderCurrentSafetyCenterData();
}
+ @Override
+ public RecyclerView onCreateRecyclerView(
+ LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+ RecyclerView recyclerView =
+ super.onCreateRecyclerView(inflater, parent, savedInstanceState);
+
+ if (mIsQuickSettingsFragment) {
+ recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
+ recyclerView.setVerticalScrollBarEnabled(false);
+ }
+ return recyclerView;
+ }
+
// Set the default divider line between preferences to be transparent
@Override
public void setDivider(Drawable divider) {
@@ -162,10 +180,10 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
private void updateStatus(StatusUiData statusUiData) {
if (mIsQuickSettingsFragment) {
- SafetyCenterResourcesContext safetyCenterResourcesContext =
- new SafetyCenterResourcesContext(requireContext());
+ SafetyCenterResourcesApk safetyCenterResourcesApk =
+ new SafetyCenterResourcesApk(requireContext());
boolean hasPendingActions =
- safetyCenterResourcesContext
+ safetyCenterResourcesApk
.getStringByName("overall_severity_level_ok_review_summary")
.equals(statusUiData.getOriginalSummary().toString());
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
index 7d5dbb3cb..9feecf5d4 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
@@ -32,7 +32,7 @@ import com.android.permissioncontroller.safetycenter.ui.ParsedSafetyCenterIntent
import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterViewModelFactory
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
/** A base fragment that represents a page in Safety Center. */
@RequiresApi(TIRAMISU)
@@ -50,7 +50,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
}
override fun onCreateAdapter(
- preferenceScreen: PreferenceScreen?
+ preferenceScreen: PreferenceScreen
): RecyclerView.Adapter<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
@@ -72,7 +72,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
sameTaskSourceIds =
- SafetyCenterResourcesContext(requireContext())
+ SafetyCenterResourcesApk(requireContext())
.getStringByName("config_same_task_safety_source_ids")
.split(",")
safetyCenterSessionId = requireArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID)
@@ -92,7 +92,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
}
val safetyCenterIntent: ParsedSafetyCenterIntent =
- requireActivity().getIntent().toSafetyCenterIntent()
+ requireActivity().intent.toSafetyCenterIntent()
val isQsFragment =
getArguments()?.getBoolean(QUICK_SETTINGS_SAFETY_CENTER_FRAGMENT, false) ?: false
collapsableIssuesCardHelper =
@@ -120,7 +120,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
override fun onStart() {
super.onStart()
configureInteractionLogger()
- safetyCenterViewModel.interactionLogger.record(Action.SAFETY_CENTER_VIEWED)
+ logSafetyCenterViewedEvent()
}
override fun onResume() {
@@ -162,6 +162,26 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
abstract fun configureInteractionLogger()
+ private fun logSafetyCenterViewedEvent() {
+ // If Safety Center was opened due to an associated notification click (i.e. intent has an
+ // associated issue), record that issue's metadata on the SAFETY_CENTER_VIEWED event
+ val maybeIssueKey = requireActivity().intent.toSafetyCenterIntent().safetyCenterIssueKey
+ val maybeIssue =
+ maybeIssueKey?.let {
+ safetyCenterViewModel.getCurrentSafetyCenterDataAsUiData().getMatchingIssue(it)
+ }
+
+ if (maybeIssue == null) {
+ safetyCenterViewModel.interactionLogger.record(Action.SAFETY_CENTER_VIEWED)
+ } else {
+ safetyCenterViewModel.interactionLogger.recordForIssue(
+ Action.SAFETY_CENTER_VIEWED,
+ maybeIssue,
+ isDismissed = false
+ )
+ }
+ }
+
private fun displayErrorDetails(errorDetails: SafetyCenterErrorDetails?) {
if (errorDetails == null) return
Toast.makeText(requireContext(), errorDetails.errorMessage, Toast.LENGTH_LONG).show()
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java
index f69746e39..8804b3c29 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java
@@ -149,6 +149,7 @@ public class SafetyCenterQsFragment extends Fragment {
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.safety_center_qs, container, false);
root.setVisibility(View.GONE);
+ root.setOverScrollMode(View.OVER_SCROLL_IF_CONTENT_SCROLLS);
View closeButton = root.findViewById(R.id.close_button);
closeButton.setOnClickListener((v) -> requireActivity().finish());
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
index 5e45d2b3c..02bf6278e 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
@@ -26,7 +26,7 @@ import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
import com.android.permissioncontroller.R
import com.android.permissioncontroller.safetycenter.ui.SafetyBrandChipPreference.Companion.closeSubpage
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
import com.android.settingslib.widget.FooterPreference
/** A fragment that represents a generic subpage in Safety Center. */
@@ -88,8 +88,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
private fun setupIllustration() {
val resName = "illustration_${SnakeCaseConverter.fromCamelCase(sourceGroupId)}"
val context = requireContext()
- val drawable =
- SafetyCenterResourcesContext(context).getDrawableByName(resName, context.theme)
+ val drawable = SafetyCenterResourcesApk(context).getDrawableByName(resName, context.theme)
if (drawable == null) {
Log.w(TAG, "$sourceGroupId doesn't have any matching illustration")
subpageIllustration.setVisible(false)
@@ -100,7 +99,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
private fun setupFooter() {
val resName = "${SnakeCaseConverter.fromCamelCase(sourceGroupId)}_footer"
- val footerText = SafetyCenterResourcesContext(requireContext()).getStringByName(resName)
+ val footerText = SafetyCenterResourcesApk(requireContext()).getStringByName(resName)
if (footerText.isEmpty()) {
Log.w(TAG, "$sourceGroupId doesn't have any matching footer")
subpageFooter.setVisible(false)
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterTouchTarget.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterTouchTarget.kt
index 0e368291e..01d23241f 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterTouchTarget.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterTouchTarget.kt
@@ -28,6 +28,7 @@ import androidx.annotation.RequiresApi
object SafetyCenterTouchTarget {
/**
* Resizes the touch target of views by delegating to the parent component.
+ *
* @param view component that will be expanded
* @param minTouchTargetSizeResource required minimum touch target size
*/
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt
index 2a9f6f780..46590448e 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt
@@ -45,10 +45,10 @@ class SafetyGroupPreference(
layoutResource = R.layout.preference_group
}
- override fun onBindViewHolder(holder: PreferenceViewHolder?) {
+ override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
- (holder?.itemView as? SafetyEntryGroupView)?.showGroup(
+ (holder.itemView as? SafetyEntryGroupView)?.showGroup(
group,
isExpanded,
isFirstCard,
@@ -56,7 +56,8 @@ class SafetyGroupPreference(
getTaskIdForEntry,
viewModel,
onExpandedListener,
- onCollapsedListener)
+ onCollapsedListener
+ )
}
override fun isSameItem(preference: Preference): Boolean =
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
index 0b8706a38..811841845 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
@@ -27,6 +27,7 @@ import android.os.Looper;
import android.safetycenter.SafetyCenterStatus;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
@@ -49,6 +50,8 @@ import java.util.Objects;
@RequiresApi(TIRAMISU)
public class SafetyStatusPreference extends Preference implements ComparablePreference {
+ private static final String TAG = "SafetyStatusPreference";
+
@Nullable private StatusUiData mStatus;
@Nullable private SafetyCenterViewModel mViewModel;
@@ -72,6 +75,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
+ Log.v(TAG, String.format("onBindViewHolder called for status %s", mStatus));
if (mStatus == null) {
return;
@@ -140,12 +144,14 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
if (mIsTextChangeAnimationRunning) {
return;
}
+ Log.v(TAG, "Starting status text animation");
String titleText = mStatus.getTitle().toString();
String summaryText = mStatus.getSummary(getContext()).toString();
boolean titleEquals = titleView.getText().toString().equals(titleText);
boolean summaryEquals = summaryView.getText().toString().equals(summaryText);
Runnable onFinish =
() -> {
+ Log.v(TAG, "Finishing status text animation");
mIsTextChangeAnimationRunning = false;
runTextAnimationIfNeeded(titleView, summaryView);
};
@@ -334,7 +340,12 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
}
void setData(StatusUiData statusUiData) {
+ if (Objects.equals(mStatus, statusUiData)) {
+ return;
+ }
+
mStatus = statusUiData;
+ Log.v(TAG, String.format("setData called for status %s", mStatus));
safeNotifyChanged();
}
@@ -349,7 +360,14 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
// Calling notifyChanged while recyclerview is scrolling or computing layout will result in an
// IllegalStateException. Post to handler to wait for UI to settle.
private void safeNotifyChanged() {
- new Handler(Looper.getMainLooper()).post(this::notifyChanged);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ Log.v(
+ TAG,
+ String.format("Calling notifyChanged for status %s", mStatus));
+ notifyChanged();
+ });
}
@Override
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
index bb09783be..a5356962a 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
@@ -62,21 +62,24 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) :
// we should ensure we won't add multiple listeners to the same view,
// and Preferences API does not allow to do cleanups when onViewRecycled,
// so we are keeping a track of the added listener attaching it as a tag to the View
- val listener: View.OnLayoutChangeListener = spacer.tag as? View.OnLayoutChangeListener
- ?: object : View.OnLayoutChangeListener {
- override fun onLayoutChange(
- v: View?,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- adjustHeight(spacer)
- }}.also { spacer.tag = it }
+ val listener: View.OnLayoutChangeListener =
+ spacer.tag as? View.OnLayoutChangeListener
+ ?: object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ v: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ adjustHeight(spacer)
+ }
+ }
+ .also { spacer.tag = it }
spacer.removeOnLayoutChangeListener(listener)
spacer.addOnLayoutChangeListener(listener)
@@ -96,12 +99,13 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) :
// differently due to the auto-scroll to highlight a specific item,
// and in this case we need to wait the content parent to be measured
if (contentParent.height == 0) {
- val globalLayoutObserver = object : ViewTreeObserver.OnGlobalLayoutListener {
- override fun onGlobalLayout() {
- contentParent.viewTreeObserver.removeOnGlobalLayoutListener(this)
- adjustHeight(spacer)
+ val globalLayoutObserver =
+ object : ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ contentParent.viewTreeObserver.removeOnGlobalLayoutListener(this)
+ adjustHeight(spacer)
+ }
}
- }
contentParent.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutObserver)
return
}
@@ -110,13 +114,14 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) :
maxKnownToolbarHeight = max(maxKnownToolbarHeight, collapsingToolbar.height)
val contentHeight = spacer.top + maxKnownToolbarHeight
- val desiredSpacerHeight = if (contentHeight > contentParent.height) {
- // making it 0 height will remove if from recyclerview
- 1
- } else {
- // to unlock the scrolling we need spacer to go slightly beyond the screen
- contentParent.height - contentHeight + 1
- }
+ val desiredSpacerHeight =
+ if (contentHeight > contentParent.height) {
+ // making it 0 height will remove if from recyclerview
+ 1
+ } else {
+ // to unlock the scrolling we need spacer to go slightly beyond the screen
+ contentParent.height - contentHeight + 1
+ }
val layoutParams = spacer.layoutParams
if (layoutParams.height != desiredSpacerHeight) {
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt
index 55288564c..8409ca75d 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt
@@ -129,21 +129,24 @@ object StatusAnimationResolver {
@JvmStatic
fun getStatusChangeAnimation(fromSeverity: Int, toSeverity: Int): Int =
- if (fromSeverity == toSeverity &&
- fromSeverity != SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK) {
+ if (
+ fromSeverity == toSeverity &&
+ fromSeverity != SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK
+ ) {
0
- } else when (fromSeverity) {
- SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK ->
- R.drawable.safety_status_info_to_info_anim
- SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION ->
- R.drawable.safety_status_recommend_to_info_anim
- SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING -> {
- if (toSeverity == SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK) {
- R.drawable.safety_status_warn_to_info_anim
- } else {
- R.drawable.safety_status_warn_to_recommend_anim
+ } else
+ when (fromSeverity) {
+ SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK ->
+ R.drawable.safety_status_info_to_info_anim
+ SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION ->
+ R.drawable.safety_status_recommend_to_info_anim
+ SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING -> {
+ if (toSeverity == SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK) {
+ R.drawable.safety_status_warn_to_info_anim
+ } else {
+ R.drawable.safety_status_warn_to_recommend_anim
+ }
}
+ else -> 0
}
- else -> 0
- }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/TextFadeAnimator.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/TextFadeAnimator.kt
index a98fcf2ad..48acad438 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/TextFadeAnimator.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/TextFadeAnimator.kt
@@ -21,6 +21,7 @@ import android.transition.Transition
import android.transition.TransitionListenerAdapter
import android.transition.TransitionManager
import android.transition.TransitionSet
+import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
@@ -64,6 +65,9 @@ constructor(targetIds: List<Int>, changeDuration: Duration = DEFAULT_TEXT_CHANGE
if (textChanges.isEmpty()) {
return
}
+
+ Log.v(TAG, "Starting text animation")
+
val firstView = textChanges[0].first
val parentViewGroup: ViewGroup = firstView.parent as ViewGroup
val fadeOutTransition =
@@ -71,16 +75,14 @@ constructor(targetIds: List<Int>, changeDuration: Duration = DEFAULT_TEXT_CHANGE
.clone()
.addListener(
object : TransitionListenerAdapter() {
- override fun onTransitionStart(transition: Transition?) {
- super.onTransitionStart(transition)
- }
override fun onTransitionEnd(transition: Transition?) {
- super.onTransitionEnd(transition)
fadeTextIn(textChanges, parentViewGroup, onFinish)
}
- })
+ }
+ )
parentViewGroup.post {
TransitionManager.beginDelayedTransition(parentViewGroup, fadeOutTransition)
+ Log.v(TAG, "Starting text fade-out transition")
for ((textView, _) in textChanges) {
textView.visibility = View.INVISIBLE
}
@@ -98,13 +100,15 @@ constructor(targetIds: List<Int>, changeDuration: Duration = DEFAULT_TEXT_CHANGE
.addListener(
object : TransitionListenerAdapter() {
override fun onTransitionEnd(transition: Transition?) {
- super.onTransitionEnd(transition)
+ Log.v(TAG, String.format("Finishing text animation"))
onFinish?.run()
}
- })
+ }
+ )
parent.post {
TransitionManager.beginDelayedTransition(parent, fadeInTransition)
+ Log.v(TAG, "Starting text fade-in transition")
for ((textView, text) in textChanges) {
textView.text = text
textView.visibility = View.VISIBLE
@@ -117,6 +121,7 @@ constructor(targetIds: List<Int>, changeDuration: Duration = DEFAULT_TEXT_CHANGE
}
companion object {
+ private const val TAG = "TextFadeAnimator"
// Duration is for fade-out & fade-in individually, not combined
private val DEFAULT_TEXT_CHANGE_DURATION = Duration.ofMillis(167)
private val linearInterpolator = LinearInterpolator()
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
index 4714d42cb..4ddcf1c3d 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
@@ -33,9 +33,9 @@ import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.getMainExecutor
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.map
import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.safetycenter.ui.InteractionLogger
import com.android.permissioncontroller.safetycenter.ui.NavigationSource
@@ -47,7 +47,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
private val TAG: String = LiveSafetyCenterViewModel::class.java.simpleName
override val statusUiLiveData: LiveData<StatusUiData>
- get() = Transformations.map(safetyCenterUiLiveData) { StatusUiData(it.safetyCenterData) }
+ get() = safetyCenterUiLiveData.map { StatusUiData(it.safetyCenterData) }
override val safetyCenterUiLiveData: LiveData<SafetyCenterUiData> by this::_safetyCenterLiveData
override val errorLiveData: LiveData<SafetyCenterErrorDetails> by this::_errorLiveData
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt
index 73c7da99f..b1ac62874 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt
@@ -83,7 +83,10 @@ class SafetyCenterQsViewModel(
val userHandle = UserHandle.getUserHandleForUid(permGroupUsage.uid)
val lightAppPermissionGroupUsageKey =
LightAppPermissionGroupUsageKey(
- packageName, permissionGroupName, userHandle)
+ packageName,
+ permissionGroupName,
+ userHandle
+ )
val appPermGroupLiveData: LightAppPermGroupLiveData =
LightAppPermGroupLiveData[
Triple(packageName, permissionGroupName, userHandle)]
@@ -110,7 +113,8 @@ class SafetyCenterQsViewModel(
LightAppPermissionGroupUsageKey(
usage.packageName,
usage.permissionGroupName,
- UserHandle.getUserHandleForUid(usage.uid))]
+ UserHandle.getUserHandleForUid(usage.uid)
+ )]
?: return false
return group.supportsRuntimePerms &&
!group.hasInstallToRuntimeSplit &&
@@ -125,7 +129,8 @@ class SafetyCenterQsViewModel(
LightAppPermissionGroupUsageKey(
usage.packageName,
usage.permissionGroupName,
- UserHandle.getUserHandleForUid(usage.uid))]
+ UserHandle.getUserHandleForUid(usage.uid)
+ )]
?: return
KotlinUtils.revokeForegroundRuntimePermissions(app, group)
@@ -177,13 +182,16 @@ class SafetyCenterQsViewModel(
getSensorState(
Sensors.CAMERA,
UserManager.DISALLOW_CAMERA_TOGGLE,
- configCameraToggleEnabled),
+ configCameraToggleEnabled
+ ),
MICROPHONE to
getSensorState(
Sensors.MICROPHONE,
UserManager.DISALLOW_MICROPHONE_TOGGLE,
- configMicToggleEnabled),
- LOCATION to SensorState(true, locationEnabled, locationEnforcedAdmin))
+ configMicToggleEnabled
+ ),
+ LOCATION to SensorState(true, locationEnabled, locationEnforcedAdmin)
+ )
}
@Suppress("OVERRIDE_DEPRECATION")
@@ -221,13 +229,14 @@ class SafetyCenterQsViewModel(
return SensorState(
sensorConfigEnabled && sensorPrivacyManager.supportsSensorToggle(sensor),
!sensorPrivacyManager.isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor),
- getEnforcedAdmin(restriction))
+ getEnforcedAdmin(restriction)
+ )
}
private fun getEnforcedAdmin(restriction: String) =
- if (userManager
- .getUserRestrictionSources(restriction, Process.myUserHandle())
- .isNotEmpty()) {
+ if (
+ userManager.getUserRestrictionSources(restriction, Process.myUserHandle()).isNotEmpty()
+ ) {
RestrictedLockUtils.getProfileOrDeviceOwner(app, Process.myUserHandle())
} else {
null
@@ -250,10 +259,12 @@ class SafetyCenterQsViewModel(
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val resolveInfo: ResolveInfo? =
context.packageManager.resolveActivity(intent, PackageManager.ResolveInfoFlags.of(0))
- if (resolveInfo != null &&
- resolveInfo.activityInfo != null &&
- resolveInfo.activityInfo.permission ==
- android.Manifest.permission.START_VIEW_PERMISSION_USAGE) {
+ if (
+ resolveInfo != null &&
+ resolveInfo.activityInfo != null &&
+ resolveInfo.activityInfo.permission ==
+ android.Manifest.permission.START_VIEW_PERMISSION_USAGE
+ ) {
intent.component = ComponentName(usage.packageName, resolveInfo.activityInfo.name)
return intent
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt
index 39241ff9a..69a315f08 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt
@@ -16,19 +16,30 @@
package com.android.permissioncontroller.safetycenter.ui.model
+import android.os.Build.VERSION_CODES.TIRAMISU
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.safetycenter.SafetyCenterData
import android.safetycenter.SafetyCenterEntryGroup
import android.safetycenter.SafetyCenterEntryOrGroup
import android.safetycenter.SafetyCenterIssue
+import android.safetycenter.SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK
import androidx.annotation.RequiresApi
import com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY
+import com.android.safetycenter.internaldata.SafetyCenterIds
+import com.android.safetycenter.internaldata.SafetyCenterIssueKey
/** UI model representation of Safety Center Data */
data class SafetyCenterUiData(
val safetyCenterData: SafetyCenterData,
val resolvedIssues: Map<IssueId, ActionId> = emptyMap()
) {
+ @RequiresApi(TIRAMISU)
+ fun getMatchingIssue(issueKey: SafetyCenterIssueKey): SafetyCenterIssue? {
+ return safetyCenterData.issues.find {
+ SafetyCenterIds.issueIdFromString(it.id).safetyCenterIssueKey == issueKey
+ }
+ }
+
/** Returns the [SafetyCenterEntryGroup] corresponding to the provided ID */
@RequiresApi(UPSIDE_DOWN_CAKE)
fun getMatchingGroup(groupId: String): SafetyCenterEntryGroup? {
@@ -51,7 +62,7 @@ data class SafetyCenterUiData(
*/
@RequiresApi(UPSIDE_DOWN_CAKE)
fun getMatchingDismissedIssues(groupId: String): List<SafetyCenterIssue> =
- selectMatchingIssuesForGroup(groupId, safetyCenterData.dismissedIssues)
+ selectMatchingIssuesForGroup(groupId, safetyCenterData.visibleDismissedIssues())
@RequiresApi(UPSIDE_DOWN_CAKE)
private fun selectMatchingIssuesForGroup(
@@ -68,4 +79,9 @@ data class SafetyCenterUiData(
if (mappingExists) matchesInMapping else matchesByDefault
}
}
+
+ /** Returns the [SafetyCenterData.getDismissedIssues] that are meant to be visible in the UI. */
+ @RequiresApi(UPSIDE_DOWN_CAKE)
+ fun SafetyCenterData.visibleDismissedIssues() =
+ dismissedIssues.filter { it.severityLevel > ISSUE_SEVERITY_LEVEL_OK }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/LazyProperties.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/LazyProperties.kt
new file mode 100644
index 000000000..542ab1e03
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/LazyProperties.kt
@@ -0,0 +1,12 @@
+package com.android.permissioncontroller.safetycenter.ui.view
+
+import android.view.View
+
+/** Returns a lazy property wrapping a view with a given ID. */
+fun <T> View.lazyView(childViewId: Int): Lazy<T> = lazyView { findViewById(childViewId) }
+
+/** Returns a lazy property wrapping a view produced by the given function. */
+fun <T> lazyView(viewProducer: () -> T): Lazy<T> =
+ // Lazy by default uses synchronization to ensure a variable is only initialized once. This
+ // is unnecessary and expensive for view properties, so we don't use synchronization here.
+ lazy(LazyThreadSafetyMode.NONE) { viewProducer() }
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/MoreIssuesHeaderView.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/MoreIssuesHeaderView.kt
index 264aa488e..ebfbef714 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/MoreIssuesHeaderView.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/MoreIssuesHeaderView.kt
@@ -43,15 +43,11 @@ constructor(
}
private val moreIssuesCardAnimator = MoreIssuesCardAnimator()
- private val statusIconView: ImageView by lazy { findViewById(R.id.status_icon) }
- private val titleView: TextView by lazy { findViewById(R.id.title) }
- private val expandCollapseLayout: View by lazy { findViewById(android.R.id.widget_frame) }
- private val counterView: TextView by lazy {
- expandCollapseLayout.findViewById(R.id.widget_title)
- }
- private val expandCollapseIcon: ImageView by lazy {
- expandCollapseLayout.findViewById(R.id.widget_icon)
- }
+ private val statusIconView: ImageView by lazyView(R.id.status_icon)
+ private val titleView: TextView by lazyView(R.id.title)
+ private val expandCollapseLayout: View by lazyView(R.id.widget_frame)
+ private val counterView: TextView by lazyView(R.id.widget_title)
+ private val expandCollapseIcon: ImageView by lazyView(R.id.widget_icon)
private var cornerAnimator: ValueAnimator? = null
fun showExpandableHeader(
@@ -168,6 +164,7 @@ constructor(
if (animateTextChange) {
counterView.text = previousText
+ Log.v(TAG, "Starting more issues card text animation")
moreIssuesCardAnimator.animateChangeText(counterView, newText)
} else {
counterView.text = newText
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryCommonViewsManager.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryCommonViewsManager.kt
index 4be327285..34565421a 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryCommonViewsManager.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryCommonViewsManager.kt
@@ -29,11 +29,11 @@ import com.android.permissioncontroller.safetycenter.ui.SeverityIconPicker
internal class SafetyEntryCommonViewsManager(rootEntryView: ViewGroup?) {
- val titleView: TextView? by lazy { rootEntryView?.findViewById(R.id.title) }
- val summaryView: TextView? by lazy { rootEntryView?.findViewById(R.id.summary) }
- private val iconView: ImageView? by lazy { rootEntryView?.findViewById(R.id.icon) }
- private val iconFrame: View? by lazy { rootEntryView?.findViewById(R.id.icon_frame) }
- private val emptySpace: View? by lazy { rootEntryView?.findViewById(R.id.empty_space) }
+ val titleView: TextView? by lazyView { rootEntryView?.findViewById(R.id.title) }
+ val summaryView: TextView? by lazyView { rootEntryView?.findViewById(R.id.summary) }
+ private val iconView: ImageView? by lazyView { rootEntryView?.findViewById(R.id.icon) }
+ private val iconFrame: View? by lazyView { rootEntryView?.findViewById(R.id.icon_frame) }
+ private val emptySpace: View? by lazyView { rootEntryView?.findViewById(R.id.empty_space) }
fun showDetails(
id: String,
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryGroupView.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryGroupView.kt
index 318ade5cb..b00b7f765 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryGroupView.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryGroupView.kt
@@ -51,7 +51,6 @@ constructor(
) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
private companion object {
- val TAG = SafetyEntryGroupView::class.java.simpleName
const val EXPAND_COLLAPSE_ANIMATION_DURATION_MS = 183L
}
@@ -59,20 +58,20 @@ constructor(
inflate(context, R.layout.safety_center_group, this)
}
- private val groupHeaderView: LinearLayout? by lazy { findViewById(R.id.group_header) }
+ private val groupHeaderView: LinearLayout? by lazyView(R.id.group_header)
- private val expandedHeaderView: ViewGroup? by lazy { findViewById(R.id.expanded_header) }
- private val expandedTitleView: TextView? by lazy {
+ private val expandedHeaderView: ViewGroup? by lazyView(R.id.expanded_header)
+ private val expandedTitleView: TextView? by lazyView {
expandedHeaderView?.findViewById(R.id.title)
}
- private val collapsedHeaderView: ViewGroup? by lazy { findViewById(R.id.collapsed_header) }
- private val commonEntryView: SafetyEntryCommonViewsManager? by lazy {
+ private val collapsedHeaderView: ViewGroup? by lazyView(R.id.collapsed_header)
+ private val commonEntryView: SafetyEntryCommonViewsManager? by lazyView {
SafetyEntryCommonViewsManager(collapsedHeaderView)
}
- private val chevronIconView: ImageView? by lazy { findViewById(R.id.chevron_icon) }
- private val entriesContainerView: LinearLayout? by lazy { findViewById(R.id.entries_container) }
+ private val chevronIconView: ImageView? by lazyView(R.id.chevron_icon)
+ private val entriesContainerView: LinearLayout? by lazyView(R.id.entries_container)
private var isExpanded: Boolean? = null
@@ -107,8 +106,15 @@ constructor(
val params = layoutParams as MarginLayoutParams
if (params.topMargin != topMargin) {
params.topMargin = topMargin
- layoutParams = params
}
+
+ if (isLastCard) {
+ params.bottomMargin = context.resources.getDimensionPixelSize(R.dimen.sc_spacing_large)
+ } else {
+ params.bottomMargin = 0
+ }
+
+ layoutParams = params
}
private fun showGroupDetails(group: SafetyCenterEntryGroup) {
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryView.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryView.kt
index ff7233686..7d7214ab0 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryView.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryView.kt
@@ -52,10 +52,10 @@ constructor(
inflate(context, R.layout.view_entry, this)
}
- private val commonEntryView: SafetyEntryCommonViewsManager? by lazy {
+ private val commonEntryView: SafetyEntryCommonViewsManager? by lazyView {
SafetyEntryCommonViewsManager(this)
}
- private val widgetFrame: ViewGroup? by lazy { findViewById(R.id.widget_frame) }
+ private val widgetFrame: ViewGroup? by lazyView(R.id.widget_frame)
fun showEntry(
entry: SafetyCenterEntry,
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt
index 013f32c85..6a415c563 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt
@@ -42,14 +42,12 @@ constructor(
inflate(context, R.layout.view_status_card, this)
}
- val statusImageView: ImageView by lazy { findViewById(R.id.status_image) }
- val titleAndSummaryContainerView: LinearLayout by lazy {
- findViewById(R.id.status_title_and_summary)
- }
- val titleView: TextView by lazy { findViewById(R.id.status_title) }
- val summaryView: TextView by lazy { findViewById(R.id.status_summary) }
- val reviewSettingsButton: MaterialButton by lazy { findViewById(R.id.review_settings_button) }
- val rescanButton: MaterialButton by lazy { findViewById(R.id.rescan_button) }
+ val statusImageView: ImageView by lazyView(R.id.status_image)
+ val titleAndSummaryContainerView: LinearLayout by lazyView(R.id.status_title_and_summary)
+ val titleView: TextView by lazyView(R.id.status_title)
+ val summaryView: TextView by lazyView(R.id.status_summary)
+ val reviewSettingsButton: MaterialButton by lazyView(R.id.review_settings_button)
+ val rescanButton: MaterialButton by lazyView(R.id.rescan_button)
fun showButtons(statusUiData: StatusUiData) {
rescanButton.isEnabled = !statusUiData.isRefreshInProgress
diff --git a/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistory.kt b/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistory.kt
index 26f327e96..69e696695 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistory.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistory.kt
@@ -59,7 +59,8 @@ data class AppsSafetyLabelHistory(val appSafetyLabelHistories: List<AppSafetyLab
.toMutableList()
.apply { add(safetyLabel) }
.sortedBy { it.receivedAt }
- .takeLast(maxToPersist))
+ .takeLast(maxToPersist)
+ )
}
/** Data class representing the information about an app. */
@@ -90,7 +91,8 @@ data class AppsSafetyLabelHistory(val appSafetyLabelHistories: List<AppSafetyLab
SafetyLabel(
AppInfo(packageName),
receivedAt,
- DataLabel.extractLocationSharingDataLabel(appMetadataSafetyLabel.dataLabel))
+ DataLabel.extractLocationSharingDataLabel(appMetadataSafetyLabel.dataLabel)
+ )
}
}
@@ -113,7 +115,8 @@ data class AppsSafetyLabelHistory(val appSafetyLabelHistories: List<AppSafetyLab
.filter { it.key == DataCategoryConstants.CATEGORY_LOCATION }
.mapValues { categoryEntry ->
DataCategory.fromAppMetadataDataCategory(categoryEntry.value)
- })
+ }
+ )
}
}
@@ -134,7 +137,8 @@ data class AppsSafetyLabelHistory(val appSafetyLabelHistories: List<AppSafetyLab
DataCategory(
appMetadataDataCategory.dataTypes.values.any {
it.purposeSet.contains(PURPOSE_ADVERTISING)
- })
+ }
+ )
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistoryPersistence.kt b/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistoryPersistence.kt
index 9bb7e819f..ce6233fdf 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistoryPersistence.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistoryPersistence.kt
@@ -90,10 +90,14 @@ object AppsSafetyLabelHistoryPersistence {
Log.e(LOG_TAG, "File not found: $file")
} catch (e: IOException) {
Log.e(
- LOG_TAG, "Failed to read file: $file, encountered exception ${e.localizedMessage}")
+ LOG_TAG,
+ "Failed to read file: $file, encountered exception ${e.localizedMessage}"
+ )
} catch (e: XmlPullParserException) {
Log.e(
- LOG_TAG, "Failed to parse file: $file, encountered exception ${e.localizedMessage}")
+ LOG_TAG,
+ "Failed to parse file: $file, encountered exception ${e.localizedMessage}"
+ )
}
return AppsSafetyLabelHistoryFileContent(appsSafetyLabelHistory = null, INITIAL_VERSION)
@@ -133,13 +137,15 @@ object AppsSafetyLabelHistoryPersistence {
AppsSafetyLabelHistory(
currentHistories.toMutableList().apply {
add(AppSafetyLabelHistory(appInfo, listOf(safetyLabel)))
- })
+ }
+ )
} else {
AppsSafetyLabelHistory(
currentHistories.map {
if (it.appInfo != appInfo) it
else it.addSafetyLabelIfChanged(safetyLabel)
- })
+ }
+ )
}
write(file, updatedAppsSafetyLabelHistory)
@@ -231,7 +237,9 @@ object AppsSafetyLabelHistoryPersistence {
// to startTime. The aim is retain one safety label prior to start time to
// be used as the "before" safety label when determining updates.
AppSafetyLabelHistory(
- appHistory.appInfo, history.subList(last, history.size))
+ appHistory.appInfo,
+ history.subList(last, history.size)
+ )
}
}
@@ -265,7 +273,10 @@ object AppsSafetyLabelHistoryPersistence {
listeners.forEach { it.onSafetyLabelHistoryChanged() }
} catch (e: Exception) {
Log.i(
- LOG_TAG, "Failed to write to $file. Previous version of file will be restored.", e)
+ LOG_TAG,
+ "Failed to write to $file. Previous version of file will be restored.",
+ e
+ )
atomicFile.failWrite(outputStream)
} finally {
try {
@@ -284,10 +295,12 @@ object AppsSafetyLabelHistoryPersistence {
return currentAppsSafetyLabelHistory.appSafetyLabelHistories.mapNotNull {
val before = it.getSafetyLabelAt(startTime)
val after = it.getLatestSafetyLabel()
- if (before == null ||
- after == null ||
- before == after ||
- before.receivedAt.isAfter(after.receivedAt))
+ if (
+ before == null ||
+ after == null ||
+ before == after ||
+ before.receivedAt.isAfter(after.receivedAt)
+ )
null
else AppSafetyLabelDiff(before, after)
}
@@ -332,7 +345,8 @@ object AppsSafetyLabelHistoryPersistence {
else ->
throw IllegalArgumentException(
"Unexpected attribute ${getAttributeName(i)} in tag" +
- " $TAG_APPS_SAFETY_LABEL_HISTORY")
+ " $TAG_APPS_SAFETY_LABEL_HISTORY"
+ )
}
}
if (version == null) {
@@ -350,7 +364,9 @@ object AppsSafetyLabelHistoryPersistence {
next()
return AppsSafetyLabelHistoryFileContent(
- AppsSafetyLabelHistory(appSafetyLabelHistories), version)
+ AppsSafetyLabelHistory(appSafetyLabelHistories),
+ version
+ )
}
private fun XmlPullParser.parseAppSafetyLabelHistory(): AppSafetyLabelHistory {
@@ -379,7 +395,8 @@ object AppsSafetyLabelHistoryPersistence {
ATTRIBUTE_RECEIVED_AT -> receivedAt = parseInstant(getAttributeValue(i))
else ->
throw IllegalArgumentException(
- "Unexpected attribute ${getAttributeName(i)} in tag $TAG_SAFETY_LABEL")
+ "Unexpected attribute ${getAttributeName(i)} in tag $TAG_SAFETY_LABEL"
+ )
}
}
if (receivedAt == null) {
@@ -432,7 +449,8 @@ object AppsSafetyLabelHistoryPersistence {
ATTRIBUTE_CONTAINS_ADS -> hasAds = getAttributeValue(i).toBoolean()
else ->
throw IllegalArgumentException(
- "Unexpected attribute ${getAttributeName(i)} in tag $TAG_DATA_SHARED_ENTRY")
+ "Unexpected attribute ${getAttributeName(i)} in tag $TAG_DATA_SHARED_ENTRY"
+ )
}
}
if (category == null) {
@@ -440,7 +458,8 @@ object AppsSafetyLabelHistoryPersistence {
}
if (hasAds == null) {
throw IllegalArgumentException(
- "Missing $ATTRIBUTE_CONTAINS_ADS in $TAG_DATA_SHARED_ENTRY")
+ "Missing $ATTRIBUTE_CONTAINS_ADS in $TAG_DATA_SHARED_ENTRY"
+ )
}
nextTag()
@@ -458,7 +477,8 @@ object AppsSafetyLabelHistoryPersistence {
ATTRIBUTE_PACKAGE_NAME -> packageName = getAttributeValue(i)
else ->
throw IllegalArgumentException(
- "Unexpected attribute ${getAttributeName(i)} in tag $TAG_APP_INFO")
+ "Unexpected attribute ${getAttributeName(i)} in tag $TAG_APP_INFO"
+ )
}
}
if (packageName == null) {
@@ -540,7 +560,8 @@ object AppsSafetyLabelHistoryPersistence {
attribute(
null,
ATTRIBUTE_CONTAINS_ADS,
- dataSharedEntry.value.containsAdvertisingPurpose.toString())
+ dataSharedEntry.value.containsAdvertisingPurpose.toString()
+ )
endTag(null, TAG_DATA_SHARED_ENTRY)
}
@@ -592,7 +613,10 @@ object AppsSafetyLabelHistoryPersistence {
*/
private fun getMaxSafetyLabelsToPersist() =
DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP, 20)
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP,
+ 20
+ )
/** An interface to listen to changes to persisted safety labels. */
interface ChangeListener {
diff --git a/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt b/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt
index 99f4adb02..0a7375642 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt
@@ -56,8 +56,10 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() {
}
val packageChangeEvent = getPackageChangeEvent(intent)
- if (!(packageChangeEvent == PackageChangeEvent.NEW_INSTALL ||
- packageChangeEvent == PackageChangeEvent.UPDATE)) {
+ if (
+ !(packageChangeEvent == PackageChangeEvent.NEW_INSTALL ||
+ packageChangeEvent == PackageChangeEvent.UPDATE)
+ ) {
return
}
@@ -74,7 +76,8 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() {
"received broadcast packageName: $packageName, current user: $currentUser," +
" packageChangeEvent: $packageChangeEvent, intent user:" +
" ${intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle::class.java)
- ?: currentUser}")
+ ?: currentUser}"
+ )
}
val userManager = Utils.getSystemServiceSafe(context, UserManager::class.java)
if (userManager.isProfile) {
@@ -127,7 +130,8 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() {
Log.i(
TAG,
"writeSafetyLabel called for packageName: $packageName, currentUser:" +
- " ${Process.myUserHandle()}")
+ " ${Process.myUserHandle()}"
+ )
}
// Get the context for the user in which the app is installed.
@@ -155,7 +159,10 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() {
val safetyLabelForPersistence: SafetyLabelForPersistence =
AppsSafetyLabelHistory.SafetyLabel.extractLocationSharingSafetyLabel(
- packageName, Instant.ofEpochMilli(receivedAtMs), safetyLabel)
+ packageName,
+ Instant.ofEpochMilli(receivedAtMs),
+ safetyLabel
+ )
val historyFile = AppsSafetyLabelHistoryPersistence.getSafetyLabelHistoryFile(context)
AppsSafetyLabelHistoryPersistence.recordSafetyLabel(safetyLabelForPersistence, historyFile)
@@ -212,12 +219,14 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() {
Log.i(
TAG,
"Forwarding intent from current user: $currentUser to profile parent" +
- " $profileParent")
+ " $profileParent"
+ )
context.sendBroadcastAsUser(
Intent(intent)
.setAction(ACTION_PACKAGE_ADDED_PERMISSIONCONTROLLER_FORWARDED)
.putExtra(Intent.EXTRA_USER, currentUser),
- profileParent)
+ profileParent
+ )
}
/** Types of package change events. */
diff --git a/PermissionController/tests/inprocess/AppThatUsesCameraPermission/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt b/PermissionController/tests/inprocess/AppThatUsesCameraPermission/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt
index 2a4900ce4..58f0a8968 100644
--- a/PermissionController/tests/inprocess/AppThatUsesCameraPermission/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt
+++ b/PermissionController/tests/inprocess/AppThatUsesCameraPermission/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt
@@ -18,5 +18,4 @@ package com.android.permissioncontroller.tests.appthatrequestpermission
import android.app.Activity
-class DummyActivity : Activity() {
-} \ No newline at end of file
+class DummyActivity : Activity() {}
diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/GetPermissionGroupInfoTest.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/GetPermissionGroupInfoTest.kt
index 1dd13be2a..b20e99c38 100644
--- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/GetPermissionGroupInfoTest.kt
+++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/GetPermissionGroupInfoTest.kt
@@ -22,9 +22,9 @@ import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.google.common.truth.Truth.assertThat
-import org.junit.Test
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
+import org.junit.Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
class GetPermissionGroupInfoTest {
@@ -43,8 +43,8 @@ class GetPermissionGroupInfoTest {
latch.countDown()
}
latch.await(timeoutMs, TimeUnit.MILLISECONDS)
- assertThat(returnedPerms).isEqualTo(
- PermissionMapping.getPlatformPermissionNamesOfGroup(group))
+ assertThat(returnedPerms)
+ .isEqualTo(PermissionMapping.getPlatformPermissionNamesOfGroup(group))
}
}
@@ -61,8 +61,8 @@ class GetPermissionGroupInfoTest {
latch.countDown()
}
latch.await(timeoutMs, TimeUnit.MILLISECONDS)
- assertThat(permGroup).isEqualTo(
- PermissionMapping.getGroupOfPlatformPermission(permName))
+ assertThat(permGroup)
+ .isEqualTo(PermissionMapping.getGroupOfPlatformPermission(permName))
}
}
}
diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveDataTest.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveDataTest.kt
index 24ea039a1..01a3525c2 100644
--- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveDataTest.kt
+++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveDataTest.kt
@@ -29,7 +29,7 @@ import org.junit.Before
import org.junit.Test
private const val APK =
- "/data/local/tmp/permissioncontroller/tests/inprocess/AppThatUsesCameraPermission.apk"
+ "/data/local/tmp/permissioncontroller/tests/inprocess/AppThatUsesCameraPermission.apk"
private const val PKG = "com.android.permissioncontroller.tests.appthatrequestpermission"
class AttributionLabelLiveDataTest {
@@ -43,8 +43,8 @@ class AttributionLabelLiveDataTest {
@Test
fun getValidTag() {
AttributionLabelLiveData["testTag", PKG, myUserHandle()].withLoadedValue {
- assertThat(context.packageManager.getResourcesForApplication(PKG)
- .getString(it!!)).isEqualTo("Test Attribution Label")
+ assertThat(context.packageManager.getResourcesForApplication(PKG).getString(it!!))
+ .isEqualTo("Test Attribution Label")
}
}
diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/DataUtils.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/DataUtils.kt
index 519a7ef64..b34c151c3 100644
--- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/DataUtils.kt
+++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/DataUtils.kt
@@ -23,11 +23,12 @@ import java.util.concurrent.CompletableFuture
fun <T> SmartUpdateMediatorLiveData<T>.withLoadedValue(block: (T?) -> Unit) {
val v = CompletableFuture<T?>()
- val observer = Observer<T?> {
- if (isInitialized) {
- v.complete(it)
+ val observer =
+ Observer<T?> {
+ if (isInitialized) {
+ v.complete(it)
+ }
}
- }
Handler(Looper.getMainLooper()).post { observeForever(observer) }
try {
@@ -35,4 +36,4 @@ fun <T> SmartUpdateMediatorLiveData<T>.withLoadedValue(block: (T?) -> Unit) {
} finally {
Handler(Looper.getMainLooper()).post { removeObserver(observer) }
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/ArrayUtilsTest.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/ArrayUtilsTest.kt
index 305dcdfb7..708d4222f 100644
--- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/ArrayUtilsTest.kt
+++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/ArrayUtilsTest.kt
@@ -23,8 +23,7 @@ import org.junit.Test
class ArrayUtilsTest {
@Test
fun appendString_appendToNull_returnsArrayWithString() {
- assertThat(ArrayUtils.appendString(null, TEST_STRING))
- .isEqualTo(arrayOf(TEST_STRING))
+ assertThat(ArrayUtils.appendString(null, TEST_STRING)).isEqualTo(arrayOf(TEST_STRING))
}
@Test
diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/KotlinUtilsTest.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/KotlinUtilsTest.kt
index 8f54da579..37aa8d988 100644
--- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/KotlinUtilsTest.kt
+++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/KotlinUtilsTest.kt
@@ -34,18 +34,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFailsWith
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mockito.argThat
import org.mockito.Mockito.mock
-import kotlin.test.assertFailsWith
import org.mockito.Mockito.`when` as whenever
-/**
- * Unit tests for [KotlinUtils].
- */
+/** Unit tests for [KotlinUtils]. */
@RunWith(AndroidJUnit4::class)
class KotlinUtilsTest {
private val targetContext = InstrumentationRegistry.getInstrumentation().targetContext
@@ -85,19 +83,25 @@ class KotlinUtilsTest {
whenever(mockContext.packageManager).thenReturn(mockPackageManager)
val installerIntent = Intent(ACTION_SHOW_APP_INFO).setPackage(installerPackage)
whenever(
- mockPackageManager.resolveActivity(
- argThat { intent -> intent.filterEquals(installerIntent) }, /* flags= */ anyInt()))
- .thenReturn(ResolveInfo().apply {
- activityInfo = ActivityInfo().apply {
- packageName = installerPackage
- name = installerActivity
+ mockPackageManager.resolveActivity(
+ argThat { intent -> intent.filterEquals(installerIntent) },
+ /* flags= */ anyInt()
+ )
+ )
+ .thenReturn(
+ ResolveInfo().apply {
+ activityInfo =
+ ActivityInfo().apply {
+ packageName = installerPackage
+ name = installerActivity
+ }
}
- })
+ )
val intent = KotlinUtils.getAppStoreIntent(mockContext, installerPackage, appPackage)
assertThat(intent).isNotNull()
- with (intent!!) {
+ with(intent!!) {
assertThat(action).isEqualTo(ACTION_SHOW_APP_INFO)
assertThat(component?.packageName).isEqualTo(installerPackage)
assertThat(component?.className).isEqualTo(installerActivity)
@@ -110,14 +114,16 @@ class KotlinUtilsTest {
val mockContext = mock(Context::class.java)
val mockPackageManager = mock(PackageManager::class.java)
whenever(mockContext.packageManager).thenReturn(mockPackageManager)
- whenever(
- mockPackageManager.resolveActivity(any(), /* flags= */ anyInt()))
- .thenReturn(ResolveInfo().apply {
- activityInfo = ActivityInfo().apply {
- packageName = ""
- name = ""
+ whenever(mockPackageManager.resolveActivity(any(), /* flags= */ anyInt()))
+ .thenReturn(
+ ResolveInfo().apply {
+ activityInfo =
+ ActivityInfo().apply {
+ packageName = ""
+ name = ""
+ }
}
- })
+ )
val intent = KotlinUtils.getAppStoreIntent(mockContext, "com.installer", appPackage)
@@ -149,7 +155,8 @@ class KotlinUtilsTest {
@Test
fun getMimeTypeForPermissions_bothReadMediaPermissions_returnsNull() {
assertThat(
- KotlinUtils.getMimeTypeForPermissions(listOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO)))
+ KotlinUtils.getMimeTypeForPermissions(listOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO))
+ )
.isNull()
}
diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/PermissionMappingTest.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/PermissionMappingTest.kt
index 64a13df60..4d942f815 100644
--- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/PermissionMappingTest.kt
+++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/PermissionMappingTest.kt
@@ -29,31 +29,41 @@ import org.junit.runner.RunWith
class PermissionMappingTest {
@Test
fun testGetPlatformPermissionGroupForOp_healthPermissionGroup() {
- assertThat(PermissionMapping.getPlatformPermissionGroupForOp(
- AppOpsManager.OPSTR_READ_WRITE_HEALTH_DATA
- )).isEqualTo(HealthPermissions.HEALTH_PERMISSION_GROUP)
+ assertThat(
+ PermissionMapping.getPlatformPermissionGroupForOp(
+ AppOpsManager.OPSTR_READ_WRITE_HEALTH_DATA
+ )
+ )
+ .isEqualTo(HealthPermissions.HEALTH_PERMISSION_GROUP)
}
@Test
fun testGetPlatformPermissionGroupForOp_microphone() {
- assertThat(PermissionMapping.getPlatformPermissionGroupForOp(
- AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE
- )).isEqualTo(Manifest.permission_group.MICROPHONE)
+ assertThat(
+ PermissionMapping.getPlatformPermissionGroupForOp(
+ AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE
+ )
+ )
+ .isEqualTo(Manifest.permission_group.MICROPHONE)
}
@Test
fun testGetPlatformPermissionGroupForOp_camera() {
assertThat(
- PermissionMapping.getPlatformPermissionGroupForOp(AppOpsManager.OPSTR_PHONE_CALL_CAMERA)
- ).isEqualTo(Manifest.permission_group.CAMERA)
+ PermissionMapping.getPlatformPermissionGroupForOp(
+ AppOpsManager.OPSTR_PHONE_CALL_CAMERA
+ )
+ )
+ .isEqualTo(Manifest.permission_group.CAMERA)
}
@Test
fun testGetPlatformPermissionGroupForOp_readContacts() {
assertThat(
- PermissionMapping.getPlatformPermissionGroupForOp(AppOpsManager.OPSTR_READ_CONTACTS)
- ).isEqualTo(
- PermissionMapping.getGroupOfPlatformPermission(Manifest.permission.READ_CONTACTS)
- )
+ PermissionMapping.getPlatformPermissionGroupForOp(AppOpsManager.OPSTR_READ_CONTACTS)
+ )
+ .isEqualTo(
+ PermissionMapping.getGroupOfPlatformPermission(Manifest.permission.READ_CONTACTS)
+ )
}
}
diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt
index 15218024e..11bcca356 100644
--- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt
+++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt
@@ -45,9 +45,9 @@ import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.privacysources.WorkPolicyInfo
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFailsWith
import org.junit.Ignore
import org.junit.Test
-import kotlin.test.assertFailsWith
class UtilsTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext as Context
@@ -110,8 +110,14 @@ class UtilsTest {
@Test
@Ignore("b/277782895")
fun getEnterpriseString() {
- assertThat(Utils.getEnterpriseString(context, WorkPolicyInfo.WORK_POLICY_TITLE,
- R.string.work_policy_title)).isInstanceOf(String::class.java)
+ assertThat(
+ Utils.getEnterpriseString(
+ context,
+ WorkPolicyInfo.WORK_POLICY_TITLE,
+ R.string.work_policy_title
+ )
+ )
+ .isInstanceOf(String::class.java)
}
@Test
@@ -148,12 +154,26 @@ class UtilsTest {
@Test
fun getPermissionGroupDescriptionString_validPermissionGroup() {
- val permissionGroupNames = listOf(ACTIVITY_RECOGNITION, CALENDAR, CALL_LOG,
- CAMERA, CONTACTS, LOCATION, MICROPHONE, NEARBY_DEVICES, PHONE, READ_MEDIA_AURAL,
- READ_MEDIA_VISUAL, SENSORS, SMS, STORAGE)
+ val permissionGroupNames =
+ listOf(
+ ACTIVITY_RECOGNITION,
+ CALENDAR,
+ CALL_LOG,
+ CAMERA,
+ CONTACTS,
+ LOCATION,
+ MICROPHONE,
+ NEARBY_DEVICES,
+ PHONE,
+ READ_MEDIA_AURAL,
+ READ_MEDIA_VISUAL,
+ SENSORS,
+ SMS,
+ STORAGE
+ )
for (permissionGroupName in permissionGroupNames) {
assertThat(Utils.getPermissionGroupDescriptionString(context, permissionGroupName, ""))
- .isNotNull()
+ .isNotNull()
}
}
@@ -167,8 +187,12 @@ class UtilsTest {
@Test
fun getPermissionLastAccessSummaryTimestamp_sensorDataPermission_lastAccessSummaryTimestampIsToday() {
- val result = Utils.getPermissionLastAccessSummaryTimestamp(System.currentTimeMillis(),
- context, LOCATION)
+ val result =
+ Utils.getPermissionLastAccessSummaryTimestamp(
+ System.currentTimeMillis(),
+ context,
+ LOCATION
+ )
assertThat(result.first).isNotEmpty()
assertThat(result.second).isEqualTo(Utils.LAST_24H_SENSOR_TODAY)
assertThat(result.third).isNotEmpty()
@@ -176,8 +200,12 @@ class UtilsTest {
@Test
fun getPermissionLastAccessSummaryTimestamp_sensorDataPermission_lastAccessSummaryTimestampIsYesterday() {
- val result = Utils.getPermissionLastAccessSummaryTimestamp(
- System.currentTimeMillis() - 24 * 60 * 60 * 1000, context, LOCATION)
+ val result =
+ Utils.getPermissionLastAccessSummaryTimestamp(
+ System.currentTimeMillis() - 24 * 60 * 60 * 1000,
+ context,
+ LOCATION
+ )
assertThat(result.first).isNotEmpty()
assertThat(result.second).isEqualTo(Utils.LAST_24H_SENSOR_YESTERDAY)
assertThat(result.third).isNotEmpty()
@@ -185,8 +213,12 @@ class UtilsTest {
@Test
fun getPermissionLastAccessSummaryTimestamp_sensorDataPermission_lastAccessSummaryTimestampIsLast7Days() {
- val result = Utils.getPermissionLastAccessSummaryTimestamp(
- System.currentTimeMillis() - 5 * 24 * 60 * 60 * 1000, context, LOCATION)
+ val result =
+ Utils.getPermissionLastAccessSummaryTimestamp(
+ System.currentTimeMillis() - 5 * 24 * 60 * 60 * 1000,
+ context,
+ LOCATION
+ )
assertThat(result.first).isNotEmpty()
assertThat(result.second).isEqualTo(Utils.LAST_7D_SENSOR)
assertThat(result.third).isNotEmpty()
@@ -194,8 +226,12 @@ class UtilsTest {
@Test
fun getPermissionLastAccessSummaryTimestamp_nonSensorDataPermission_lastAccessSummaryTimestampIsLast24Hrs() {
- val result = Utils.getPermissionLastAccessSummaryTimestamp(
- System.currentTimeMillis(), context, STORAGE)
+ val result =
+ Utils.getPermissionLastAccessSummaryTimestamp(
+ System.currentTimeMillis(),
+ context,
+ STORAGE
+ )
assertThat(result.first).isNotEmpty()
assertThat(result.second).isEqualTo(Utils.LAST_24H_CONTENT_PROVIDER)
assertThat(result.third).isNotEmpty()
@@ -203,8 +239,12 @@ class UtilsTest {
@Test
fun getPermissionLastAccessSummaryTimestamp_nonSensorDataPermission_lastAccessSummaryTimestampIs7Days() {
- val result = Utils.getPermissionLastAccessSummaryTimestamp(
- System.currentTimeMillis() - 5 * 60 * 60 * 24 * 1000, context, STORAGE)
+ val result =
+ Utils.getPermissionLastAccessSummaryTimestamp(
+ System.currentTimeMillis() - 5 * 60 * 60 * 24 * 1000,
+ context,
+ STORAGE
+ )
assertThat(result.first).isNotEmpty()
assertThat(result.second).isEqualTo(Utils.LAST_7D_CONTENT_PROVIDER)
assertThat(result.third).isNotEmpty()
diff --git a/PermissionController/tests/mocking/Android.bp b/PermissionController/tests/mocking/Android.bp
index 2d267eec5..f03cdef3e 100644
--- a/PermissionController/tests/mocking/Android.bp
+++ b/PermissionController/tests/mocking/Android.bp
@@ -140,5 +140,5 @@ android_test {
"mts-permission",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationControllerTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationControllerTest.kt
index e4cc110f0..c09b2e53c 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationControllerTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationControllerTest.kt
@@ -49,9 +49,7 @@ import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-/**
- * Unit tests for [HibernationController].
- */
+/** Unit tests for [HibernationController]. */
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
class HibernationControllerTest {
@@ -68,12 +66,9 @@ class HibernationControllerTest {
private var mockitoSession: MockitoSession? = null
- @Mock
- lateinit var context: Context
- @Mock
- lateinit var appHibernationManager: AppHibernationManager
- @Mock
- lateinit var usageStatsManager: UsageStatsManager
+ @Mock lateinit var context: Context
+ @Mock lateinit var appHibernationManager: AppHibernationManager
+ @Mock lateinit var usageStatsManager: UsageStatsManager
lateinit var filesDir: File
@@ -82,8 +77,11 @@ class HibernationControllerTest {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- mockitoSession = mockitoSession().mockStatic(PermissionControllerApplication::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
whenever(PermissionControllerApplication.get()).thenReturn(application)
filesDir = InstrumentationRegistry.getInstrumentation().getTargetContext().getCacheDir()
whenever(application.filesDir).thenReturn(filesDir)
@@ -92,8 +90,8 @@ class HibernationControllerTest {
doReturn(appHibernationManager).`when`(context).getSystemService(APP_HIBERNATION_SERVICE)
doReturn(usageStatsManager).`when`(context).getSystemService(USAGE_STATS_SERVICE)
- hibernationController = HibernationController(
- context, TEST_UNUSED_THRESHOLD, true /* targetsPreS */)
+ hibernationController =
+ HibernationController(context, TEST_UNUSED_THRESHOLD, true /* targetsPreS */)
}
@After
@@ -123,8 +121,8 @@ class HibernationControllerTest {
// GIVEN an app that is globally unused (i.e. unused at a package level)
val userPackages = listOf(makePackageInfo(PACKAGE_NAME_1), makePackageInfo(PACKAGE_NAME_2))
val map = mapOf(UserHandle.of(USER_ID) to userPackages)
- whenever(usageStatsManager.getLastTimeAnyComponentUsed(PACKAGE_NAME_1)).thenReturn(
- System.currentTimeMillis() - (TEST_UNUSED_THRESHOLD + TEST_MOCK_DELAY))
+ whenever(usageStatsManager.getLastTimeAnyComponentUsed(PACKAGE_NAME_1))
+ .thenReturn(System.currentTimeMillis() - (TEST_UNUSED_THRESHOLD + TEST_MOCK_DELAY))
// WHEN the controller hibernates the apps
hibernationController.hibernateApps(map)
@@ -138,8 +136,8 @@ class HibernationControllerTest {
// GIVEN an app that has been used globally (i.e. used at a package level)
val userPackages = listOf(makePackageInfo(PACKAGE_NAME_1), makePackageInfo(PACKAGE_NAME_2))
val map = mapOf(UserHandle.of(USER_ID) to userPackages)
- whenever(usageStatsManager.getLastTimeAnyComponentUsed(PACKAGE_NAME_1)).thenReturn(
- System.currentTimeMillis() - (TEST_UNUSED_THRESHOLD - TEST_MOCK_DELAY))
+ whenever(usageStatsManager.getLastTimeAnyComponentUsed(PACKAGE_NAME_1))
+ .thenReturn(System.currentTimeMillis() - (TEST_UNUSED_THRESHOLD - TEST_MOCK_DELAY))
// WHEN the controller hibernates the apps
hibernationController.hibernateApps(map)
@@ -162,6 +160,7 @@ class HibernationControllerTest {
0 /* firstInstallTime */,
0 /* lastUpdateTime */,
false /* areAttributionsUserVisible */,
- emptyMap() /* attributionTagsToLabels */)
+ emptyMap() /* attributionTagsToLabels */
+ )
}
}
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 fadd35f82..d85cb40b0 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
@@ -53,9 +53,7 @@ import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-/**
- * Unit tests for [HibernationPolicy].
- */
+/** Unit tests for [HibernationPolicy]. */
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
class HibernationPolicyTest {
@@ -77,10 +75,12 @@ class HibernationPolicyTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(DeviceConfig::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(DeviceConfig::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
`when`(PermissionControllerApplication.get()).thenReturn(application)
realContext = ApplicationProvider.getApplicationContext()
@@ -109,12 +109,14 @@ class HibernationPolicyTest {
fun onReceive_shouldInitializeAndAdjustStartTimeOfUnusedAppTracking() {
receiver.onReceive(context, Intent(Intent.ACTION_BOOT_COMPLETED))
val startTimeOfUnusedAppTracking =
- sharedPreferences.getLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
- SNAPSHOT_UNINITIALIZED)
+ sharedPreferences.getLong(
+ PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
+ SNAPSHOT_UNINITIALIZED
+ )
val systemTimeSnapshot =
- sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
- val realtimeSnapshot = sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT,
- SNAPSHOT_UNINITIALIZED)
+ sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
+ val realtimeSnapshot =
+ sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
val currentTimeMillis = System.currentTimeMillis()
val currentRealTime = SystemClock.elapsedRealtime()
assertThat(startTimeOfUnusedAppTracking).isNotEqualTo(SNAPSHOT_UNINITIALIZED)
@@ -134,30 +136,28 @@ class HibernationPolicyTest {
@Test
fun getStartTimeOfUnusedAppTracking_shouldReturnExpectedValue() {
assertThat(getStartTimeOfUnusedAppTracking(sharedPreferences))
- .isNotEqualTo(SNAPSHOT_UNINITIALIZED)
+ .isNotEqualTo(SNAPSHOT_UNINITIALIZED)
receiver.onReceive(context, Intent(Intent.ACTION_BOOT_COMPLETED))
- val systemTimeSnapshot = sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT,
- SNAPSHOT_UNINITIALIZED)
+ val systemTimeSnapshot =
+ sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
sharedPreferences
- .edit()
- .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTimeSnapshot - ONE_DAY_MS)
- .apply()
+ .edit()
+ .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTimeSnapshot - ONE_DAY_MS)
+ .apply()
assertThat(getStartTimeOfUnusedAppTracking(sharedPreferences))
- .isNotEqualTo(systemTimeSnapshot)
+ .isNotEqualTo(systemTimeSnapshot)
}
- private fun assertAdjustedTime(
- systemTimeSnapshot: Long,
- realtimeSnapshot: Long
- ) {
+ private fun assertAdjustedTime(systemTimeSnapshot: Long, realtimeSnapshot: Long) {
val newStartTimeOfUnusedAppTracking =
- sharedPreferences.getLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
- SNAPSHOT_UNINITIALIZED)
+ sharedPreferences.getLong(
+ PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
+ SNAPSHOT_UNINITIALIZED
+ )
val newSystemTimeSnapshot =
- sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
+ sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
val newRealtimeSnapshot =
- sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT,
- SNAPSHOT_UNINITIALIZED)
+ sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
assertThat(newStartTimeOfUnusedAppTracking).isNotEqualTo(SNAPSHOT_UNINITIALIZED)
assertThat(newSystemTimeSnapshot).isNotEqualTo(SNAPSHOT_UNINITIALIZED)
assertThat(newSystemTimeSnapshot).isGreaterThan(systemTimeSnapshot)
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/FakeEventStorage.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/FakeEventStorage.kt
index de43bd2d5..057af89d1 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/FakeEventStorage.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/FakeEventStorage.kt
@@ -19,9 +19,7 @@ package com.android.permissioncontroller.tests.mocking.permission.data
import com.android.permissioncontroller.permission.data.PermissionEvent
import com.android.permissioncontroller.permission.service.PermissionEventStorage
-/**
- * Fake event storage class used for tests
- */
+/** Fake event storage class used for tests */
class FakeEventStorage<T : PermissionEvent> : PermissionEventStorage<T> {
val events: MutableList<T> = mutableListOf()
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/v33/RecentPermissionDecisionsLiveDataTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/v33/RecentPermissionDecisionsLiveDataTest.kt
index 43d599871..b6d63ecb0 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/v33/RecentPermissionDecisionsLiveDataTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/v33/RecentPermissionDecisionsLiveDataTest.kt
@@ -28,19 +28,17 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidJUnit4::class)
class RecentPermissionDecisionsLiveDataTest {
- @Mock
- lateinit var job: Job
+ @Mock lateinit var job: Job
- @Mock
- lateinit var recentDecision: PermissionDecision
+ @Mock lateinit var recentDecision: PermissionDecision
private val recentPermissionDecisionStorage = FakeEventStorage<PermissionDecision>()
@@ -49,11 +47,9 @@ class RecentPermissionDecisionsLiveDataTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- runBlocking {
- recentPermissionDecisionStorage.storeEvent(recentDecision)
- }
- recentPermissionDecisionsLiveData = spy(RecentPermissionDecisionsLiveData(
- recentPermissionDecisionStorage))
+ runBlocking { recentPermissionDecisionStorage.storeEvent(recentDecision) }
+ recentPermissionDecisionsLiveData =
+ spy(RecentPermissionDecisionsLiveData(recentPermissionDecisionStorage))
}
@Test
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/BasePermissionEventStorageTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/BasePermissionEventStorageTest.kt
index 41a60101d..534342144 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/BasePermissionEventStorageTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/BasePermissionEventStorageTest.kt
@@ -27,6 +27,11 @@ import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.service.BasePermissionEventStorage
import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.io.InputStream
+import java.io.OutputStream
+import java.util.Date
+import java.util.concurrent.TimeUnit
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@@ -34,16 +39,11 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-import java.io.File
-import java.io.InputStream
-import java.io.OutputStream
-import java.util.Date
-import java.util.concurrent.TimeUnit
@RunWith(AndroidJUnit4::class)
class BasePermissionEventStorageTest {
@@ -66,11 +66,9 @@ class BasePermissionEventStorageTest {
private val parkingEvent = TestPermissionEvent("package.test.parking", jan22020)
private val podcastEvent = TestPermissionEvent("package.test.podcast", jan22020)
- @Mock
- lateinit var jobScheduler: JobScheduler
+ @Mock lateinit var jobScheduler: JobScheduler
- @Mock
- lateinit var existingJob: JobInfo
+ @Mock lateinit var existingJob: JobInfo
private lateinit var context: Context
private lateinit var storage: BasePermissionEventStorage<TestPermissionEvent>
@@ -80,10 +78,12 @@ class BasePermissionEventStorageTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(DeviceConfig::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(DeviceConfig::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
`when`(PermissionControllerApplication.get()).thenReturn(application)
context = ApplicationProvider.getApplicationContext()
filesDir = context.cacheDir
@@ -107,9 +107,7 @@ class BasePermissionEventStorageTest {
@Test
fun loadEvents_noData_returnsEmptyList() {
init()
- runBlocking {
- assertThat(storage.loadEvents()).isEmpty()
- }
+ runBlocking { assertThat(storage.loadEvents()).isEmpty() }
}
@Test
@@ -130,8 +128,7 @@ class BasePermissionEventStorageTest {
storage.storeEvent(parkingEvent)
storage.storeEvent(podcastEvent)
assertThat(storage.loadEvents())
- .containsExactly(musicEvent, mapEvent, parkingEvent,
- podcastEvent)
+ .containsExactly(musicEvent, mapEvent, parkingEvent, podcastEvent)
}
}
@@ -181,10 +178,10 @@ class BasePermissionEventStorageTest {
fun removeOldData_removesOnlyOldData() {
init()
val todayEvent = parkingEvent.copy(eventTime = System.currentTimeMillis())
- val sixDaysAgoEvent = podcastEvent.copy(
- eventTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(6))
- val eightDaysAgoEvent = parkingEvent.copy(
- eventTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(8))
+ val sixDaysAgoEvent =
+ podcastEvent.copy(eventTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(6))
+ val eightDaysAgoEvent =
+ parkingEvent.copy(eventTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(8))
runBlocking {
storage.storeEvent(eightDaysAgoEvent)
storage.storeEvent(sixDaysAgoEvent)
@@ -202,11 +199,10 @@ class BasePermissionEventStorageTest {
storage.storeEvent(musicEvent)
storage.updateEventsBySystemTimeDelta(TimeUnit.DAYS.toMillis(1))
- assertThat(storage.loadEvents()).containsExactly(
- musicEvent.copy(
- eventTime = musicEvent.eventTime + TimeUnit.DAYS.toMillis(1)
+ assertThat(storage.loadEvents())
+ .containsExactly(
+ musicEvent.copy(eventTime = musicEvent.eventTime + TimeUnit.DAYS.toMillis(1))
)
- )
}
}
@@ -217,18 +213,15 @@ class BasePermissionEventStorageTest {
storage.storeEvent(musicEvent)
storage.updateEventsBySystemTimeDelta(-TimeUnit.DAYS.toMillis(1))
- assertThat(storage.loadEvents()).containsExactly(
- musicEvent.copy(
- eventTime = musicEvent.eventTime - TimeUnit.DAYS.toMillis(1)
+ assertThat(storage.loadEvents())
+ .containsExactly(
+ musicEvent.copy(eventTime = musicEvent.eventTime - TimeUnit.DAYS.toMillis(1))
)
- )
}
}
- private class TestPermissionEventStorage(
- context: Context,
- jobScheduler: JobScheduler
- ) : BasePermissionEventStorage<TestPermissionEvent>(context, jobScheduler) {
+ private class TestPermissionEventStorage(context: Context, jobScheduler: JobScheduler) :
+ BasePermissionEventStorage<TestPermissionEvent>(context, jobScheduler) {
lateinit var fakeDiskStore: List<TestPermissionEvent>
override fun serialize(stream: OutputStream, events: List<TestPermissionEvent>) {
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionChangeStorageImplTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionChangeStorageImplTest.kt
index 761fff18b..21855c124 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionChangeStorageImplTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionChangeStorageImplTest.kt
@@ -28,6 +28,10 @@ import com.android.permissioncontroller.permission.data.PermissionChange
import com.android.permissioncontroller.permission.service.PermissionChangeStorageImpl
import com.android.permissioncontroller.permission.utils.Utils
import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.util.Date
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@@ -39,10 +43,6 @@ import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-import java.io.ByteArrayInputStream
-import java.io.ByteArrayOutputStream
-import java.io.File
-import java.util.Date
@RunWith(AndroidJUnit4::class)
class PermissionChangeStorageImplTest {
@@ -57,8 +57,7 @@ class PermissionChangeStorageImplTest {
private val mapChange = PermissionChange(MAP_PACKAGE_NAME, jan12020)
- @Mock
- lateinit var jobScheduler: JobScheduler
+ @Mock lateinit var jobScheduler: JobScheduler
private lateinit var context: Context
private lateinit var storage: PermissionChangeStorageImpl
@@ -68,10 +67,12 @@ class PermissionChangeStorageImplTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(DeviceConfig::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(DeviceConfig::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
Mockito.`when`(PermissionControllerApplication.get()).thenReturn(application)
context = ApplicationProvider.getApplicationContext()
filesDir = context.cacheDir
@@ -101,8 +102,7 @@ class PermissionChangeStorageImplTest {
@Test
fun serialize_roundsTimeDownToDate() {
- val laterInTheDayGrant = mapChange.copy(
- eventTime = (mapChange.eventTime + FIVE_HOURS_MS))
+ val laterInTheDayGrant = mapChange.copy(eventTime = (mapChange.eventTime + FIVE_HOURS_MS))
val outStream = ByteArrayOutputStream()
storage.serialize(outStream, listOf(laterInTheDayGrant))
@@ -113,9 +113,12 @@ class PermissionChangeStorageImplTest {
@Test
fun serialize_exactTimeDataCanBeParsed() {
Mockito.`when`(
- DeviceConfig.getBoolean(ArgumentMatchers.eq(DeviceConfig.NAMESPACE_PERMISSIONS),
- ArgumentMatchers.eq(Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME),
- ArgumentMatchers.anyBoolean()))
+ DeviceConfig.getBoolean(
+ ArgumentMatchers.eq(DeviceConfig.NAMESPACE_PERMISSIONS),
+ ArgumentMatchers.eq(Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME),
+ ArgumentMatchers.anyBoolean()
+ )
+ )
.thenReturn(true)
val outStream = ByteArrayOutputStream()
@@ -131,9 +134,12 @@ class PermissionChangeStorageImplTest {
storage.serialize(outStream, listOf(mapChange))
Mockito.`when`(
- DeviceConfig.getBoolean(ArgumentMatchers.eq(DeviceConfig.NAMESPACE_PERMISSIONS),
- ArgumentMatchers.eq(Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME),
- ArgumentMatchers.anyBoolean()))
+ DeviceConfig.getBoolean(
+ ArgumentMatchers.eq(DeviceConfig.NAMESPACE_PERMISSIONS),
+ ArgumentMatchers.eq(Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME),
+ ArgumentMatchers.anyBoolean()
+ )
+ )
.thenReturn(true)
val inStream = ByteArrayInputStream(outStream.toByteArray())
@@ -143,20 +149,25 @@ class PermissionChangeStorageImplTest {
@Test
fun serialize_afterStoresExactTimeChangedToFalse_roundsTimeDownToDate() {
Mockito.`when`(
- DeviceConfig.getBoolean(ArgumentMatchers.eq(DeviceConfig.NAMESPACE_PERMISSIONS),
- ArgumentMatchers.eq(Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME),
- ArgumentMatchers.anyBoolean()))
+ DeviceConfig.getBoolean(
+ ArgumentMatchers.eq(DeviceConfig.NAMESPACE_PERMISSIONS),
+ ArgumentMatchers.eq(Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME),
+ ArgumentMatchers.anyBoolean()
+ )
+ )
.thenReturn(true)
- val laterInTheDayEvent = mapChange.copy(
- eventTime = (mapChange.eventTime + FIVE_HOURS_MS))
+ val laterInTheDayEvent = mapChange.copy(eventTime = (mapChange.eventTime + FIVE_HOURS_MS))
val outStream = ByteArrayOutputStream()
storage.serialize(outStream, listOf(laterInTheDayEvent))
Mockito.`when`(
- DeviceConfig.getBoolean(ArgumentMatchers.eq(DeviceConfig.NAMESPACE_PERMISSIONS),
- ArgumentMatchers.eq(Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME),
- ArgumentMatchers.anyBoolean()))
+ DeviceConfig.getBoolean(
+ ArgumentMatchers.eq(DeviceConfig.NAMESPACE_PERMISSIONS),
+ ArgumentMatchers.eq(Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME),
+ ArgumentMatchers.anyBoolean()
+ )
+ )
.thenReturn(false)
val inStream = ByteArrayInputStream(outStream.toByteArray())
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionEventCleanupJobServiceTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionEventCleanupJobServiceTest.kt
index b60fea123..2f95d467d 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionEventCleanupJobServiceTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionEventCleanupJobServiceTest.kt
@@ -28,6 +28,7 @@ import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.service.PermissionEventCleanupJobService
import com.android.permissioncontroller.permission.service.PermissionEventCleanupJobService.Companion.DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY
import com.android.permissioncontroller.permission.utils.Utils
+import java.io.File
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -38,7 +39,6 @@ import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-import java.io.File
@RunWith(AndroidJUnit4::class)
class PermissionEventCleanupJobServiceTest {
@@ -47,10 +47,8 @@ class PermissionEventCleanupJobServiceTest {
val application = Mockito.mock(PermissionControllerApplication::class.java)
}
- @Mock
- lateinit var jobScheduler: JobScheduler
- @Mock
- lateinit var existingJob: JobInfo
+ @Mock lateinit var jobScheduler: JobScheduler
+ @Mock lateinit var existingJob: JobInfo
private lateinit var context: Context
private lateinit var mockitoSession: MockitoSession
@@ -59,19 +57,24 @@ class PermissionEventCleanupJobServiceTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(DeviceConfig::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(DeviceConfig::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
Mockito.`when`(PermissionControllerApplication.get()).thenReturn(application)
context = ApplicationProvider.getApplicationContext()
filesDir = context.cacheDir
Mockito.`when`(application.filesDir).thenReturn(filesDir)
Mockito.`when`(jobScheduler.schedule(Mockito.any())).thenReturn(JobScheduler.RESULT_SUCCESS)
Mockito.`when`(
- DeviceConfig.getLong(eq(DeviceConfig.NAMESPACE_PERMISSIONS),
- eq(Utils.PROPERTY_PERMISSION_EVENTS_CHECK_OLD_FREQUENCY_MILLIS),
- eq(DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY)))
+ DeviceConfig.getLong(
+ eq(DeviceConfig.NAMESPACE_PERMISSIONS),
+ eq(Utils.PROPERTY_PERMISSION_EVENTS_CHECK_OLD_FREQUENCY_MILLIS),
+ eq(DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY)
+ )
+ )
.thenReturn(DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY)
}
@@ -93,8 +96,8 @@ class PermissionEventCleanupJobServiceTest {
@Test
fun init_existingJob_doesNotScheduleNewJob() {
- Mockito.`when`(existingJob.intervalMillis).thenReturn(
- DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY)
+ Mockito.`when`(existingJob.intervalMillis)
+ .thenReturn(DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY)
Mockito.`when`(jobScheduler.getPendingJob(Constants.OLD_PERMISSION_EVENT_CLEANUP_JOB_ID))
.thenReturn(existingJob)
PermissionEventCleanupJobService.scheduleOldDataCleanupIfNecessary(context, jobScheduler)
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionStorageTimeChangeReceiverTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionStorageTimeChangeReceiverTest.kt
index cf5d92fe3..2ae4b0585 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionStorageTimeChangeReceiverTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PermissionStorageTimeChangeReceiverTest.kt
@@ -34,6 +34,8 @@ import com.android.permissioncontroller.permission.service.PermissionStorageTime
import com.android.permissioncontroller.permission.service.PermissionStorageTimeChangeReceiver.Companion.PREF_KEY_SYSTEM_TIME_SNAPSHOT
import com.android.permissioncontroller.permission.service.PermissionStorageTimeChangeReceiver.Companion.SNAPSHOT_UNINITIALIZED
import com.android.permissioncontroller.permission.utils.TimeSource
+import java.io.File
+import java.util.concurrent.TimeUnit
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -43,17 +45,15 @@ import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-import java.io.File
-import java.util.concurrent.TimeUnit
@RunWith(AndroidJUnit4::class)
class PermissionStorageTimeChangeReceiverTest {
@@ -62,20 +62,15 @@ class PermissionStorageTimeChangeReceiverTest {
val application = mock(PermissionControllerApplication::class.java)
}
- @Mock
- lateinit var context: Context
+ @Mock lateinit var context: Context
- @Mock
- lateinit var sharedPreferences: SharedPreferences
+ @Mock lateinit var sharedPreferences: SharedPreferences
- @Mock
- lateinit var editor: SharedPreferences.Editor
+ @Mock lateinit var editor: SharedPreferences.Editor
- @Mock
- lateinit var packageManager: PackageManager
+ @Mock lateinit var packageManager: PackageManager
- @Mock
- lateinit var permissionEventStorage: PermissionEventStorage<out PermissionEvent>
+ @Mock lateinit var permissionEventStorage: PermissionEventStorage<out PermissionEvent>
private val fakeTimeSource = FakeTimeSource()
private lateinit var mockitoSession: MockitoSession
@@ -85,25 +80,27 @@ class PermissionStorageTimeChangeReceiverTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(DeviceConfig::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(DeviceConfig::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
`when`(PermissionControllerApplication.get()).thenReturn(application)
`when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPreferences)
`when`(sharedPreferences.edit()).thenReturn(editor)
val context: Context = ApplicationProvider.getApplicationContext()
filesDir = context.cacheDir
`when`(application.filesDir).thenReturn(filesDir)
- `when`(DeviceConfig.getProperty(eq(DeviceConfig.NAMESPACE_PERMISSIONS),
- anyString())).thenReturn(null)
+ `when`(DeviceConfig.getProperty(eq(DeviceConfig.NAMESPACE_PERMISSIONS), anyString()))
+ .thenReturn(null)
`when`(sharedPreferences.getLong(eq(PREF_KEY_SYSTEM_TIME_SNAPSHOT), anyLong()))
.thenReturn(SNAPSHOT_UNINITIALIZED)
`when`(sharedPreferences.getLong(eq(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT), anyLong()))
.thenReturn(SNAPSHOT_UNINITIALIZED)
- receiver = spy(PermissionStorageTimeChangeReceiver(listOf(permissionEventStorage),
- fakeTimeSource))
+ receiver =
+ spy(PermissionStorageTimeChangeReceiver(listOf(permissionEventStorage), fakeTimeSource))
}
@After
@@ -123,8 +120,8 @@ class PermissionStorageTimeChangeReceiverTest {
@Test
fun onReceive_timeSetReceived_beforeBootCompleted_doesNothing() {
- fakeTimeSource.currentTimeMillis = fakeTimeSource.currentTimeMillis +
- TimeUnit.DAYS.toMillis(2)
+ fakeTimeSource.currentTimeMillis =
+ fakeTimeSource.currentTimeMillis + TimeUnit.DAYS.toMillis(2)
receiver.onReceive(context, Intent(Intent.ACTION_TIME_CHANGED))
verify(receiver, never()).onTimeChanged(anyLong())
@@ -143,8 +140,8 @@ class PermissionStorageTimeChangeReceiverTest {
fun onReceive_timeDiffBelowMinimum_doesNothing() {
mockBootCompletedSnapshot()
- fakeTimeSource.currentTimeMillis = fakeTimeSource.currentTimeMillis -
- TimeUnit.SECONDS.toMillis(30)
+ fakeTimeSource.currentTimeMillis =
+ fakeTimeSource.currentTimeMillis - TimeUnit.SECONDS.toMillis(30)
receiver.onReceive(context, Intent(Intent.ACTION_TIME_CHANGED))
verify(receiver, never()).onTimeChanged(anyLong())
@@ -155,10 +152,9 @@ class PermissionStorageTimeChangeReceiverTest {
mockBootCompletedSnapshot()
// in 3 days the time is set to one day from now (effectively set back by 2 days)
- fakeTimeSource.currentTimeMillis = fakeTimeSource.currentTimeMillis +
- TimeUnit.DAYS.toMillis(1)
- fakeTimeSource.elapsedRealtime = fakeTimeSource.elapsedRealtime +
- TimeUnit.DAYS.toMillis(3)
+ fakeTimeSource.currentTimeMillis =
+ fakeTimeSource.currentTimeMillis + TimeUnit.DAYS.toMillis(1)
+ fakeTimeSource.elapsedRealtime = fakeTimeSource.elapsedRealtime + TimeUnit.DAYS.toMillis(3)
receiver.onReceive(context, Intent(Intent.ACTION_TIME_CHANGED))
verify(receiver).onTimeChanged(-TimeUnit.DAYS.toMillis(2))
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PersistedStoragePackageUninstalledReceiverTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PersistedStoragePackageUninstalledReceiverTest.kt
index 2cefaab67..baa848960 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PersistedStoragePackageUninstalledReceiverTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/PersistedStoragePackageUninstalledReceiverTest.kt
@@ -29,6 +29,8 @@ import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.service.PersistedStoragePackageUninstalledReceiver
import com.android.permissioncontroller.tests.mocking.permission.data.FakeEventStorage
import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.util.Date
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.After
@@ -37,14 +39,12 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.spy
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-import java.io.File
-import java.util.Date
@RunWith(AndroidJUnit4::class)
class PersistedStoragePackageUninstalledReceiverTest {
@@ -55,14 +55,11 @@ class PersistedStoragePackageUninstalledReceiverTest {
private val musicEvent = TestPermissionEvent("package.test.music", Date(2020, 0, 1).time)
- @Mock
- lateinit var context: Context
+ @Mock lateinit var context: Context
- @Mock
- lateinit var packageManager: PackageManager
+ @Mock lateinit var packageManager: PackageManager
- @Mock
- lateinit var jobScheduler: JobScheduler
+ @Mock lateinit var jobScheduler: JobScheduler
private lateinit var mockitoSession: MockitoSession
private lateinit var filesDir: File
@@ -72,17 +69,24 @@ class PersistedStoragePackageUninstalledReceiverTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(PermissionControllerApplication::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
`when`(PermissionControllerApplication.get()).thenReturn(application)
val context: Context = ApplicationProvider.getApplicationContext()
filesDir = context.cacheDir
`when`(application.filesDir).thenReturn(filesDir)
permissionEventStorage = spy(FakeEventStorage())
- receiver = spy(PersistedStoragePackageUninstalledReceiver(
- listOf(permissionEventStorage), Dispatchers.Main.immediate))
+ receiver =
+ spy(
+ PersistedStoragePackageUninstalledReceiver(
+ listOf(permissionEventStorage),
+ Dispatchers.Main.immediate
+ )
+ )
}
@After
@@ -112,8 +116,6 @@ class PersistedStoragePackageUninstalledReceiverTest {
receiver.onReceive(context, intent)
- runBlocking {
- assertThat(permissionEventStorage.loadEvents().isEmpty())
- }
+ runBlocking { assertThat(permissionEventStorage.loadEvents().isEmpty()) }
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/RuntimePermissionsUpgradeControllerTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/RuntimePermissionsUpgradeControllerTest.kt
index d576f2924..d4522241b 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/RuntimePermissionsUpgradeControllerTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/RuntimePermissionsUpgradeControllerTest.kt
@@ -24,6 +24,7 @@ import android.Manifest.permission.READ_EXTERNAL_STORAGE
import android.Manifest.permission.READ_MEDIA_AUDIO
import android.Manifest.permission.READ_MEDIA_IMAGES
import android.Manifest.permission.READ_MEDIA_VIDEO
+import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
import android.Manifest.permission.SEND_SMS
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.ActivityManager
@@ -82,8 +83,7 @@ class RuntimePermissionsUpgradeControllerTest {
init {
whenever(application.applicationContext).thenReturn(application)
- whenever(application.createContextAsUser(any(), anyInt())).thenReturn(
- application)
+ whenever(application.createContextAsUser(any(), anyInt())).thenReturn(application)
whenever(application.registerComponentCallbacks(any())).thenAnswer {
val dataRepository = it.arguments[0] as ComponentCallbacks2
@@ -94,34 +94,31 @@ class RuntimePermissionsUpgradeControllerTest {
}
/** Latest permission database version known in this test */
- private val LATEST_VERSION = if (SdkLevel.isAtLeastT()) {
- 10
- } else {
- 9
- }
+ private val LATEST_VERSION =
+ if (SdkLevel.isAtLeastT()) {
+ 10
+ } else {
+ 9
+ }
/** Use a unique test package name for each test */
private val TEST_PKG_NAME: String
- get() = Thread.currentThread().stackTrace
- .filter { it.className == this::class.java.name }[1].methodName
+ get() =
+ Thread.currentThread()
+ .stackTrace
+ .filter { it.className == this::class.java.name }[1]
+ .methodName
/** Mockito session of this test */
private var mockitoSession: MockitoSession? = null
- @Mock
- lateinit var packageManager: PackageManager
- @Mock
- lateinit var permissionManager: PermissionManager
- @Mock
- lateinit var activityManager: ActivityManager
- @Mock
- lateinit var appOpsManager: AppOpsManager
- @Mock
- lateinit var locationManager: LocationManager
- @Mock
- lateinit var userManager: UserManager
- @Mock
- lateinit var jobScheduler: JobScheduler
+ @Mock lateinit var packageManager: PackageManager
+ @Mock lateinit var permissionManager: PermissionManager
+ @Mock lateinit var activityManager: ActivityManager
+ @Mock lateinit var appOpsManager: AppOpsManager
+ @Mock lateinit var locationManager: LocationManager
+ @Mock lateinit var userManager: UserManager
+ @Mock lateinit var jobScheduler: JobScheduler
/**
* Set up {@link #packageManager} as if the passed packages are installed.
@@ -130,24 +127,28 @@ class RuntimePermissionsUpgradeControllerTest {
*/
private fun setPackages(vararg pkgs: Package) {
val mockPackageInfo = { pkgs: List<Package>, flags: Long ->
- pkgs.filter { pkg ->
- (flags and MATCH_FACTORY_ONLY.toLong()) == 0L || pkg.isPreinstalled
- }.map { pkg ->
- PackageInfo().apply {
- packageName = pkg.name
- requestedPermissions = pkg.permissions.map { it.name }.toTypedArray()
- requestedPermissionsFlags = pkg.permissions.map {
- if (it.isGranted) {
- REQUESTED_PERMISSION_GRANTED
- } else {
- 0
- }
- }.toIntArray()
- applicationInfo = ApplicationInfo().apply {
- targetSdkVersion = pkg.targetSdkVersion
+ pkgs
+ .filter { pkg ->
+ (flags and MATCH_FACTORY_ONLY.toLong()) == 0L || pkg.isPreinstalled
+ }
+ .map { pkg ->
+ PackageInfo().apply {
+ packageName = pkg.name
+ requestedPermissions = pkg.permissions.map { it.name }.toTypedArray()
+ requestedPermissionsFlags =
+ pkg.permissions
+ .map {
+ if (it.isGranted) {
+ REQUESTED_PERMISSION_GRANTED
+ } else {
+ 0
+ }
+ }
+ .toIntArray()
+ applicationInfo =
+ ApplicationInfo().apply { targetSdkVersion = pkg.targetSdkVersion }
}
}
- }
}
whenever(packageManager.getInstalledPackagesAsUser(anyInt(), anyInt())).thenAnswer {
@@ -158,22 +159,22 @@ class RuntimePermissionsUpgradeControllerTest {
if (SdkLevel.isAtLeastT()) {
whenever(
- packageManager.getInstalledPackagesAsUser(
- any(PackageManager.PackageInfoFlags::class.java),
- anyInt()
+ packageManager.getInstalledPackagesAsUser(
+ any(PackageManager.PackageInfoFlags::class.java),
+ anyInt()
+ )
)
- ).thenAnswer {
- val flags = it.arguments[0] as PackageManager.PackageInfoFlags
+ .thenAnswer {
+ val flags = it.arguments[0] as PackageManager.PackageInfoFlags
- mockPackageInfo(pkgs.toList(), flags.value)
- }
+ mockPackageInfo(pkgs.toList(), flags.value)
+ }
}
whenever(packageManager.getPackageInfo(anyString(), anyInt())).thenAnswer {
val packageName = it.arguments[0] as String
- packageManager.getInstalledPackagesAsUser(0, 0)
- .find { it.packageName == packageName }
+ packageManager.getInstalledPackagesAsUser(0, 0).find { it.packageName == packageName }
?: throw PackageManager.NameNotFoundException()
}
@@ -181,8 +182,12 @@ class RuntimePermissionsUpgradeControllerTest {
val permissionName = it.arguments[0] as String
val packageName = it.arguments[1] as String
- pkgs.find { it.name == packageName }?.permissions
- ?.find { it.name == permissionName }?.flags ?: 0
+ pkgs
+ .find { it.name == packageName }
+ ?.permissions
+ ?.find { it.name == permissionName }
+ ?.flags
+ ?: 0
}
}
@@ -194,37 +199,43 @@ class RuntimePermissionsUpgradeControllerTest {
fun initSystem() {
initMocks(this)
- mockitoSession = mockitoSession().mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(Settings.Secure::class.java).strictness(LENIENT).startMocking()
+ mockitoSession =
+ mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(Settings.Secure::class.java)
+ .strictness(LENIENT)
+ .startMocking()
whenever(PermissionControllerApplication.get()).thenReturn(application)
- whenever(application.getSystemService(PermissionManager::class.java)).thenReturn(
- permissionManager)
- whenever(application.getSystemService(ActivityManager::class.java)).thenReturn(
- activityManager)
+ whenever(application.getSystemService(PermissionManager::class.java))
+ .thenReturn(permissionManager)
+ whenever(application.getSystemService(ActivityManager::class.java))
+ .thenReturn(activityManager)
whenever(application.getSystemService(AppOpsManager::class.java)).thenReturn(appOpsManager)
- whenever(application.getSystemService(LocationManager::class.java)).thenReturn(
- locationManager)
- whenever(application.getSystemService(UserManager::class.java)).thenReturn(
- userManager)
- whenever(application.getSystemService(JobScheduler::class.java)).thenReturn(
- jobScheduler)
+ whenever(application.getSystemService(LocationManager::class.java))
+ .thenReturn(locationManager)
+ whenever(application.getSystemService(UserManager::class.java)).thenReturn(userManager)
+ whenever(application.getSystemService(JobScheduler::class.java)).thenReturn(jobScheduler)
whenever(application.packageManager).thenReturn(packageManager)
whenever(packageManager.getPermissionInfo(any(), anyInt())).thenAnswer {
val permissionName = it.arguments[0] as String
- InstrumentationRegistry.getInstrumentation().getTargetContext().packageManager
- .getPermissionInfo(permissionName, 0)
+ InstrumentationRegistry.getInstrumentation()
+ .getTargetContext()
+ .packageManager
+ .getPermissionInfo(permissionName, 0)
}
whenever(packageManager.getPermissionGroupInfo(any(), anyInt())).thenAnswer {
val groupName = it.arguments[0] as String
- InstrumentationRegistry.getInstrumentation().getTargetContext().packageManager
- .getPermissionGroupInfo(groupName, 0)
+ InstrumentationRegistry.getInstrumentation()
+ .getTargetContext()
+ .packageManager
+ .getPermissionGroupInfo(groupName, 0)
}
// We cannot use thenReturn(mutableListOf()) because that would return the same instance.
@@ -233,15 +244,14 @@ class RuntimePermissionsUpgradeControllerTest {
}
}
- /**
- * Call {@link RuntimePermissionsUpgradeController#upgradeIfNeeded) and wait until finished.
- */
+ /** Call {@link RuntimePermissionsUpgradeController#upgradeIfNeeded) and wait until finished. */
private fun upgradeIfNeeded() {
val completionCallback = CompletableFuture<Unit>()
runWithShellPermissionIdentity {
- RuntimePermissionsUpgradeController.upgradeIfNeeded(application, Runnable {
- completionCallback.complete(Unit)
- })
+ RuntimePermissionsUpgradeController.upgradeIfNeeded(
+ application,
+ Runnable { completionCallback.complete(Unit) }
+ )
completionCallback.join()
}
}
@@ -252,37 +262,37 @@ class RuntimePermissionsUpgradeControllerTest {
private fun verifyWhitelisted(packageName: String, vararg permissionNames: String) {
for (permissionName in permissionNames) {
- verify(packageManager, timeout(100)).addWhitelistedRestrictedPermission(
- packageName, permissionName, FLAG_PERMISSION_WHITELIST_UPGRADE)
+ verify(packageManager, timeout(100))
+ .addWhitelistedRestrictedPermission(
+ packageName,
+ permissionName,
+ FLAG_PERMISSION_WHITELIST_UPGRADE
+ )
}
}
private fun verifyNotWhitelisted(packageName: String, vararg permissionNames: String) {
for (permissionName in permissionNames) {
- verify(packageManager, never()).addWhitelistedRestrictedPermission(eq(packageName),
- eq(permissionName), anyInt())
+ verify(packageManager, never())
+ .addWhitelistedRestrictedPermission(eq(packageName), eq(permissionName), anyInt())
}
}
private fun verifyGranted(packageName: String, permissionName: String) {
- verify(packageManager, timeout(100)).grantRuntimePermission(eq(packageName),
- eq(permissionName), any())
+ verify(packageManager, timeout(100))
+ .grantRuntimePermission(eq(packageName), eq(permissionName), any())
}
private fun verifyNotGranted(packageName: String, permissionName: String) {
- verify(packageManager, never()).grantRuntimePermission(eq(packageName),
- eq(permissionName), any())
+ verify(packageManager, never())
+ .grantRuntimePermission(eq(packageName), eq(permissionName), any())
}
@Test
fun restrictedPermissionsOfPreinstalledPackagesGetWhiteListed() {
setInitialDatabaseVersion(LATEST_VERSION)
- setPackages(
- PreinstalledPackage(TEST_PKG_NAME,
- Permission(SEND_SMS)
- )
- )
+ setPackages(PreinstalledPackage(TEST_PKG_NAME, Permission(SEND_SMS)))
upgradeIfNeeded()
@@ -293,11 +303,7 @@ class RuntimePermissionsUpgradeControllerTest {
fun nonRestrictedPermissionsOfPreinstalledPackagesDoNotGetWhiteListed() {
setInitialDatabaseVersion(LATEST_VERSION)
- setPackages(
- PreinstalledPackage(TEST_PKG_NAME,
- Permission(ACCESS_FINE_LOCATION)
- )
- )
+ setPackages(PreinstalledPackage(TEST_PKG_NAME, Permission(ACCESS_FINE_LOCATION)))
upgradeIfNeeded()
@@ -307,11 +313,7 @@ class RuntimePermissionsUpgradeControllerTest {
@Test
fun restrictedPermissionsOfNonPreinstalledPackagesDoNotGetWhiteListed() {
setInitialDatabaseVersion(LATEST_VERSION)
- setPackages(
- Package(TEST_PKG_NAME,
- Permission(SEND_SMS)
- )
- )
+ setPackages(Package(TEST_PKG_NAME, Permission(SEND_SMS)))
upgradeIfNeeded()
@@ -321,12 +323,7 @@ class RuntimePermissionsUpgradeControllerTest {
@Test
fun smsAndCallLogGetsWhitelistedWhenInitialVersionIs0() {
setInitialDatabaseVersion(0)
- setPackages(
- Package(TEST_PKG_NAME,
- Permission(SEND_SMS),
- Permission(READ_CALL_LOG)
- )
- )
+ setPackages(Package(TEST_PKG_NAME, Permission(SEND_SMS), Permission(READ_CALL_LOG)))
upgradeIfNeeded()
@@ -337,12 +334,7 @@ class RuntimePermissionsUpgradeControllerTest {
@Test
fun smsAndCallLogGDoesNotGetWhitelistedWhenInitialVersionIs1() {
setInitialDatabaseVersion(1)
- setPackages(
- Package(TEST_PKG_NAME,
- Permission(SEND_SMS),
- Permission(READ_CALL_LOG)
- )
- )
+ setPackages(Package(TEST_PKG_NAME, Permission(SEND_SMS), Permission(READ_CALL_LOG)))
upgradeIfNeeded()
@@ -353,11 +345,7 @@ class RuntimePermissionsUpgradeControllerTest {
@Test
fun backgroundLocationGetsWhitelistedWhenInitialVersionIs3() {
setInitialDatabaseVersion(3)
- setPackages(
- Package(TEST_PKG_NAME,
- Permission(ACCESS_BACKGROUND_LOCATION)
- )
- )
+ setPackages(Package(TEST_PKG_NAME, Permission(ACCESS_BACKGROUND_LOCATION)))
upgradeIfNeeded()
@@ -367,11 +355,7 @@ class RuntimePermissionsUpgradeControllerTest {
@Test
fun backgroundLocationGetsWhitelistedWhenInitialVersionIs4() {
setInitialDatabaseVersion(4)
- setPackages(
- Package(TEST_PKG_NAME,
- Permission(ACCESS_BACKGROUND_LOCATION)
- )
- )
+ setPackages(Package(TEST_PKG_NAME, Permission(ACCESS_BACKGROUND_LOCATION)))
upgradeIfNeeded()
@@ -381,11 +365,7 @@ class RuntimePermissionsUpgradeControllerTest {
@Test
fun storageGetsWhitelistedWhenInitialVersionIs5() {
setInitialDatabaseVersion(5)
- setPackages(
- Package(TEST_PKG_NAME,
- Permission(READ_EXTERNAL_STORAGE)
- )
- )
+ setPackages(Package(TEST_PKG_NAME, Permission(READ_EXTERNAL_STORAGE)))
upgradeIfNeeded()
@@ -395,11 +375,7 @@ class RuntimePermissionsUpgradeControllerTest {
@Test
fun storageGetsWhitelistedWhenInitialVersionIs6() {
setInitialDatabaseVersion(6)
- setPackages(
- Package(TEST_PKG_NAME,
- Permission(READ_EXTERNAL_STORAGE)
- )
- )
+ setPackages(Package(TEST_PKG_NAME, Permission(READ_EXTERNAL_STORAGE)))
upgradeIfNeeded()
@@ -410,7 +386,8 @@ class RuntimePermissionsUpgradeControllerTest {
fun locationGetsExpandedWhenUpgradingFromP() {
setInitialDatabaseVersion(-1)
setPackages(
- Package(TEST_PKG_NAME,
+ Package(
+ TEST_PKG_NAME,
Permission(ACCESS_FINE_LOCATION, isGranted = true),
Permission(ACCESS_BACKGROUND_LOCATION)
)
@@ -425,7 +402,8 @@ class RuntimePermissionsUpgradeControllerTest {
fun locationDoesNotGetExpandedWhenNotUpgradingFromP() {
setInitialDatabaseVersion(0)
setPackages(
- Package(TEST_PKG_NAME,
+ Package(
+ TEST_PKG_NAME,
Permission(ACCESS_FINE_LOCATION, isGranted = true),
Permission(ACCESS_BACKGROUND_LOCATION)
)
@@ -440,7 +418,8 @@ class RuntimePermissionsUpgradeControllerTest {
fun locationDoesNotGetExpandedWhenUpgradingFromPWhenForegroundPermissionIsDenied() {
setInitialDatabaseVersion(-1)
setPackages(
- Package(TEST_PKG_NAME,
+ Package(
+ TEST_PKG_NAME,
Permission(ACCESS_FINE_LOCATION),
Permission(ACCESS_BACKGROUND_LOCATION)
)
@@ -455,9 +434,13 @@ class RuntimePermissionsUpgradeControllerTest {
fun storageGetsExpandedWhenVersionIs7() {
setInitialDatabaseVersion(7)
setPackages(
- Package(TEST_PKG_NAME,
- Permission(READ_EXTERNAL_STORAGE, isGranted = true,
- flags = FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT),
+ Package(
+ TEST_PKG_NAME,
+ Permission(
+ READ_EXTERNAL_STORAGE,
+ isGranted = true,
+ flags = FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
+ ),
Permission(ACCESS_MEDIA_LOCATION)
)
)
@@ -471,9 +454,13 @@ class RuntimePermissionsUpgradeControllerTest {
fun storageDoesNotGetExpandedWhenVersionIs8() {
setInitialDatabaseVersion(8)
setPackages(
- Package(TEST_PKG_NAME,
- Permission(READ_EXTERNAL_STORAGE, isGranted = true,
- flags = FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT),
+ Package(
+ TEST_PKG_NAME,
+ Permission(
+ READ_EXTERNAL_STORAGE,
+ isGranted = true,
+ flags = FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
+ ),
Permission(ACCESS_MEDIA_LOCATION)
)
)
@@ -487,9 +474,12 @@ class RuntimePermissionsUpgradeControllerTest {
fun storageDoesNotGetExpandedWhenDenied() {
setInitialDatabaseVersion(7)
setPackages(
- Package(TEST_PKG_NAME,
- Permission(READ_EXTERNAL_STORAGE,
- flags = FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT),
+ Package(
+ TEST_PKG_NAME,
+ Permission(
+ READ_EXTERNAL_STORAGE,
+ flags = FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
+ ),
Permission(ACCESS_MEDIA_LOCATION)
)
)
@@ -503,9 +493,13 @@ class RuntimePermissionsUpgradeControllerTest {
fun storageDoesNotGetExpandedWhenNewUser() {
setInitialDatabaseVersion(0)
setPackages(
- Package(TEST_PKG_NAME,
- Permission(READ_EXTERNAL_STORAGE, isGranted = true,
- flags = FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT),
+ Package(
+ TEST_PKG_NAME,
+ Permission(
+ READ_EXTERNAL_STORAGE,
+ isGranted = true,
+ flags = FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
+ ),
Permission(ACCESS_MEDIA_LOCATION)
)
)
@@ -526,13 +520,23 @@ class RuntimePermissionsUpgradeControllerTest {
whenever(packageManager.isDeviceUpgrading).thenReturn(true)
setInitialDatabaseVersion(9)
setPackages(
- Package(TEST_PKG_NAME,
- Permission(READ_EXTERNAL_STORAGE, isGranted = true,
- flags = FLAG_PERMISSION_USER_SET),
- Permission(WRITE_EXTERNAL_STORAGE, isGranted = true,
- flags = FLAG_PERMISSION_USER_SET),
- Permission(ACCESS_MEDIA_LOCATION, isGranted = true,
- flags = FLAG_PERMISSION_USER_SET),
+ Package(
+ TEST_PKG_NAME,
+ Permission(
+ READ_EXTERNAL_STORAGE,
+ isGranted = true,
+ flags = FLAG_PERMISSION_USER_SET
+ ),
+ Permission(
+ WRITE_EXTERNAL_STORAGE,
+ isGranted = true,
+ flags = FLAG_PERMISSION_USER_SET
+ ),
+ Permission(
+ ACCESS_MEDIA_LOCATION,
+ isGranted = true,
+ flags = FLAG_PERMISSION_USER_SET
+ ),
Permission(READ_MEDIA_AUDIO, isGranted = false),
Permission(READ_MEDIA_VIDEO, isGranted = false),
Permission(READ_MEDIA_IMAGES, isGranted = false),
@@ -547,6 +551,66 @@ class RuntimePermissionsUpgradeControllerTest {
verifyGranted(TEST_PKG_NAME, READ_MEDIA_IMAGES)
}
+ @Test
+ fun userSelectedGrantedIfReadMediaVisualGrantedWhenVersionIs10() {
+ Assume.assumeTrue(SdkLevel.isAtLeastU())
+ whenever(packageManager.isDeviceUpgrading).thenReturn(true)
+ setInitialDatabaseVersion(10)
+ setPackages(
+ Package(
+ TEST_PKG_NAME,
+ Permission(READ_MEDIA_VIDEO, isGranted = true, flags = FLAG_PERMISSION_USER_SET),
+ Permission(READ_MEDIA_IMAGES, isGranted = true, flags = FLAG_PERMISSION_USER_SET),
+ Permission(READ_MEDIA_VISUAL_USER_SELECTED, isGranted = false),
+ targetSdkVersion = 33
+ )
+ )
+
+ upgradeIfNeeded()
+
+ verifyGranted(TEST_PKG_NAME, READ_MEDIA_VISUAL_USER_SELECTED)
+ }
+
+ @Test
+ fun userSelectedNotGrantedIfDeviceNotUpgradingWhenVersionIs10() {
+ Assume.assumeTrue(SdkLevel.isAtLeastU())
+ whenever(packageManager.isDeviceUpgrading).thenReturn(false)
+ setInitialDatabaseVersion(10)
+ setPackages(
+ Package(
+ TEST_PKG_NAME,
+ Permission(READ_MEDIA_VIDEO, isGranted = true, flags = FLAG_PERMISSION_USER_SET),
+ Permission(READ_MEDIA_IMAGES, isGranted = true, flags = FLAG_PERMISSION_USER_SET),
+ Permission(READ_MEDIA_VISUAL_USER_SELECTED, isGranted = false),
+ targetSdkVersion = 33
+ )
+ )
+
+ upgradeIfNeeded()
+
+ verifyNotGranted(TEST_PKG_NAME, READ_MEDIA_VISUAL_USER_SELECTED)
+ }
+
+ @Test
+ fun userSelectedNotGrantedIfReadMediaVisualNotGrantedWhenVersionIs10() {
+ Assume.assumeTrue(SdkLevel.isAtLeastU())
+ whenever(packageManager.isDeviceUpgrading).thenReturn(false)
+ setInitialDatabaseVersion(10)
+ setPackages(
+ Package(
+ TEST_PKG_NAME,
+ Permission(READ_MEDIA_VIDEO, isGranted = false, flags = FLAG_PERMISSION_USER_SET),
+ Permission(READ_MEDIA_IMAGES, isGranted = false, flags = FLAG_PERMISSION_USER_SET),
+ Permission(READ_MEDIA_VISUAL_USER_SELECTED, isGranted = false),
+ targetSdkVersion = 33
+ )
+ )
+
+ upgradeIfNeeded()
+
+ verifyNotGranted(TEST_PKG_NAME, READ_MEDIA_VISUAL_USER_SELECTED)
+ }
+
@After
fun resetSystem() {
// Send low memory notifications for all data repositories which will clear cached data
@@ -575,12 +639,9 @@ class RuntimePermissionsUpgradeControllerTest {
) : this(name, permission.toList(), isPreinstalled, targetSdkVersion)
}
- private class PreinstalledPackage(
- name: String,
- permissions: List<Permission> = emptyList()
- ) : Package(name, permissions, true) {
- constructor(name: String, vararg permission: Permission) :
- this(name, permission.toList())
+ private class PreinstalledPackage(name: String, permissions: List<Permission> = emptyList()) :
+ Package(name, permissions, true) {
+ constructor(name: String, vararg permission: Permission) : this(name, permission.toList())
}
private fun <R> runWithShellPermissionIdentity(block: () -> R): R {
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/v33/PermissionDecisionStorageImplTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/v33/PermissionDecisionStorageImplTest.kt
index de8e3f5ef..fbbc07837 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/v33/PermissionDecisionStorageImplTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/v33/PermissionDecisionStorageImplTest.kt
@@ -27,6 +27,10 @@ import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.data.v33.PermissionDecision
import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl
import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.util.Date
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@@ -37,10 +41,6 @@ import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-import java.io.ByteArrayInputStream
-import java.io.ByteArrayOutputStream
-import java.io.File
-import java.util.Date
@RunWith(AndroidJUnit4::class)
class PermissionDecisionStorageImplTest {
@@ -53,13 +53,12 @@ class PermissionDecisionStorageImplTest {
private val jan12020 = Date(2020, 0, 1).time
- private val mapLocationGrant = PermissionDecision(
- MAP_PACKAGE_NAME, jan12020, "location", /* isGranted */ true)
- private val parkingLocationGrant = PermissionDecision(
- "package.test.parking", jan12020, "location", /* isGranted */ false)
+ private val mapLocationGrant =
+ PermissionDecision(MAP_PACKAGE_NAME, jan12020, "location", /* isGranted */ true)
+ private val parkingLocationGrant =
+ PermissionDecision("package.test.parking", jan12020, "location", /* isGranted */ false)
- @Mock
- lateinit var jobScheduler: JobScheduler
+ @Mock lateinit var jobScheduler: JobScheduler
private lateinit var context: Context
private lateinit var storage: PermissionDecisionStorageImpl
@@ -69,10 +68,12 @@ class PermissionDecisionStorageImplTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(DeviceConfig::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(DeviceConfig::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
Mockito.`when`(PermissionControllerApplication.get()).thenReturn(application)
context = ApplicationProvider.getApplicationContext()
filesDir = context.cacheDir
@@ -102,8 +103,8 @@ class PermissionDecisionStorageImplTest {
@Test
fun serialize_roundsTimeDownToDate() {
- val laterInTheDayGrant = mapLocationGrant.copy(
- eventTime = (mapLocationGrant.eventTime + FIVE_HOURS_MS))
+ val laterInTheDayGrant =
+ mapLocationGrant.copy(eventTime = (mapLocationGrant.eventTime + FIVE_HOURS_MS))
val outStream = ByteArrayOutputStream()
storage.serialize(outStream, listOf(laterInTheDayGrant))
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/handheld/v31/DashboardUtilsTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/handheld/v31/DashboardUtilsTest.kt
index 776e0719c..136162040 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/handheld/v31/DashboardUtilsTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/handheld/v31/DashboardUtilsTest.kt
@@ -21,189 +21,123 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr
import com.android.permissioncontroller.permission.ui.handheld.v31.getTimeDiffStr
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.TimeUnit
import org.junit.Test
import org.junit.runner.RunWith
-import java.util.concurrent.TimeUnit
-/**
- * A suite of unit tests to test the permission dashboard utils.
- */
+/** A suite of unit tests to test the permission dashboard utils. */
@RunWith(AndroidJUnit4::class)
class DashboardUtilsTest {
@Test
fun getTimeDiffStr_durationSecondsOne() {
val duration: Long = TimeUnit.SECONDS.toMillis(1L)
- assertThat(
- getTimeDiffStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("1 second")
+ assertThat(getTimeDiffStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("1 second")
}
@Test
fun getTimeDiffStr_durationSecondsOther() {
val duration: Long = TimeUnit.SECONDS.toMillis(59L)
- assertThat(
- getTimeDiffStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("59 seconds")
+ assertThat(getTimeDiffStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("59 seconds")
}
@Test
fun getTimeDiffStr_durationMinutesOne() {
val duration: Long = TimeUnit.MINUTES.toMillis(1L)
- assertThat(
- getTimeDiffStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("1 minute")
+ assertThat(getTimeDiffStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("1 minute")
}
@Test
fun getTimeDiffStr_durationMinutesOther() {
val duration: Long = TimeUnit.MINUTES.toMillis(59L)
- assertThat(
- getTimeDiffStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("59 minutes")
+ assertThat(getTimeDiffStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("59 minutes")
}
@Test
fun getTimeDiffStr_durationHoursOne() {
val duration: Long = TimeUnit.HOURS.toMillis(1L)
- assertThat(
- getTimeDiffStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("1 hour")
+ assertThat(getTimeDiffStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("1 hour")
}
@Test
fun getTimeDiffStr_durationHoursOther() {
val duration: Long = TimeUnit.HOURS.toMillis(23L)
- assertThat(
- getTimeDiffStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("23 hours")
+ assertThat(getTimeDiffStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("23 hours")
}
@Test
fun getTimeDiffStr_durationDaysOne() {
val duration: Long = TimeUnit.DAYS.toMillis(1L)
- assertThat(
- getTimeDiffStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("1 day")
+ assertThat(getTimeDiffStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("1 day")
}
@Test
fun getTimeDiffStr_durationDaysOther() {
val duration: Long = TimeUnit.DAYS.toMillis(2L)
- assertThat(
- getTimeDiffStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("2 days")
+ assertThat(getTimeDiffStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("2 days")
}
@Test
fun getDurationUsedStr_durationSecondsOne() {
val duration: Long = TimeUnit.SECONDS.toMillis(1L)
- assertThat(
- getDurationUsedStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("1 sec")
+ assertThat(getDurationUsedStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("1 sec")
}
@Test
fun getDurationUsedStr_durationSecondsOther() {
val duration: Long = TimeUnit.SECONDS.toMillis(59L)
- assertThat(
- getDurationUsedStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("59 secs")
+ assertThat(getDurationUsedStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("59 secs")
}
@Test
fun getDurationUsedStr_durationMinutesOne() {
val duration: Long = TimeUnit.MINUTES.toMillis(1L)
- assertThat(
- getDurationUsedStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("1 min")
+ assertThat(getDurationUsedStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("1 min")
}
@Test
fun getDurationUsedStr_durationMinutesOther() {
val duration: Long = TimeUnit.MINUTES.toMillis(59L)
- assertThat(
- getDurationUsedStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("59 mins")
+ assertThat(getDurationUsedStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("59 mins")
}
@Test
fun getDurationUsedStr_durationHoursOne() {
val duration: Long = TimeUnit.HOURS.toMillis(1L)
- assertThat(
- getDurationUsedStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("1 hour")
+ assertThat(getDurationUsedStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("1 hour")
}
@Test
fun getDurationUsedStr_durationHoursOther() {
val duration: Long = TimeUnit.HOURS.toMillis(23L)
- assertThat(
- getDurationUsedStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("23 hours")
+ assertThat(getDurationUsedStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("23 hours")
}
@Test
fun getDurationUsedStr_durationDaysOne() {
val duration: Long = TimeUnit.DAYS.toMillis(1L)
- assertThat(
- getDurationUsedStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("1 day")
+ assertThat(getDurationUsedStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("1 day")
}
@Test
fun getDurationUsedStr_durationDaysOther() {
val duration: Long = TimeUnit.DAYS.toMillis(2L)
- assertThat(
- getDurationUsedStr(
- ApplicationProvider.getApplicationContext(),
- duration
- )
- ).isEqualTo("2 days")
- }
-} \ No newline at end of file
+ assertThat(getDurationUsedStr(ApplicationProvider.getApplicationContext(), duration))
+ .isEqualTo("2 days")
+ }
+}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt
index 55aa40e50..0e8890df5 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt
@@ -47,26 +47,18 @@ import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-/**
- * Unit tests for [ReviewPermissionsViewModel]
- */
+/** Unit tests for [ReviewPermissionsViewModel] */
@RunWith(AndroidJUnit4::class)
class ReviewPermissionsViewModelTest {
private val testPackageName = "test.package"
- @Mock
- private lateinit var application: PermissionControllerApplication
- @Mock
- private lateinit var permGroup: LightAppPermGroup
- @Mock
- private lateinit var foregroundSubGroup: LightAppPermGroup.AppPermSubGroup
- @Mock
- private lateinit var backgroundSubGroup: LightAppPermGroup.AppPermSubGroup
- @Mock
- private lateinit var admin: RestrictedLockUtils.EnforcedAdmin
- @Mock
- private lateinit var packageManager: PackageManager
+ @Mock private lateinit var application: PermissionControllerApplication
+ @Mock private lateinit var permGroup: LightAppPermGroup
+ @Mock private lateinit var foregroundSubGroup: LightAppPermGroup.AppPermSubGroup
+ @Mock private lateinit var backgroundSubGroup: LightAppPermGroup.AppPermSubGroup
+ @Mock private lateinit var admin: RestrictedLockUtils.EnforcedAdmin
+ @Mock private lateinit var packageManager: PackageManager
private lateinit var mockitoSession: MockitoSession
private lateinit var context: Context
@@ -76,10 +68,12 @@ class ReviewPermissionsViewModelTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(Utils::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(Utils::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
context = ApplicationProvider.getApplicationContext()
val userHandle: UserHandle = android.os.Process.myUserHandle()
@@ -114,9 +108,9 @@ class ReviewPermissionsViewModelTest {
val summary = model.getSummaryForIndividuallyControlledPermGroup(permGroup)
assertEquals(
- ReviewPermissionsViewModel.PermissionSummary(
- SummaryMessage.REVOKED_COUNT, false, 1
- ), summary)
+ ReviewPermissionsViewModel.PermissionSummary(SummaryMessage.REVOKED_COUNT, false, 1),
+ summary
+ )
}
@Test
@@ -124,48 +118,78 @@ class ReviewPermissionsViewModelTest {
whenever(permGroup.isGranted).thenReturn(true)
whenever(foregroundSubGroup.isPolicyFixed).thenReturn(true)
- val summary = model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_FOREGROUND,
- permGroup, context)
+ val summary =
+ model.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_FOREGROUND,
+ permGroup,
+ context
+ )
assertEquals(SummaryMessage.ENABLED_BY_POLICY_FOREGROUND_ONLY.toPermSummary(), summary)
val spyViewModel = spy(model)
doReturn(admin).`when`(spyViewModel).getAdmin(context, permGroup)
- val summaryAdmin = spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
- PERMISSION_FOREGROUND, permGroup, context)
- assertEquals(SummaryMessage.ENABLED_BY_ADMIN_FOREGROUND_ONLY.toPermSummary(true),
- summaryAdmin)
+ val summaryAdmin =
+ spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_FOREGROUND,
+ permGroup,
+ context
+ )
+ assertEquals(
+ SummaryMessage.ENABLED_BY_ADMIN_FOREGROUND_ONLY.toPermSummary(true),
+ summaryAdmin
+ )
}
@Test
fun getSummary_backgroundFixedPolicy_foregroundRequested() {
whenever(backgroundSubGroup.isPolicyFixed).thenReturn(true)
- val summary = model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_FOREGROUND,
- permGroup, context)
+ val summary =
+ model.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_FOREGROUND,
+ permGroup,
+ context
+ )
assertEquals(SummaryMessage.DISABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary(), summary)
val spyViewModel = spy(model)
doReturn(admin).`when`(spyViewModel).getAdmin(context, permGroup)
- val summaryAdmin = spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
- PERMISSION_FOREGROUND, permGroup, context)
- assertEquals(SummaryMessage.DISABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true),
- summaryAdmin)
+ val summaryAdmin =
+ spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_FOREGROUND,
+ permGroup,
+ context
+ )
+ assertEquals(
+ SummaryMessage.DISABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true),
+ summaryAdmin
+ )
}
@Test
fun getSummary_backgroundFixedPolicy() {
whenever(backgroundSubGroup.isPolicyFixed).thenReturn(true)
- val summary = model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BACKGROUND,
- permGroup, context)
+ val summary =
+ model.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_BACKGROUND,
+ permGroup,
+ context
+ )
assertEquals(SummaryMessage.ENABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary(), summary)
val spyViewModel = spy(model)
doReturn(admin).`when`(spyViewModel).getAdmin(context, permGroup)
- val summaryAdmin = spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
- PERMISSION_BACKGROUND, permGroup, context)
- assertEquals(SummaryMessage.ENABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true),
- summaryAdmin)
+ val summaryAdmin =
+ spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_BACKGROUND,
+ permGroup,
+ context
+ )
+ assertEquals(
+ SummaryMessage.ENABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true),
+ summaryAdmin
+ )
}
@Test
@@ -173,14 +197,22 @@ class ReviewPermissionsViewModelTest {
whenever(permGroup.isPolicyFullyFixed).thenReturn(true)
whenever(permGroup.hasBackgroundGroup).thenReturn(true)
- val summary = model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_FOREGROUND,
- permGroup, context)
+ val summary =
+ model.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_FOREGROUND,
+ permGroup,
+ context
+ )
assertEquals(SummaryMessage.ENABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary(), summary)
val spyViewModel = spy(model)
doReturn(admin).`when`(spyViewModel).getAdmin(context, permGroup)
- val summaryAdmin = spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
- PERMISSION_FOREGROUND, permGroup, context)
+ val summaryAdmin =
+ spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_FOREGROUND,
+ permGroup,
+ context
+ )
assertEquals(SummaryMessage.ENABLED_BY_ADMIN_FOREGROUND_ONLY.toPermSummary(), summaryAdmin)
}
@@ -189,14 +221,22 @@ class ReviewPermissionsViewModelTest {
whenever(permGroup.isPolicyFullyFixed).thenReturn(true)
whenever(permGroup.hasBackgroundGroup).thenReturn(true)
- val summary = model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BACKGROUND,
- permGroup, context)
+ val summary =
+ model.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_BACKGROUND,
+ permGroup,
+ context
+ )
assertEquals(SummaryMessage.ENFORCED_BY_POLICY.toPermSummary(), summary)
val spyViewModel = spy(model)
doReturn(admin).`when`(spyViewModel).getAdmin(context, permGroup)
- val summaryAdmin = spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
- PERMISSION_BACKGROUND, permGroup, context)
+ val summaryAdmin =
+ spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_BACKGROUND,
+ permGroup,
+ context
+ )
assertEquals(SummaryMessage.ENABLED_BY_ADMIN.toPermSummary(), summaryAdmin)
}
@@ -204,14 +244,18 @@ class ReviewPermissionsViewModelTest {
fun getSummary_fullyFixedPolicy_hasNoBackgroundGroup() {
whenever(permGroup.isPolicyFullyFixed).thenReturn(true)
- val summary = model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BOTH,
- permGroup, context)
+ val summary =
+ model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BOTH, permGroup, context)
assertEquals(SummaryMessage.ENFORCED_BY_POLICY.toPermSummary(), summary)
val spyViewModel = spy(model)
doReturn(admin).`when`(spyViewModel).getAdmin(context, permGroup)
- val summaryAdmin = spyViewModel.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BOTH,
- permGroup, context)
+ val summaryAdmin =
+ spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_BOTH,
+ permGroup,
+ context
+ )
assertEquals(SummaryMessage.ENABLED_BY_ADMIN.toPermSummary(), summaryAdmin)
}
@@ -220,14 +264,18 @@ class ReviewPermissionsViewModelTest {
whenever(foregroundSubGroup.isPolicyFixed).thenReturn(true)
whenever(permGroup.isGranted).thenReturn(false)
- val summary = model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BOTH,
- permGroup, context)
+ val summary =
+ model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BOTH, permGroup, context)
assertEquals(SummaryMessage.ENFORCED_BY_POLICY.toPermSummary(), summary)
val spyViewModel = spy(model)
doReturn(admin).`when`(spyViewModel).getAdmin(context, permGroup)
- val adminSummary = spyViewModel.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BOTH,
- permGroup, context)
+ val adminSummary =
+ spyViewModel.getSummaryForFixedByPolicyPermissionGroup(
+ PERMISSION_BOTH,
+ permGroup,
+ context
+ )
assertEquals(SummaryMessage.DISABLED_BY_ADMIN.toPermSummary(), adminSummary)
}
@@ -235,8 +283,8 @@ class ReviewPermissionsViewModelTest {
fun getSummary_systemFixedPolicy() {
whenever(permGroup.isSystemFixed).thenReturn(true)
- val summary = model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BOTH,
- permGroup, context)
+ val summary =
+ model.getSummaryForFixedByPolicyPermissionGroup(PERMISSION_BOTH, permGroup, context)
assertEquals(SummaryMessage.ENABLED_SYSTEM_FIXED.toPermSummary(), summary)
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt
index c688273c6..b1e309628 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt
@@ -65,7 +65,8 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
-private const val PERMISSION_CONTROLLER_CHANGED_FLAG_MASK = FLAG_PERMISSION_USER_SET or
+private const val PERMISSION_CONTROLLER_CHANGED_FLAG_MASK =
+ FLAG_PERMISSION_USER_SET or
FLAG_PERMISSION_USER_FIXED or
FLAG_PERMISSION_ONE_TIME or
FLAG_PERMISSION_REVOKED_COMPAT or
@@ -102,8 +103,7 @@ class GrantRevokeTests {
}
}
- @Mock
- val app: Application = mock(Application::class.java)
+ @Mock val app: Application = mock(Application::class.java)
/**
* Create a mock Application object, with a mock packageManager, AppOpsManager, and
@@ -120,11 +120,11 @@ class GrantRevokeTests {
.thenReturn(-1)
`when`(app.getSystemService(AppOpsManager::class.java)).thenReturn(aom)
- `when`(app.getSystemService(ActivityManager::class.java)).thenReturn(
- mock(ActivityManager::class.java))
+ `when`(app.getSystemService(ActivityManager::class.java))
+ .thenReturn(mock(ActivityManager::class.java))
- `when`(app.getSystemService(PermissionManager::class.java)).thenReturn(
- mock(PermissionManager::class.java))
+ `when`(app.getSystemService(PermissionManager::class.java))
+ .thenReturn(mock(PermissionManager::class.java))
}
/**
@@ -143,19 +143,34 @@ class GrantRevokeTests {
val permFlags = mutableListOf<Int>()
for ((permName, isGranted) in perms) {
permNames.add(permName)
- permFlags.add(if (isGranted) {
- PERMISSION_GRANTED
- } else {
- PERMISSION_DENIED
- })
+ permFlags.add(
+ if (isGranted) {
+ PERMISSION_GRANTED
+ } else {
+ PERMISSION_DENIED
+ }
+ )
}
- return LightPackageInfo(TEST_PACKAGE_NAME, listOf(), permNames, permFlags, TEST_UID,
- if (isPreMApp) {
- Build.VERSION_CODES.LOLLIPOP
- } else {
- Build.VERSION_CODES.R
- }, isInstantApp, isInstantApp, 0, 0L, 0L, false, emptyMap())
+ return LightPackageInfo(
+ TEST_PACKAGE_NAME,
+ listOf(),
+ permNames,
+ permFlags,
+ TEST_UID,
+ if (isPreMApp) {
+ Build.VERSION_CODES.LOLLIPOP
+ } else {
+ Build.VERSION_CODES.R
+ },
+ isInstantApp,
+ isInstantApp,
+ 0,
+ 0L,
+ 0L,
+ false,
+ emptyMap()
+ )
}
/**
@@ -164,13 +179,13 @@ class GrantRevokeTests {
* @param pkg Package requesting the permission
* @param permName The name of the permission
* @param granted Whether the permission is granted (should be false if the permission is compat
- * revoked)
+ * revoked)
* @param backgroundPerm The name of this permission's background permission, if there is one
* @param foregroundPerms The names of this permission's foreground permissions, if there are
- * any
+ * any
* @param flags The system permission flags of this permission
* @param permInfoProtectionFlags The flags that the PermissionInfo object has (accessed by
- * PermissionInfo.getProtectionFlags)
+ * PermissionInfo.getProtectionFlags)
*/
private fun createMockPerm(
pkgInfo: LightPackageInfo,
@@ -180,12 +195,25 @@ class GrantRevokeTests {
flags: Int = NO_FLAGS,
permInfoProtectionFlags: Int = 0
): LightPermission {
- val permInfo = LightPermInfo(permName, TEST_PACKAGE_NAME, PERM_GROUP_NAME, backgroundPerm,
- PermissionInfo.PROTECTION_DANGEROUS, permInfoProtectionFlags, 0,
- pkgInfo.appFlags and ApplicationInfo.FLAG_SYSTEM != 0)
- return LightPermission(pkgInfo, permInfo,
- pkgInfo.requestedPermissionsFlags[pkgInfo.requestedPermissions.indexOf(permName)]
- == PERMISSION_GRANTED, flags, foregroundPerms)
+ val permInfo =
+ LightPermInfo(
+ permName,
+ TEST_PACKAGE_NAME,
+ PERM_GROUP_NAME,
+ backgroundPerm,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permInfoProtectionFlags,
+ 0,
+ pkgInfo.appFlags and ApplicationInfo.FLAG_SYSTEM != 0
+ )
+ return LightPermission(
+ pkgInfo,
+ permInfo,
+ pkgInfo.requestedPermissionsFlags[pkgInfo.requestedPermissions.indexOf(permName)] ==
+ PERMISSION_GRANTED,
+ flags,
+ foregroundPerms
+ )
}
/**
@@ -203,12 +231,11 @@ class GrantRevokeTests {
}
/**
- * Create a list of strings which usefully states which flags are set in a group of flags.
- * Only checks for flags relevant to granting and revoking (so, for instance, policy fixed is
- * not checked).
+ * Create a list of strings which usefully states which flags are set in a group of flags. Only
+ * checks for flags relevant to granting and revoking (so, for instance, policy fixed is not
+ * checked).
*
* @param flags The flags to check
- *
* @return a list of strings, representing which flags have been set
*/
private fun flagsToString(flags: Int): List<String> {
@@ -253,28 +280,32 @@ class GrantRevokeTests {
val flags = state.second
assertWithMessage("permission $permName grant state incorrect")
- .that(perms[permName]?.isGranted).isEqualTo(granted)
+ .that(perms[permName]?.isGranted)
+ .isEqualTo(granted)
val actualFlags = perms[permName]!!.flags
- assertWithMessage("permission $permName flags incorrect, expected" +
- "${flagsToString(flags)}; got ${flagsToString(actualFlags)}")
- .that(perms[permName]?.flags).isEqualTo(flags)
+ assertWithMessage(
+ "permission $permName flags incorrect, expected" +
+ "${flagsToString(flags)}; got ${flagsToString(actualFlags)}"
+ )
+ .that(perms[permName]?.flags)
+ .isEqualTo(flags)
}
}
/**
- * Verify that permission state was propagated to the system. Verify that grant or revoke
- * were called, if applicable, or verify they weren't. Verify that we have set flags
- * correctly, if applicable, or verify flags were not set.
+ * Verify that permission state was propagated to the system. Verify that grant or revoke were
+ * called, if applicable, or verify they weren't. Verify that we have set flags correctly, if
+ * applicable, or verify flags were not set.
*
* @param permName The name of the permission to verify
* @param expectPermChange Whether or not a permission grant or revoke was expected. If false,
- * verify neither grant nor revoke were called
- * @param expectPermGranted If a permission change was expected, verify that the permission
- * was set to granted (if true) or revoked (if false)
+ * verify neither grant nor revoke were called
+ * @param expectPermGranted If a permission change was expected, verify that the permission was
+ * set to granted (if true) or revoked (if false)
* @param expectedFlags The flags that the system should have set the permission to have
- * @param originalFlags The flags the permission originally had. Used to ensure the correct
- * flag mask was used
+ * @param originalFlags The flags the permission originally had. Used to ensure the correct flag
+ * mask was used
*/
private fun verifyPermissionState(
permName: String,
@@ -296,11 +327,23 @@ class GrantRevokeTests {
}
if (expectedFlags != originalFlags) {
- verify(pm).updatePermissionFlags(permName, TEST_PACKAGE_NAME,
- PERMISSION_CONTROLLER_CHANGED_FLAG_MASK, expectedFlags, TEST_USER)
+ verify(pm)
+ .updatePermissionFlags(
+ permName,
+ TEST_PACKAGE_NAME,
+ PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
+ expectedFlags,
+ TEST_USER
+ )
} else {
- verify(pm, never()).updatePermissionFlags(eq(permName), eq(TEST_PACKAGE_NAME), anyInt(),
- anyInt(), eq(TEST_USER))
+ verify(pm, never())
+ .updatePermissionFlags(
+ eq(permName),
+ eq(TEST_PACKAGE_NAME),
+ anyInt(),
+ anyInt(),
+ eq(TEST_USER)
+ )
}
}
@@ -310,7 +353,7 @@ class GrantRevokeTests {
*
* @param appOpName The name of the app op to check
* @param expectAppOpSet Whether an app op change was expected. If false, verify setUidMode was
- * not called
+ * not called
* @param expectedMode If a change was expected, the mode the app op should be set to
*/
private fun verifyAppOpState(
@@ -355,8 +398,12 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_ALLOWED)
verifyAppKillState(shouldBeKilled = false)
@@ -381,17 +428,28 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_ALLOWED)
- verifyPermissionState(permName = FG_PERM_2_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
- verifyAppOpState(appOpName = OP_2_NAME, expectAppOpSet = true,
- expectedMode = MODE_FOREGROUND)
+ verifyPermissionState(
+ permName = FG_PERM_2_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
+ verifyAppOpState(
+ appOpName = OP_2_NAME,
+ expectAppOpSet = true,
+ expectedMode = MODE_FOREGROUND
+ )
verifyAppKillState(shouldBeKilled = false)
- val expectedState = mutableMapOf(FG_PERM_NAME to (true to newFlags),
- FG_PERM_2_NAME to (true to newFlags))
+ val expectedState =
+ mutableMapOf(FG_PERM_NAME to (true to newFlags), FG_PERM_2_NAME to (true to newFlags))
assertGroupPermState(newGroup, expectedState)
}
@@ -410,8 +468,12 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME_NO_APP_OP, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME_NO_APP_OP,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = false)
verifyAppOpState(appOpName = OP_2_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
@@ -436,14 +498,18 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantBackgroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = BG_PERM_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = BG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_ALLOWED)
verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = false)
verifyAppKillState(shouldBeKilled = false)
- val expectedState = mutableMapOf(FG_PERM_NAME to (true to NO_FLAGS),
- BG_PERM_NAME to (true to newFlags))
+ val expectedState =
+ mutableMapOf(FG_PERM_NAME to (true to NO_FLAGS), BG_PERM_NAME to (true to newFlags))
assertGroupPermState(newGroup, expectedState)
}
@@ -464,26 +530,34 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
verifyPermissionState(permName = BG_PERM_NAME, expectPermChange = false)
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_FOREGROUND)
verifyAppKillState(shouldBeKilled = false)
- val expectedState = mutableMapOf(FG_PERM_NAME to (true to newFlags),
- BG_PERM_NAME to (false to NO_FLAGS))
+ val expectedState =
+ mutableMapOf(FG_PERM_NAME to (true to newFlags), BG_PERM_NAME to (false to NO_FLAGS))
assertGroupPermState(newGroup, expectedState)
resetMockAppState()
val newGroup2 = KotlinUtils.grantBackgroundRuntimePermissions(app, newGroup)
- verifyPermissionState(permName = BG_PERM_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = BG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_ALLOWED)
verifyAppKillState(shouldBeKilled = false)
- val expectedState2 = mutableMapOf(FG_PERM_NAME to (true to newFlags),
- BG_PERM_NAME to (true to newFlags))
+ val expectedState2 =
+ mutableMapOf(FG_PERM_NAME to (true to newFlags), BG_PERM_NAME to (true to newFlags))
assertGroupPermState(newGroup2, expectedState2)
}
@@ -497,20 +571,28 @@ class GrantRevokeTests {
val pkg = createMockPackage(mapOf(FG_PERM_NAME to false, BG_PERM_NAME to false))
val perms = mutableMapOf<String, LightPermission>()
val origBgFlags = FLAG_PERMISSION_AUTO_REVOKED
- perms[FG_PERM_NAME] = createMockPerm(
- pkg, FG_PERM_NAME, BG_PERM_NAME, null, FLAG_PERMISSION_AUTO_REVOKED)
- perms[BG_PERM_NAME] = createMockPerm(
- pkg, BG_PERM_NAME, null, listOf(FG_PERM_NAME), origBgFlags)
+ perms[FG_PERM_NAME] =
+ createMockPerm(pkg, FG_PERM_NAME, BG_PERM_NAME, null, FLAG_PERMISSION_AUTO_REVOKED)
+ perms[BG_PERM_NAME] =
+ createMockPerm(pkg, BG_PERM_NAME, null, listOf(FG_PERM_NAME), origBgFlags)
val group = createMockGroup(pkg, perms)
resetMockAppState()
KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
- verifyPermissionState(permName = BG_PERM_NAME, expectPermChange = false,
- expectedFlags = NO_FLAGS, originalFlags = origBgFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
+ verifyPermissionState(
+ permName = BG_PERM_NAME,
+ expectPermChange = false,
+ expectedFlags = NO_FLAGS,
+ originalFlags = origBgFlags
+ )
}
/**
@@ -530,16 +612,24 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_ALLOWED)
- verifyPermissionState(permName = FG_PERM_2_NAME, expectPermChange = false,
- expectedFlags = permFlags, originalFlags = permFlags)
+ verifyPermissionState(
+ permName = FG_PERM_2_NAME,
+ expectPermChange = false,
+ expectedFlags = permFlags,
+ originalFlags = permFlags
+ )
verifyAppOpState(appOpName = OP_2_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
- val expectedState = mutableMapOf(FG_PERM_NAME to (true to newFlags),
- FG_PERM_2_NAME to (false to permFlags))
+ val expectedState =
+ mutableMapOf(FG_PERM_NAME to (true to newFlags), FG_PERM_2_NAME to (false to permFlags))
assertGroupPermState(newGroup, expectedState)
}
@@ -560,25 +650,33 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_FOREGROUND)
verifyAppKillState(shouldBeKilled = false)
- var expectedState = mutableMapOf(FG_PERM_NAME to (true to newFlags),
- BG_PERM_NAME to (false to permFlags))
+ var expectedState =
+ mutableMapOf(FG_PERM_NAME to (true to newFlags), BG_PERM_NAME to (false to permFlags))
assertGroupPermState(newGroup, expectedState)
resetMockAppState()
val newGroup2 = KotlinUtils.grantBackgroundRuntimePermissions(app, newGroup)
- verifyPermissionState(permName = BG_PERM_NAME, expectPermChange = false,
- expectedFlags = permFlags, originalFlags = permFlags)
+ verifyPermissionState(
+ permName = BG_PERM_NAME,
+ expectPermChange = false,
+ expectedFlags = permFlags,
+ originalFlags = permFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
- expectedState = mutableMapOf(FG_PERM_NAME to (true to newFlags),
- BG_PERM_NAME to (false to permFlags))
+ expectedState =
+ mutableMapOf(FG_PERM_NAME to (true to newFlags), BG_PERM_NAME to (false to permFlags))
assertGroupPermState(newGroup2, expectedState)
}
@@ -598,8 +696,12 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = false,
- expectedFlags = newFlags, originalFlags = oldFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = false,
+ expectedFlags = newFlags,
+ originalFlags = oldFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
@@ -622,8 +724,12 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = false,
- expectedFlags = newFlags, originalFlags = oldFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = false,
+ expectedFlags = newFlags,
+ originalFlags = oldFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_ALLOWED)
verifyAppKillState(shouldBeKilled = true)
@@ -645,8 +751,12 @@ class GrantRevokeTests {
resetMockAppState()
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = false,
- expectedFlags = flags, originalFlags = flags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = false,
+ expectedFlags = flags,
+ originalFlags = flags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
@@ -654,9 +764,7 @@ class GrantRevokeTests {
assertGroupPermState(newGroup, expectedState)
}
- /**
- * Test that an instant app cannot have regular (non-instant) permission granted.
- */
+ /** Test that an instant app cannot have regular (non-instant) permission granted. */
@Test
fun cantGrantInstantAppStandardPermTest() {
val pkg = createMockPackage(mapOf(FG_PERM_NAME to false), isInstantApp = true)
@@ -682,8 +790,12 @@ class GrantRevokeTests {
fun cantGrantPreRuntimeAppWithRuntimeOnlyPermTest() {
val pkg = createMockPackage(mapOf(FG_PERM_NAME to false), isPreMApp = true)
val perms = mutableMapOf<String, LightPermission>()
- perms[FG_PERM_NAME] = createMockPerm(pkg, FG_PERM_NAME,
- permInfoProtectionFlags = PROTECTION_FLAG_RUNTIME_ONLY)
+ perms[FG_PERM_NAME] =
+ createMockPerm(
+ pkg,
+ FG_PERM_NAME,
+ permInfoProtectionFlags = PROTECTION_FLAG_RUNTIME_ONLY
+ )
val group = createMockGroup(pkg, perms)
resetMockAppState()
@@ -697,23 +809,25 @@ class GrantRevokeTests {
assertGroupPermState(newGroup, expectedState)
}
- /**
- * Test that an instant package can have an instant permission granted.
- */
+ /** Test that an instant package can have an instant permission granted. */
@Test
fun grantInstantAppInstantPermTest() {
val pkg = createMockPackage(mapOf(FG_PERM_NAME to false), isInstantApp = true)
val perms = mutableMapOf<String, LightPermission>()
- perms[FG_PERM_NAME] = createMockPerm(pkg, FG_PERM_NAME,
- permInfoProtectionFlags = PROTECTION_FLAG_INSTANT)
+ perms[FG_PERM_NAME] =
+ createMockPerm(pkg, FG_PERM_NAME, permInfoProtectionFlags = PROTECTION_FLAG_INSTANT)
val group = createMockGroup(pkg, perms)
resetMockAppState()
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = true, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = true,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_ALLOWED)
verifyAppKillState(shouldBeKilled = false)
@@ -721,9 +835,7 @@ class GrantRevokeTests {
assertGroupPermState(newGroup, expectedState)
}
- /**
- * Test that granting a permission clears the user fixed and review required flags.
- */
+ /** Test that granting a permission clears the user fixed and review required flags. */
@Test
fun grantClearsUserFixedAndReviewRequired() {
val pkg = createMockPackage(mapOf(FG_PERM_NAME to true))
@@ -735,8 +847,12 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = false,
- expectedFlags = newFlags, originalFlags = oldFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = false,
+ expectedFlags = newFlags,
+ originalFlags = oldFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
@@ -744,9 +860,7 @@ class GrantRevokeTests {
assertGroupPermState(newGroup, expectedState)
}
- /**
- * Test revoking one foreground permission. The permission and app op should be revoked.
- */
+ /** Test revoking one foreground permission. The permission and app op should be revoked. */
@Test
fun revokeOnePermTest() {
val pkg = createMockPackage(mapOf(FG_PERM_NAME to true))
@@ -758,8 +872,12 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppKillState(shouldBeKilled = false)
@@ -767,9 +885,7 @@ class GrantRevokeTests {
assertGroupPermState(newGroup, expectedState)
}
- /**
- * Test revoking two foreground permissions. Both permissions and app ops should be revoked.
- */
+ /** Test revoking two foreground permissions. Both permissions and app ops should be revoked. */
@Test
fun revokeTwoPermTest() {
val pkg = createMockPackage(mapOf(FG_PERM_NAME to true, FG_PERM_2_NAME to true))
@@ -782,16 +898,24 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
- verifyPermissionState(permName = FG_PERM_2_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_2_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_2_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppKillState(shouldBeKilled = false)
- val expectedState = mutableMapOf(FG_PERM_NAME to (false to newFlags),
- FG_PERM_2_NAME to (false to newFlags))
+ val expectedState =
+ mutableMapOf(FG_PERM_NAME to (false to newFlags), FG_PERM_2_NAME to (false to newFlags))
assertGroupPermState(newGroup, expectedState)
}
@@ -809,8 +933,12 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME_NO_APP_OP, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME_NO_APP_OP,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = false)
verifyAppOpState(appOpName = OP_2_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
@@ -836,13 +964,17 @@ class GrantRevokeTests {
val newFlags = FLAG_PERMISSION_USER_SET
verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = false)
- verifyPermissionState(permName = BG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = BG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_FOREGROUND)
verifyAppKillState(shouldBeKilled = false)
- val expectedState = mutableMapOf(FG_PERM_NAME to (true to NO_FLAGS),
- BG_PERM_NAME to (false to newFlags))
+ val expectedState =
+ mutableMapOf(FG_PERM_NAME to (true to NO_FLAGS), BG_PERM_NAME to (false to newFlags))
assertGroupPermState(newGroup, expectedState)
}
@@ -863,24 +995,32 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeBackgroundRuntimePermissions(app, group, true)
val newFlags = FLAG_PERMISSION_USER_SET or FLAG_PERMISSION_USER_FIXED
- verifyPermissionState(permName = BG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = BG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_FOREGROUND)
verifyAppKillState(shouldBeKilled = false)
- val expectedState = mutableMapOf(FG_PERM_NAME to (true to NO_FLAGS),
- BG_PERM_NAME to (false to newFlags))
+ val expectedState =
+ mutableMapOf(FG_PERM_NAME to (true to NO_FLAGS), BG_PERM_NAME to (false to newFlags))
assertGroupPermState(newGroup, expectedState)
resetMockAppState()
val newGroup2 = KotlinUtils.revokeForegroundRuntimePermissions(app, newGroup, true)
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppKillState(shouldBeKilled = false)
- val expectedState2 = mutableMapOf(FG_PERM_NAME to (false to newFlags),
- BG_PERM_NAME to (false to newFlags))
+ val expectedState2 =
+ mutableMapOf(FG_PERM_NAME to (false to newFlags), BG_PERM_NAME to (false to newFlags))
assertGroupPermState(newGroup2, expectedState2)
}
@@ -901,15 +1041,19 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyPermissionState(permName = FG_PERM_2_NAME, expectPermChange = false)
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppOpState(appOpName = OP_2_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
- val expectedState = mutableMapOf(FG_PERM_NAME to (false to newFlags),
- FG_PERM_2_NAME to (true to permFlags))
+ val expectedState =
+ mutableMapOf(FG_PERM_NAME to (false to newFlags), FG_PERM_2_NAME to (true to permFlags))
assertGroupPermState(newGroup, expectedState)
}
@@ -930,13 +1074,17 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppKillState(shouldBeKilled = false)
- var expectedState = mutableMapOf(FG_PERM_NAME to (false to newFlags),
- BG_PERM_NAME to (true to permFlags))
+ var expectedState =
+ mutableMapOf(FG_PERM_NAME to (false to newFlags), BG_PERM_NAME to (true to permFlags))
assertGroupPermState(newGroup, expectedState)
resetMockAppState()
@@ -946,14 +1094,14 @@ class GrantRevokeTests {
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
- expectedState = mutableMapOf(FG_PERM_NAME to (false to newFlags),
- BG_PERM_NAME to (true to permFlags))
+ expectedState =
+ mutableMapOf(FG_PERM_NAME to (false to newFlags), BG_PERM_NAME to (true to permFlags))
assertGroupPermState(newGroup2, expectedState)
}
/**
- * Test revoking a one time granted permission. The permission should be revoked, but no
- * longer be one time.
+ * Test revoking a one time granted permission. The permission should be revoked, but no longer
+ * be one time.
*/
@Test
fun revokeOneTimeTest() {
@@ -967,8 +1115,13 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags, originalFlags = oldFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags,
+ originalFlags = oldFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppKillState(shouldBeKilled = false)
@@ -977,8 +1130,8 @@ class GrantRevokeTests {
}
/**
- * Test compat revoking (permission granted, app op denied) permission. The app op
- * should be revoked, while the permission remains granted. The app should also be killed.
+ * Test compat revoking (permission granted, app op denied) permission. The app op should be
+ * revoked, while the permission remains granted. The app should also be killed.
*/
@Test
fun revokePreMAppTest() {
@@ -991,8 +1144,11 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET or FLAG_PERMISSION_REVOKED_COMPAT
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = false,
- expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppKillState(shouldBeKilled = true)
@@ -1038,8 +1194,12 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group, true)
val newFlags = FLAG_PERMISSION_USER_SET or FLAG_PERMISSION_USER_FIXED
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppKillState(shouldBeKilled = false)
@@ -1064,8 +1224,13 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags, originalFlags = oldFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags,
+ originalFlags = oldFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppKillState(shouldBeKilled = false)
@@ -1089,8 +1254,13 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group, true)
val newFlags = oldFlags or FLAG_PERMISSION_USER_FIXED
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = true,
- expectPermGranted = false, expectedFlags = newFlags, originalFlags = oldFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = true,
+ expectPermGranted = false,
+ expectedFlags = newFlags,
+ originalFlags = oldFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = true, expectedMode = MODE_IGNORED)
verifyAppKillState(shouldBeKilled = false)
@@ -1099,8 +1269,8 @@ class GrantRevokeTests {
}
/**
- * Test revoking an already revoked permission, while changing its user fixed state from true
- * to false. The user fixed should update, but the state should stay the same otherwise.
+ * Test revoking an already revoked permission, while changing its user fixed state from true to
+ * false. The user fixed should update, but the state should stay the same otherwise.
*/
@Test
fun changeUserFixedTest() {
@@ -1114,8 +1284,12 @@ class GrantRevokeTests {
val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group)
val newFlags = FLAG_PERMISSION_USER_SET
- verifyPermissionState(permName = FG_PERM_NAME, expectPermChange = false,
- expectedFlags = newFlags, originalFlags = oldFlags)
+ verifyPermissionState(
+ permName = FG_PERM_NAME,
+ expectPermChange = false,
+ expectedFlags = newFlags,
+ originalFlags = oldFlags
+ )
verifyAppOpState(appOpName = OP_NAME, expectAppOpSet = false)
verifyAppKillState(shouldBeKilled = false)
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/StringUtilsTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/StringUtilsTest.kt
index 178f5d56b..02308f750 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/StringUtilsTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/StringUtilsTest.kt
@@ -16,62 +16,64 @@
package com.android.permissioncontroller.tests.mocking.permission.utils
-import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.utils.StringUtils
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.utils.StringUtils
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-/**
- * A suite of unit tests to test the permission dashboard utils.
- */
+/** A suite of unit tests to test the permission dashboard utils. */
@RunWith(AndroidJUnit4::class)
class StringUtilsTest {
@Test
fun getIcuPluralsString_one_noArguments() {
assertThat(
- StringUtils.getIcuPluralsString(
- ApplicationProvider.getApplicationContext(),
- R.string.test_icu_plural,
- 1
+ StringUtils.getIcuPluralsString(
+ ApplicationProvider.getApplicationContext(),
+ R.string.test_icu_plural,
+ 1
+ )
)
- ).isEqualTo("1 test")
+ .isEqualTo("1 test")
}
@Test
fun getIcuPluralsString_other_noArguments() {
assertThat(
- StringUtils.getIcuPluralsString(
- ApplicationProvider.getApplicationContext(),
- R.string.test_icu_plural,
- 2
+ StringUtils.getIcuPluralsString(
+ ApplicationProvider.getApplicationContext(),
+ R.string.test_icu_plural,
+ 2
+ )
)
- ).isEqualTo("2 tests")
+ .isEqualTo("2 tests")
}
@Test
fun getIcuPluralsString_one_additionalArguments() {
assertThat(
- StringUtils.getIcuPluralsString(
- ApplicationProvider.getApplicationContext(),
- R.string.test_icu_plural_with_argument,
- 1,
- "with argument"
+ StringUtils.getIcuPluralsString(
+ ApplicationProvider.getApplicationContext(),
+ R.string.test_icu_plural_with_argument,
+ 1,
+ "with argument"
+ )
)
- ).isEqualTo("1 test with argument")
+ .isEqualTo("1 test with argument")
}
@Test
fun getIcuPluralsString_other_additionalArguments() {
assertThat(
- StringUtils.getIcuPluralsString(
- ApplicationProvider.getApplicationContext(),
- R.string.test_icu_plural_with_argument,
- 2,
- "with argument"
+ StringUtils.getIcuPluralsString(
+ ApplicationProvider.getApplicationContext(),
+ R.string.test_icu_plural_with_argument,
+ 2,
+ "with argument"
+ )
)
- ).isEqualTo("2 tests with argument")
+ .isEqualTo("2 tests with argument")
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AccessibilitySourceServiceTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AccessibilitySourceServiceTest.kt
index 2fcf4a24d..f794ab0d3 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AccessibilitySourceServiceTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AccessibilitySourceServiceTest.kt
@@ -33,8 +33,8 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
-import org.junit.runner.RunWith
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
@@ -53,8 +53,7 @@ import org.mockito.quality.Strictness
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
class AccessibilitySourceServiceTest {
- @Mock
- lateinit var jobService: AccessibilityJobService
+ @Mock lateinit var jobService: AccessibilityJobService
private lateinit var context: Context
private lateinit var mockitoSession: MockitoSession
private lateinit var accessibilitySourceService: AccessibilitySourceService
@@ -66,9 +65,11 @@ class AccessibilitySourceServiceTest {
MockitoAnnotations.initMocks(this)
context = ApplicationProvider.getApplicationContext()
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(DeviceConfig::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(DeviceConfig::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
accessibilitySourceService = runWithShellPermissionIdentity {
AccessibilitySourceService(context)
@@ -91,10 +92,9 @@ class AccessibilitySourceServiceTest {
runWithShellPermissionIdentity {
runBlocking {
- accessibilitySourceService.processAccessibilityJob(
- jobParameters,
- jobService
- ) { shouldCancel }
+ accessibilitySourceService.processAccessibilityJob(jobParameters, jobService) {
+ shouldCancel
+ }
}
}
verify(jobService).jobFinished(jobParameters, true)
@@ -103,9 +103,7 @@ class AccessibilitySourceServiceTest {
@Test
fun markServiceAsNotified() {
val a11yService = ComponentName("com.test.package", "AccessibilityService")
- runBlocking {
- accessibilitySourceService.markServiceAsNotified(a11yService)
- }
+ runBlocking { accessibilitySourceService.markServiceAsNotified(a11yService) }
val storedServices = getNotifiedServices()
assertThat(storedServices.size).isEqualTo(1)
@@ -143,9 +141,7 @@ class AccessibilitySourceServiceTest {
val allServices = listOf(a11yService, a11yService2, a11yService3)
val notifiedServices = runBlocking {
- allServices.forEach {
- accessibilitySourceService.markServiceAsNotified(it)
- }
+ allServices.forEach { accessibilitySourceService.markServiceAsNotified(it) }
accessibilitySourceService.removeFromNotifiedServices(a11yService2)
getNotifiedServices()
}
@@ -164,9 +160,7 @@ class AccessibilitySourceServiceTest {
val testComponents = listOf(testComponent, testComponent2, testComponent3)
val notifiedServices = runBlocking {
- testComponents.forEach {
- accessibilitySourceService.markServiceAsNotified(it)
- }
+ testComponents.forEach { accessibilitySourceService.markServiceAsNotified(it) }
accessibilitySourceService.removePackageState(testComponent.packageName)
getNotifiedServices()
}
@@ -182,9 +176,7 @@ class AccessibilitySourceServiceTest {
val testComponents = listOf(testComponent, testComponent2)
val notifiedServices = runBlocking {
- testComponents.forEach {
- accessibilitySourceService.markServiceAsNotified(it)
- }
+ testComponents.forEach { accessibilitySourceService.markServiceAsNotified(it) }
accessibilitySourceService.removePackageState(testComponent.packageName)
getNotifiedServices()
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AppDataSharingUpdatesPrivacySourceTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AppDataSharingUpdatesPrivacySourceTest.kt
index b1b5694ff..d09b5093f 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AppDataSharingUpdatesPrivacySourceTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/AppDataSharingUpdatesPrivacySourceTest.kt
@@ -82,7 +82,10 @@ class AppDataSharingUpdatesPrivacySourceTest {
.startMocking()
whenever(
Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java), eq(SafetyCenterManager::class.java)))
+ any(ContextWrapper::class.java),
+ eq(SafetyCenterManager::class.java)
+ )
+ )
.thenReturn(mockSafetyCenterManager)
appDataSharingUpdatesPrivacySource = AppDataSharingUpdatesPrivacySource()
@@ -116,7 +119,10 @@ class AppDataSharingUpdatesPrivacySourceTest {
.putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, REFRESH_ID)
appDataSharingUpdatesPrivacySource.rescanAndPushSafetyCenterData(
- context, refreshIntent, EVENT_REFRESH_REQUESTED)
+ context,
+ refreshIntent,
+ EVENT_REFRESH_REQUESTED
+ )
val expectedSafetySourceData: SafetySourceData =
SafetySourceData.Builder()
@@ -124,14 +130,18 @@ class AppDataSharingUpdatesPrivacySourceTest {
SafetySourceStatus.Builder(
DATA_SHARING_UPDATES_TITLE,
DATA_SHARING_UPDATES_SUMMARY,
- SafetySourceData.SEVERITY_LEVEL_INFORMATION)
+ SafetySourceData.SEVERITY_LEVEL_INFORMATION
+ )
.setPendingIntent(
PendingIntent.getActivity(
context,
/* requestCode= */ 0,
Intent(ACTION_REVIEW_APP_DATA_SHARING_UPDATES),
- FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE))
- .build())
+ FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
+ )
+ .build()
+ )
.build()
val expectedSafetyEvent =
SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
@@ -139,7 +149,10 @@ class AppDataSharingUpdatesPrivacySourceTest {
.build()
verify(mockSafetyCenterManager)
.setSafetySourceData(
- APP_DATA_SHARING_UPDATES_SOURCE_ID, expectedSafetySourceData, expectedSafetyEvent)
+ APP_DATA_SHARING_UPDATES_SOURCE_ID,
+ expectedSafetySourceData,
+ expectedSafetyEvent
+ )
}
@Test
@@ -148,7 +161,10 @@ class AppDataSharingUpdatesPrivacySourceTest {
val bootCompleteIntent = Intent(ACTION_BOOT_COMPLETED)
appDataSharingUpdatesPrivacySource.rescanAndPushSafetyCenterData(
- context, bootCompleteIntent, EVENT_DEVICE_REBOOTED)
+ context,
+ bootCompleteIntent,
+ EVENT_DEVICE_REBOOTED
+ )
val expectedSafetyEvent = SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build()
verify(mockSafetyCenterManager)
@@ -163,7 +179,10 @@ class AppDataSharingUpdatesPrivacySourceTest {
.putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, REFRESH_ID)
appDataSharingUpdatesPrivacySource.rescanAndPushSafetyCenterData(
- context, refreshIntent, EVENT_REFRESH_REQUESTED)
+ context,
+ refreshIntent,
+ EVENT_REFRESH_REQUESTED
+ )
val expectedSafetyEvent =
SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
@@ -190,7 +209,9 @@ class AppDataSharingUpdatesPrivacySourceTest {
DeviceConfig.getBoolean(
eq(NAMESPACE_PRIVACY),
eq(SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED),
- anyBoolean()))
+ anyBoolean()
+ )
+ )
.thenReturn(enabled)
}
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerCheckInternalTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerCheckInternalTest.kt
index 6f1d2c5c9..bc00d3bc8 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerCheckInternalTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerCheckInternalTest.kt
@@ -95,7 +95,9 @@ class NotificationListenerCheckInternalTest {
// Setup Safety Center
doReturn(mockSafetyCenterManager).`when` {
Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java), eq(SafetyCenterManager::class.java))
+ any(ContextWrapper::class.java),
+ eq(SafetyCenterManager::class.java)
+ )
}
notificationListenerCheck = runWithShellPermissionIdentity {
@@ -121,7 +123,9 @@ class NotificationListenerCheckInternalTest {
runWithShellPermissionIdentity {
runBlocking {
notificationListenerCheck.getEnabledNotificationListenersAndNotifyIfNeeded(
- jobParameters, mockNotificationListenerCheckJobService)
+ jobParameters,
+ mockNotificationListenerCheckJobService
+ )
}
}
@@ -135,7 +139,9 @@ class NotificationListenerCheckInternalTest {
runWithShellPermissionIdentity {
runBlocking {
notificationListenerCheck.getEnabledNotificationListenersAndNotifyIfNeeded(
- jobParameters, mockNotificationListenerCheckJobService)
+ jobParameters,
+ mockNotificationListenerCheckJobService
+ )
}
}
@@ -149,7 +155,9 @@ class NotificationListenerCheckInternalTest {
runWithShellPermissionIdentity {
runBlocking {
notificationListenerCheck.getEnabledNotificationListenersAndNotifyIfNeeded(
- jobParameters, mockNotificationListenerCheckJobService)
+ jobParameters,
+ mockNotificationListenerCheckJobService
+ )
}
}
@@ -157,7 +165,8 @@ class NotificationListenerCheckInternalTest {
.setSafetySourceData(
eq(SC_NLS_SOURCE_ID),
any(SafetySourceData::class.java),
- any(SafetyEvent::class.java))
+ any(SafetyEvent::class.java)
+ )
}
@Test
@@ -185,7 +194,8 @@ class NotificationListenerCheckInternalTest {
val updatedNlsComponents = runWithShellPermissionIdentity {
runBlocking {
notificationListenerCheck.removeDisabledComponentsFromNotifiedComponents(
- updatedEnabledComponents)
+ updatedEnabledComponents
+ )
getNotifiedComponents()
}
}
@@ -445,7 +455,9 @@ class NotificationListenerCheckInternalTest {
val testAppLabel = "TestApp Label"
doReturn(PackageInfo().apply { applicationInfo = ApplicationInfo() }).`when` {
Utils.getPackageInfoForComponentName(
- any(Context::class.java), any(ComponentName::class.java))
+ any(Context::class.java),
+ any(ComponentName::class.java)
+ )
}
doReturn(testAppLabel).`when` {
Utils.getApplicationLabel(any(Context::class.java), any(ApplicationInfo::class.java))
@@ -453,7 +465,8 @@ class NotificationListenerCheckInternalTest {
val safetySourceIssue =
Preconditions.checkNotNull(
- notificationListenerCheck.createSafetySourceIssue(testComponent, 0))
+ notificationListenerCheck.createSafetySourceIssue(testComponent, 0)
+ )
val expectedId = "notification_listener_${testComponent.flattenToString()}"
val expectedTitle =
@@ -470,21 +483,28 @@ class NotificationListenerCheckInternalTest {
}
val expectedDismissPendingIntent =
PendingIntent.getBroadcast(
- context, 0, expectedDismissIntent, PendingIntent.FLAG_IMMUTABLE)
+ context,
+ 0,
+ expectedDismissIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
val expectedAction1 =
SafetySourceIssue.Action.Builder(
SC_NLS_DISABLE_ACTION_ID,
context.getString(R.string.notification_listener_remove_access_button_label),
- getDisableNlsPendingIntent(context, expectedId, testComponent))
+ getDisableNlsPendingIntent(context, expectedId, testComponent)
+ )
.setWillResolve(true)
.setSuccessMessage(
- context.getString(R.string.notification_listener_remove_access_success_label))
+ context.getString(R.string.notification_listener_remove_access_success_label)
+ )
.build()
val expectedAction2 =
SafetySourceIssue.Action.Builder(
NotificationListenerCheckInternal.SC_SHOW_NLS_SETTINGS_ACTION_ID,
context.getString(R.string.notification_listener_review_app_button_label),
- getNotificationListenerSettingsPendingIntent(context, testComponent))
+ getNotificationListenerSettingsPendingIntent(context, testComponent)
+ )
.build()
assertThat(safetySourceIssue.id).isEqualTo(expectedId)
@@ -501,9 +521,7 @@ class NotificationListenerCheckInternalTest {
@Test
fun exemptPackagesNotInitializedUntilUsed() {
assertThat(notificationListenerCheck.exemptPackagesDelegate.isInitialized()).isFalse()
- runWithShellPermissionIdentity {
- notificationListenerCheck.exemptPackages
- }
+ runWithShellPermissionIdentity { notificationListenerCheck.exemptPackages }
assertThat(notificationListenerCheck.exemptPackagesDelegate.isInitialized()).isTrue()
}
@@ -542,7 +560,8 @@ class NotificationListenerCheckInternalTest {
identifier = componentName.flattenToString()
putExtra(
Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME,
- componentName.flattenToString())
+ componentName.flattenToString()
+ )
}
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPregrantsTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPregrantsTest.kt
index d8d4b3866..55419ca3d 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPregrantsTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPregrantsTest.kt
@@ -47,9 +47,11 @@ class NotificationListenerPregrantsTest {
MockitoAnnotations.initMocks(this)
context = ApplicationProvider.getApplicationContext()
- mockitoSession = ExtendedMockito.mockitoSession()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
.mockStatic(Utils::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ .strictness(Strictness.LENIENT)
+ .startMocking()
notificationListenerPregrants = NotificationListenerPregrants(context)
}
@@ -71,4 +73,4 @@ class NotificationListenerPregrantsTest {
notificationListenerPregrants.pregrantedPackages
assertTrue(notificationListenerPregrants.pregrantedPackagesDelegate.isInitialized())
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPrivacySourceTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPrivacySourceTest.kt
index 9c1edb795..30f0880e1 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPrivacySourceTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerPrivacySourceTest.kt
@@ -37,10 +37,10 @@ import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.permissioncontroller.Constants.NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID
-import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.PermissionControllerApplication
-import com.android.permissioncontroller.privacysources.NotificationListenerPrivacySource
+import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.privacysources.NotificationListenerCheckInternal
+import com.android.permissioncontroller.privacysources.NotificationListenerPrivacySource
import com.android.permissioncontroller.privacysources.PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED
import com.android.permissioncontroller.privacysources.SC_NLS_SOURCE_ID
import com.android.permissioncontroller.privacysources.SafetyCenterReceiver
@@ -53,27 +53,23 @@ import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.MockitoSession
-import org.mockito.quality.Strictness
import org.mockito.Mockito.never
-import org.mockito.Mockito.`when` as whenever
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
class NotificationListenerPrivacySourceTest {
private lateinit var mockitoSession: MockitoSession
- @Mock
- lateinit var mockSafetyCenterManager: SafetyCenterManager
- @Mock
- lateinit var mockNotificationManager: NotificationManager
- @Mock
- lateinit var mockRoleManager: RoleManager
- @Mock
- lateinit var mockUserManager: UserManager
+ @Mock lateinit var mockSafetyCenterManager: SafetyCenterManager
+ @Mock lateinit var mockNotificationManager: NotificationManager
+ @Mock lateinit var mockRoleManager: RoleManager
+ @Mock lateinit var mockUserManager: UserManager
private lateinit var context: Context
private lateinit var notificationListenerCheck: NotificationListenerCheckInternal
@@ -92,66 +88,89 @@ class NotificationListenerPrivacySourceTest {
MockitoAnnotations.initMocks(this)
context = ApplicationProvider.getApplicationContext()
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(DeviceConfig::class.java)
- .mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(Utils::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(DeviceConfig::class.java)
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(Utils::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
// Setup default flagging
setNotificationListenerCheckEnabled(true)
// Setup contexts
- whenever(Utils.getParentUserContext(any(ContextWrapper::class.java)))
- .thenReturn(context)
+ whenever(Utils.getParentUserContext(any(ContextWrapper::class.java))).thenReturn(context)
// Setup package manager and mocked NLS packages
val packageInfo1 = getPackageInfo()
val packageInfo2 = getPackageInfo()
- whenever(Utils.getPackageInfoForComponentName(
- any(ContextWrapper::class.java),
- eq(testComponent1)))
+ whenever(
+ Utils.getPackageInfoForComponentName(
+ any(ContextWrapper::class.java),
+ eq(testComponent1)
+ )
+ )
.thenReturn(packageInfo1)
- whenever(Utils.getPackageInfoForComponentName(
- any(ContextWrapper::class.java),
- eq(testComponent2)))
+ whenever(
+ Utils.getPackageInfoForComponentName(
+ any(ContextWrapper::class.java),
+ eq(testComponent2)
+ )
+ )
.thenReturn(packageInfo2)
- whenever(Utils.getApplicationLabel(
- any(ContextWrapper::class.java),
- eq(packageInfo1.applicationInfo)))
+ whenever(
+ Utils.getApplicationLabel(
+ any(ContextWrapper::class.java),
+ eq(packageInfo1.applicationInfo)
+ )
+ )
.thenReturn(testComponent1.className)
- whenever(Utils.getApplicationLabel(
- any(ContextWrapper::class.java),
- eq(packageInfo2.applicationInfo)))
+ whenever(
+ Utils.getApplicationLabel(
+ any(ContextWrapper::class.java),
+ eq(packageInfo2.applicationInfo)
+ )
+ )
.thenReturn(testComponent2.className)
// Setup UserManager and User
- whenever(Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java),
- eq(UserManager::class.java)))
+ whenever(
+ Utils.getSystemServiceSafe(
+ any(ContextWrapper::class.java),
+ eq(UserManager::class.java)
+ )
+ )
.thenReturn(mockUserManager)
- whenever(mockUserManager.getProfileParent(any(UserHandle::class.java)))
- .thenReturn(null)
+ whenever(mockUserManager.getProfileParent(any(UserHandle::class.java))).thenReturn(null)
// Setup Notification Manager
- whenever(Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java),
- eq(NotificationManager::class.java)))
+ whenever(
+ Utils.getSystemServiceSafe(
+ any(ContextWrapper::class.java),
+ eq(NotificationManager::class.java)
+ )
+ )
.thenReturn(mockNotificationManager)
whenever(mockNotificationManager.enabledNotificationListeners)
.thenReturn(listOf(testComponent1, testComponent2))
- whenever(Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java),
- eq(RoleManager::class.java)))
+ whenever(
+ Utils.getSystemServiceSafe(
+ any(ContextWrapper::class.java),
+ eq(RoleManager::class.java)
+ )
+ )
.thenReturn(mockRoleManager)
- whenever(mockRoleManager.getRoleHolders(anyString()))
- .thenReturn(emptyList())
+ whenever(mockRoleManager.getRoleHolders(anyString())).thenReturn(emptyList())
// Setup Safety Center
- whenever(Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java),
- eq(SafetyCenterManager::class.java)))
+ whenever(
+ Utils.getSystemServiceSafe(
+ any(ContextWrapper::class.java),
+ eq(SafetyCenterManager::class.java)
+ )
+ )
.thenReturn(mockSafetyCenterManager)
// Init NotificationListenerCheckInternal, used to quickly create expected SafetySourceData
@@ -167,24 +186,21 @@ class NotificationListenerPrivacySourceTest {
@Test
fun safetyCenterEnabledChanged_removesNotifications() {
- runWithShellPermissionIdentity {
- privacySource.safetyCenterEnabledChanged(context, false)
- }
+ runWithShellPermissionIdentity { privacySource.safetyCenterEnabledChanged(context, false) }
verify(mockNotificationManager).cancel(NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID)
}
@Test
fun safetyCenterEnabledChanged_doesNotUpdateSafetyCenterData() {
- runWithShellPermissionIdentity {
- privacySource.safetyCenterEnabledChanged(context, false)
- }
+ runWithShellPermissionIdentity { privacySource.safetyCenterEnabledChanged(context, false) }
verify(mockSafetyCenterManager, never())
.setSafetySourceData(
eq(SC_NLS_SOURCE_ID),
any(SafetySourceData::class.java),
- any(SafetyEvent::class.java))
+ any(SafetyEvent::class.java)
+ )
}
@Test
@@ -209,18 +225,17 @@ class NotificationListenerPrivacySourceTest {
SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build()
verify(mockSafetyCenterManager)
- .setSafetySourceData(
- SC_NLS_SOURCE_ID,
- expectedSafetySourceData,
- expectedSafetyEvent
- )
+ .setSafetySourceData(SC_NLS_SOURCE_ID, expectedSafetySourceData, expectedSafetyEvent)
}
@Test
fun rescanAndPushSafetyCenterData_updatesSafetyCenterEventRefresh() {
- val intent = Intent(SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES)
- .putExtra(SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID,
- SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID)
+ val intent =
+ Intent(SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(
+ SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID,
+ SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID
+ )
privacySource.rescanAndPushSafetyCenterData(
context,
intent,
@@ -237,11 +252,7 @@ class NotificationListenerPrivacySourceTest {
.build()
verify(mockSafetyCenterManager)
- .setSafetySourceData(
- SC_NLS_SOURCE_ID,
- expectedSafetySourceData,
- expectedSafetyEvent
- )
+ .setSafetySourceData(SC_NLS_SOURCE_ID, expectedSafetySourceData, expectedSafetyEvent)
}
@Test
@@ -257,16 +268,11 @@ class NotificationListenerPrivacySourceTest {
SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
verify(mockSafetyCenterManager)
- .setSafetySourceData(
- SC_NLS_SOURCE_ID,
- expectedSafetySourceData,
- expectedSafetyEvent
- )
+ .setSafetySourceData(SC_NLS_SOURCE_ID, expectedSafetySourceData, expectedSafetyEvent)
}
@Test
- fun rescanAndPushSafetyCenterData_notificationListenerCheckDisabled_noSafetyCenterInteractions
- () {
+ fun rescanAndPushSafetyCenterData_notificationListenerCheckDisabled_noSafetyCenterInteractions() {
setNotificationListenerCheckEnabled(false)
privacySource.rescanAndPushSafetyCenterData(
@@ -280,18 +286,17 @@ class NotificationListenerPrivacySourceTest {
private fun setNotificationListenerCheckEnabled(enabled: Boolean) {
whenever(
- DeviceConfig.getBoolean(
- eq(DeviceConfig.NAMESPACE_PRIVACY),
- eq(PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED),
- anyBoolean()
+ DeviceConfig.getBoolean(
+ eq(DeviceConfig.NAMESPACE_PRIVACY),
+ eq(PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED),
+ anyBoolean()
+ )
)
- ).thenReturn(enabled)
+ .thenReturn(enabled)
}
private fun getPackageInfo(): PackageInfo {
- return PackageInfo().apply {
- applicationInfo = ApplicationInfo()
- }
+ return PackageInfo().apply { applicationInfo = ApplicationInfo() }
}
private fun createExpectedSafetyCenterData(): SafetySourceData {
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt
index b4b1abd96..6a45ac7c4 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt
@@ -38,7 +38,6 @@ import com.android.permissioncontroller.privacysources.PrivacySource
import com.android.permissioncontroller.privacysources.SafetyCenterReceiver
import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent.EVENT_DEVICE_REBOOTED
import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent.EVENT_REFRESH_REQUESTED
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
@@ -48,7 +47,6 @@ import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
@@ -56,16 +54,14 @@ import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when` as whenever
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-/**
- * Unit tests for [SafetyCenterReceiver]
- */
+/** Unit tests for [SafetyCenterReceiver] */
@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
@@ -80,48 +76,50 @@ class SafetyCenterReceiverTest {
private val testCoroutineDispatcher = TestCoroutineDispatcher()
- @Mock
- lateinit var mockSafetyCenterManager: SafetyCenterManager
- @Mock
- lateinit var mockPackageManager: PackageManager
- @Mock
- lateinit var mockPrivacySource: PrivacySource
- @Mock
- lateinit var mockPrivacySource2: PrivacySource
- @Mock
- lateinit var mockUserManager: UserManager
+ @Mock lateinit var mockSafetyCenterManager: SafetyCenterManager
+ @Mock lateinit var mockPackageManager: PackageManager
+ @Mock lateinit var mockPrivacySource: PrivacySource
+ @Mock lateinit var mockPrivacySource2: PrivacySource
+ @Mock lateinit var mockUserManager: UserManager
private lateinit var mockitoSession: MockitoSession
private lateinit var safetyCenterReceiver: SafetyCenterReceiver
- private fun privacySourceMap(context: Context) = mapOf(
- TEST_PRIVACY_SOURCE_ID to mockPrivacySource,
- TEST_PRIVACY_SOURCE_ID_2 to mockPrivacySource2
- )
+ private fun privacySourceMap(context: Context) =
+ mapOf(
+ TEST_PRIVACY_SOURCE_ID to mockPrivacySource,
+ TEST_PRIVACY_SOURCE_ID_2 to mockPrivacySource2
+ )
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mockitoSession = ExtendedMockito.mockitoSession()
- .mockStatic(DeviceConfig::class.java)
- .mockStatic(PermissionControllerApplication::class.java)
- .mockStatic(Utils::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(DeviceConfig::class.java)
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(Utils::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
whenever(PermissionControllerApplication.get()).thenReturn(application)
whenever(application.applicationContext).thenReturn(application)
whenever(application.packageManager).thenReturn(mockPackageManager)
whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
whenever(
- Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java), eq(UserManager::class.java)
- ))
+ Utils.getSystemServiceSafe(
+ any(ContextWrapper::class.java),
+ eq(UserManager::class.java)
+ )
+ )
.thenReturn(mockUserManager)
whenever(
- Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java), eq(SafetyCenterManager::class.java)
- ))
+ Utils.getSystemServiceSafe(
+ any(ContextWrapper::class.java),
+ eq(SafetyCenterManager::class.java)
+ )
+ )
.thenReturn(mockSafetyCenterManager)
whenever(mockUserManager.isProfile).thenReturn(false)
@@ -140,12 +138,13 @@ class SafetyCenterReceiverTest {
private fun mockQSTileSettingsFlag() {
whenever(
- DeviceConfig.getInt(
- eq(DeviceConfig.NAMESPACE_PRIVACY),
- eq(SafetyCenterQsTileService.QS_TILE_COMPONENT_SETTING_FLAGS),
- ArgumentMatchers.anyInt()
+ DeviceConfig.getInt(
+ eq(DeviceConfig.NAMESPACE_PRIVACY),
+ eq(SafetyCenterQsTileService.QS_TILE_COMPONENT_SETTING_FLAGS),
+ ArgumentMatchers.anyInt()
+ )
)
- ).thenReturn(PackageManager.DONT_KILL_APP)
+ .thenReturn(PackageManager.DONT_KILL_APP)
}
@Test
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/TextStorageRepositoryTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/TextStorageRepositoryTest.kt
index 15b9168ad..6c824122c 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/TextStorageRepositoryTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/TextStorageRepositoryTest.kt
@@ -25,6 +25,9 @@ import androidx.test.filters.SdkSuppress
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.permissioncontroller.privacysources.PrivacySourceData
import com.android.permissioncontroller.privacysources.TextStorageRepository
+import java.io.BufferedWriter
+import java.io.File
+import java.io.FileWriter
import org.junit.After
import org.junit.Assert
import org.junit.Before
@@ -33,9 +36,6 @@ import org.junit.runner.RunWith
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-import java.io.BufferedWriter
-import java.io.File
-import java.io.FileWriter
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
@@ -50,9 +50,11 @@ class TextStorageRepositoryTest {
MockitoAnnotations.initMocks(this)
context = ApplicationProvider.getApplicationContext()
- mockitoSession = ExtendedMockito.mockitoSession()
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
.mockStatic(DeviceConfig::class.java)
- .strictness(Strictness.LENIENT).startMocking()
+ .strictness(Strictness.LENIENT)
+ .startMocking()
dataFile = context.getFileStreamPath("testFile")
}
@@ -83,10 +85,12 @@ class TextStorageRepositoryTest {
@Test
fun testWrite_MultipleEntries() {
val storageRepository = TextStorageRepository(dataFile)
- val components = listOf(TestPrivacySourceComponent("a", 100),
+ val components =
+ listOf(
+ TestPrivacySourceComponent("a", 100),
TestPrivacySourceComponent("b", 200),
TestPrivacySourceComponent("c", 300)
- )
+ )
storageRepository.persistData(components)
val dataList = storageRepository.readData(creator)
Assert.assertEquals(3, dataList.size)
@@ -95,10 +99,12 @@ class TextStorageRepositoryTest {
@Test
fun testRead_Ignore_CorruptedEntries() {
val storageRepository = TextStorageRepository(dataFile)
- val components = listOf(TestPrivacySourceComponent("a", 100),
- TestPrivacySourceComponent("b", 200),
- TestPrivacySourceComponent("c", 300)
- )
+ val components =
+ listOf(
+ TestPrivacySourceComponent("a", 100),
+ TestPrivacySourceComponent("b", 200),
+ TestPrivacySourceComponent("c", 300)
+ )
storageRepository.persistData(components)
appendCorruptedData(dataFile, "not_enough_parts")
appendCorruptedData(dataFile, "not_enough_parts")
@@ -110,9 +116,8 @@ class TextStorageRepositoryTest {
@Test
fun testRead_Ignore_NumberFormatError() {
val storageRepository = TextStorageRepository(dataFile)
- val components = listOf(TestPrivacySourceComponent("a", 100),
- TestPrivacySourceComponent("b", 200)
- )
+ val components =
+ listOf(TestPrivacySourceComponent("a", 100), TestPrivacySourceComponent("b", 200))
storageRepository.persistData(components)
appendCorruptedData(dataFile, "com.example.TestService hundred")
appendCorruptedData(dataFile, "com.example.TestService1 abc")
@@ -128,19 +133,18 @@ class TextStorageRepositoryTest {
}
}
- private val creator = object : PrivacySourceData.Creator<TestPrivacySourceComponent> {
- override fun fromStorageData(data: String): TestPrivacySourceComponent {
- val lineComponents = data.split(" ")
- return TestPrivacySourceComponent(lineComponents[0], lineComponents[1].toInt())
+ private val creator =
+ object : PrivacySourceData.Creator<TestPrivacySourceComponent> {
+ override fun fromStorageData(data: String): TestPrivacySourceComponent {
+ val lineComponents = data.split(" ")
+ return TestPrivacySourceComponent(lineComponents[0], lineComponents[1].toInt())
+ }
}
- }
}
-class TestPrivacySourceComponent(
- private val name: String,
- private val timestamp: Int
-) : PrivacySourceData {
+class TestPrivacySourceComponent(private val name: String, private val timestamp: Int) :
+ PrivacySourceData {
override fun toStorageData(): String {
return "$name $timestamp"
}
-} \ No newline at end of file
+}
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 3e86d58b0..bbbf0e86a 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
@@ -85,24 +85,34 @@ class WorkPolicyInfoTest {
val application = Mockito.mock(PermissionControllerApplication::class.java)
whenever(
Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java), eq(UserManager::class.java)))
+ any(ContextWrapper::class.java),
+ eq(UserManager::class.java)
+ )
+ )
.thenReturn(mockUserManager)
whenever(
Utils.getSystemServiceSafe(
- any(ContextWrapper::class.java), eq(SafetyCenterManager::class.java)))
+ any(ContextWrapper::class.java),
+ eq(SafetyCenterManager::class.java)
+ )
+ )
.thenReturn(mockSafetyCenterManager)
whenever(mockUserManager.isProfile).thenReturn(false)
whenever(
Utils.getEnterpriseString(
any(ContextWrapper::class.java),
eq(WorkPolicyInfo.WORK_POLICY_TITLE),
- anyInt()))
+ anyInt()
+ )
+ )
.thenReturn(WORK_POLICY_TITLE)
whenever(
Utils.getEnterpriseString(
any(ContextWrapper::class.java),
eq(WorkPolicyInfo.WORK_POLICY_SUMMARY),
- anyInt()))
+ anyInt()
+ )
+ )
.thenReturn(WORK_POLICY_SUMMARY)
whenever(PermissionControllerApplication.get()).thenReturn(application)
@@ -121,7 +131,8 @@ class WorkPolicyInfoTest {
Intent(Intent.ACTION_BOOT_COMPLETED)
.putExtra(
SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID,
- SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID)
+ SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID
+ )
whenever(mockWorkPolicyUtils.workPolicyInfoIntentDO).thenReturn(intent)
whenever(mockWorkPolicyUtils.workPolicyInfoIntentPO).thenReturn(null)
@@ -134,7 +145,8 @@ class WorkPolicyInfoTest {
SafetySourceStatus.Builder(
WORK_POLICY_TITLE,
WORK_POLICY_SUMMARY,
- SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED)
+ SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
+ )
.setPendingIntent(pendingIntent)
.build()
val expectedSafetySourceData: SafetySourceData =
@@ -146,7 +158,8 @@ class WorkPolicyInfoTest {
.setSafetySourceData(
WorkPolicyInfo.WORK_POLICY_INFO_SOURCE_ID,
expectedSafetySourceData,
- expectedSafetyEvent)
+ expectedSafetyEvent
+ )
}
@Test
@@ -170,7 +183,8 @@ class WorkPolicyInfoTest {
.setSafetySourceData(
WorkPolicyInfo.WORK_POLICY_INFO_SOURCE_ID,
expectedSafetySourceData,
- expectedSafetyEvent)
+ expectedSafetyEvent
+ )
}
@Test
@@ -189,7 +203,10 @@ class WorkPolicyInfoTest {
whenever(mockWorkPolicyUtils.workPolicyInfoIntentPO).thenReturn(null)
workPolicyInfo.rescanAndPushSafetyCenterData(
- context, intent, RefreshEvent.EVENT_DEVICE_REBOOTED)
+ context,
+ intent,
+ RefreshEvent.EVENT_DEVICE_REBOOTED
+ )
val pendingIntent =
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
@@ -197,7 +214,8 @@ class WorkPolicyInfoTest {
SafetySourceStatus.Builder(
WORK_POLICY_TITLE,
WORK_POLICY_SUMMARY,
- SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED)
+ SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
+ )
.setPendingIntent(pendingIntent)
.build()
@@ -210,7 +228,8 @@ class WorkPolicyInfoTest {
.setSafetySourceData(
WorkPolicyInfo.WORK_POLICY_INFO_SOURCE_ID,
expectedSafetySourceData,
- expectedSafetyEvent)
+ expectedSafetyEvent
+ )
}
@Test
@@ -219,13 +238,17 @@ class WorkPolicyInfoTest {
Intent(Intent.ACTION_BOOT_COMPLETED)
.putExtra(
SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID,
- SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID)
+ SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID
+ )
whenever(mockWorkPolicyUtils.workPolicyInfoIntentDO).thenReturn(intent)
whenever(mockWorkPolicyUtils.workPolicyInfoIntentPO).thenReturn(null)
workPolicyInfo.rescanAndPushSafetyCenterData(
- context, intent, RefreshEvent.EVENT_REFRESH_REQUESTED)
+ context,
+ intent,
+ RefreshEvent.EVENT_REFRESH_REQUESTED
+ )
val pendingIntent =
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
@@ -233,7 +256,8 @@ class WorkPolicyInfoTest {
SafetySourceStatus.Builder(
WORK_POLICY_TITLE,
WORK_POLICY_SUMMARY,
- SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED)
+ SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
+ )
.setPendingIntent(pendingIntent)
.build()
@@ -251,7 +275,8 @@ class WorkPolicyInfoTest {
.setSafetySourceData(
WorkPolicyInfo.WORK_POLICY_INFO_SOURCE_ID,
expectedSafetySourceData,
- expectedSafetyEvent)
+ expectedSafetyEvent
+ )
}
@Test
@@ -270,7 +295,8 @@ class WorkPolicyInfoTest {
SafetySourceStatus.Builder(
WORK_POLICY_TITLE,
WORK_POLICY_SUMMARY,
- SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED)
+ SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
+ )
.setPendingIntent(pendingIntent)
.build()
@@ -283,7 +309,8 @@ class WorkPolicyInfoTest {
.setSafetySourceData(
WorkPolicyInfo.WORK_POLICY_INFO_SOURCE_ID,
expectedSafetySourceData,
- expectedSafetyEvent)
+ expectedSafetyEvent
+ )
}
@Test
@@ -302,6 +329,7 @@ class WorkPolicyInfoTest {
.setSafetySourceData(
WorkPolicyInfo.WORK_POLICY_INFO_SOURCE_ID,
expectedSafetySourceData,
- expectedSafetyEvent)
+ expectedSafetyEvent
+ )
}
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt
index 34ea6fcf0..ca0392716 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt
@@ -22,6 +22,9 @@ import android.safetycenter.SafetyCenterData
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
+import android.safetycenter.SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_RECOMMENDATION
import android.safetycenter.SafetyCenterStatus
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
@@ -209,6 +212,44 @@ class SafetyCenterUiDataTest {
assertThat(result).isEmpty()
}
+ @Test
+ fun getMatchingDismissedIssues_doesntReturnGreenIssues() {
+ val greenDismissedIssue =
+ createSafetyCenterIssue(
+ "id1",
+ MATCHING_GROUP_ID,
+ severityLevel = ISSUE_SEVERITY_LEVEL_OK
+ )
+ val yellowDismissedIssue =
+ createSafetyCenterIssue(
+ "id2",
+ MATCHING_GROUP_ID,
+ severityLevel = ISSUE_SEVERITY_LEVEL_RECOMMENDATION
+ )
+ val redDismissedIssue =
+ createSafetyCenterIssue(
+ "id3",
+ MATCHING_GROUP_ID,
+ severityLevel = ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING
+ )
+ val nonMatchingDismissedIssue = createSafetyCenterIssue("id4", NON_MATCHING_GROUP_ID)
+ val safetyCenterData =
+ createSafetyCenterData(
+ dismissedIssues =
+ listOf(
+ redDismissedIssue,
+ yellowDismissedIssue,
+ greenDismissedIssue,
+ nonMatchingDismissedIssue
+ ),
+ )
+
+ val result =
+ SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
+
+ assertThat(result).containsExactly(redDismissedIssue, yellowDismissedIssue).inOrder()
+ }
+
private companion object {
const val MATCHING_GROUP_ID = "matching_group_id"
const val NON_MATCHING_GROUP_ID = "non_matching_group_id"
@@ -238,8 +279,13 @@ class SafetyCenterUiDataTest {
fun createSafetyCenterEntryGroup(groupId: String) =
SafetyCenterEntryGroup.Builder(groupId, "group title").build()
- fun createSafetyCenterIssue(issueId: String, groupId: String) =
+ fun createSafetyCenterIssue(
+ issueId: String,
+ groupId: String,
+ severityLevel: Int = ISSUE_SEVERITY_LEVEL_RECOMMENDATION
+ ) =
SafetyCenterIssue.Builder(issueId, "issue title", "issue summary")
+ .setSeverityLevel(severityLevel)
.setGroupId(groupId)
.build()
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt
index d963fd535..2b9f6b8d2 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt
@@ -72,8 +72,8 @@ class StatusUiDataTest {
assertThat(StatusUiData(STATUS).originalSummary).isEqualTo(STATUS.summary)
assertThat(StatusUiData(ANOTHER_STATUS).originalSummary).isEqualTo(ANOTHER_STATUS.summary)
assertThat(
- StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf()))
- .originalSummary)
+ StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf())).originalSummary
+ )
.isEqualTo(STATUS.summary)
}
@@ -83,7 +83,8 @@ class StatusUiDataTest {
assertThat(StatusUiData(ANOTHER_STATUS).severityLevel)
.isEqualTo(ANOTHER_STATUS.severityLevel)
assertThat(
- StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf())).severityLevel)
+ StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf())).severityLevel
+ )
.isEqualTo(STATUS.severityLevel)
}
@@ -114,7 +115,9 @@ class StatusUiDataTest {
mockContext.getString(
R.string.safety_status_preference_title_and_summary_content_description,
STATUS.title,
- STATUS.summary))
+ STATUS.summary
+ )
+ )
.thenReturn(expectedContentDescription)
val actualContentDescription = StatusUiData(STATUS).getContentDescription(mockContext)
@@ -163,14 +166,16 @@ class StatusUiDataTest {
@Test
fun isRefreshInProgress_dataFetch_isTrue() {
assertThat(
- uiDataForRefreshStatus(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS).isRefreshInProgress)
+ uiDataForRefreshStatus(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS).isRefreshInProgress
+ )
.isTrue()
}
@Test
fun isRefreshInProgress_fullRescan_isTrue() {
assertThat(
- uiDataForRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS).isRefreshInProgress)
+ uiDataForRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS).isRefreshInProgress
+ )
.isTrue()
}
@@ -185,8 +190,10 @@ class StatusUiDataTest {
StatusUiData(
statusForSeverity(OVERALL_SEVERITY_LEVEL_OK),
hasIssues = false,
- hasPendingActions = false)
- .shouldShowRescanButton())
+ hasPendingActions = false
+ )
+ .shouldShowRescanButton()
+ )
.isTrue()
}
@@ -196,8 +203,10 @@ class StatusUiDataTest {
StatusUiData(
statusForSeverity(OVERALL_SEVERITY_LEVEL_UNKNOWN),
hasIssues = false,
- hasPendingActions = false)
- .shouldShowRescanButton())
+ hasPendingActions = false
+ )
+ .shouldShowRescanButton()
+ )
.isTrue()
}
@@ -207,8 +216,10 @@ class StatusUiDataTest {
StatusUiData(
statusForSeverity(OVERALL_SEVERITY_LEVEL_OK),
hasIssues = true,
- hasPendingActions = false)
- .shouldShowRescanButton())
+ hasPendingActions = false
+ )
+ .shouldShowRescanButton()
+ )
.isFalse()
}
@@ -218,8 +229,10 @@ class StatusUiDataTest {
StatusUiData(
statusForSeverity(OVERALL_SEVERITY_LEVEL_OK),
hasIssues = false,
- hasPendingActions = true)
- .shouldShowRescanButton())
+ hasPendingActions = true
+ )
+ .shouldShowRescanButton()
+ )
.isFalse()
}
@@ -227,13 +240,17 @@ class StatusUiDataTest {
fun shouldShowRescanButton_severityNotOkOrUnknown_isFalse() {
for (severity in
listOf(
- OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING, OVERALL_SEVERITY_LEVEL_RECOMMENDATION)) {
+ OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING,
+ OVERALL_SEVERITY_LEVEL_RECOMMENDATION
+ )) {
assertThat(
StatusUiData(
statusForSeverity(severity),
hasIssues = false,
- hasPendingActions = false)
- .shouldShowRescanButton())
+ hasPendingActions = false
+ )
+ .shouldShowRescanButton()
+ )
.isFalse()
}
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryPersistenceTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryPersistenceTest.kt
index 10221f6fa..ff91677b8 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryPersistenceTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryPersistenceTest.kt
@@ -113,7 +113,9 @@ class AppsSafetyLabelHistoryPersistenceTest {
val appsSafetyLabelHistory =
AppsSafetyLabelHistory(
listOf(
- AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V2))))
+ AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V2))
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
assertThat(AppsSafetyLabelHistoryPersistence.read(dataFile).appsSafetyLabelHistory)
@@ -128,7 +130,10 @@ class AppsSafetyLabelHistoryPersistenceTest {
AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1)),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2))))
+ listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
assertThat(AppsSafetyLabelHistoryPersistence.read(dataFile).appsSafetyLabelHistory)
@@ -145,7 +150,9 @@ class AppsSafetyLabelHistoryPersistenceTest {
val appsSafetyLabelHistory =
AppsSafetyLabelHistory(
listOf(
- AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V2))))
+ AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V2))
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
assertThat(AppsSafetyLabelHistoryPersistence.read(dataFile).version).isEqualTo(0)
@@ -156,9 +163,13 @@ class AppsSafetyLabelHistoryPersistenceTest {
val appsSafetyLabelHistory =
AppsSafetyLabelHistory(
listOf(
- AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V2))))
+ AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V2))
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(
- dataFile, AppsSafetyLabelHistoryFileContent(appsSafetyLabelHistory, 5))
+ dataFile,
+ AppsSafetyLabelHistoryFileContent(appsSafetyLabelHistory, 5)
+ )
assertThat(AppsSafetyLabelHistoryPersistence.read(dataFile).version).isEqualTo(5)
}
@@ -174,7 +185,12 @@ class AppsSafetyLabelHistoryPersistenceTest {
AppsSafetyLabelHistory(
listOf(
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1)))))
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V1)
+ )
+ )
+ )
+ )
}
@Test
@@ -182,7 +198,9 @@ class AppsSafetyLabelHistoryPersistenceTest {
val appsSafetyLabelHistory =
AppsSafetyLabelHistory(
listOf(
- AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1))))
+ AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1))
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
AppsSafetyLabelHistoryPersistence.recordSafetyLabel(SAFETY_LABEL_PKG_2_V1, dataFile)
@@ -192,9 +210,16 @@ class AppsSafetyLabelHistoryPersistenceTest {
AppsSafetyLabelHistory(
listOf(
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1)),
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V1)
+ ),
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V1)))))
+ AppInfo(PACKAGE_NAME_2),
+ listOf(SAFETY_LABEL_PKG_2_V1)
+ )
+ )
+ )
+ )
}
@Test
@@ -205,7 +230,10 @@ class AppsSafetyLabelHistoryPersistenceTest {
AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1)),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2))))
+ listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
AppsSafetyLabelHistoryPersistence.recordSafetyLabel(SAFETY_LABEL_PKG_2_V3, dataFile)
@@ -215,13 +243,20 @@ class AppsSafetyLabelHistoryPersistenceTest {
AppsSafetyLabelHistory(
listOf(
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1)),
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V1)
+ ),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
listOf(
SAFETY_LABEL_PKG_2_V1,
SAFETY_LABEL_PKG_2_V2,
- SAFETY_LABEL_PKG_2_V3)))))
+ SAFETY_LABEL_PKG_2_V3
+ )
+ )
+ )
+ )
+ )
}
@Test
@@ -238,7 +273,11 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(SAFETY_LABEL_PKG_2_V2, SAFETY_LABEL_PKG_2_V3)))))
+ listOf(SAFETY_LABEL_PKG_2_V2, SAFETY_LABEL_PKG_2_V3)
+ )
+ )
+ )
+ )
}
@Test
@@ -249,7 +288,10 @@ class AppsSafetyLabelHistoryPersistenceTest {
AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V2)),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2))))
+ listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
AppsSafetyLabelHistoryPersistence.recordSafetyLabel(SAFETY_LABEL_PKG_1_V3, dataFile)
@@ -264,7 +306,9 @@ class AppsSafetyLabelHistoryPersistenceTest {
AppsSafetyLabelHistory(
listOf(
AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1)),
- AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V1))))
+ AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V1))
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
AppsSafetyLabelHistoryPersistence.recordSafetyLabels(
@@ -272,8 +316,10 @@ class AppsSafetyLabelHistoryPersistenceTest {
SAFETY_LABEL_PKG_1_V2,
SAFETY_LABEL_PKG_2_V2,
SAFETY_LABEL_PKG_2_V3,
- SAFETY_LABEL_PKG_3_V1),
- dataFile)
+ SAFETY_LABEL_PKG_3_V1
+ ),
+ dataFile
+ )
assertThat(AppsSafetyLabelHistoryPersistence.read(dataFile).appsSafetyLabelHistory)
.isEqualTo(
@@ -281,16 +327,23 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)
+ ),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
listOf(
SAFETY_LABEL_PKG_2_V1,
SAFETY_LABEL_PKG_2_V2,
- SAFETY_LABEL_PKG_2_V3)),
+ SAFETY_LABEL_PKG_2_V3
+ )
+ ),
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_3), listOf(SAFETY_LABEL_PKG_3_V1)),
- )))
+ AppInfo(PACKAGE_NAME_3),
+ listOf(SAFETY_LABEL_PKG_3_V1)
+ ),
+ )
+ )
+ )
}
@Test
@@ -308,7 +361,9 @@ class AppsSafetyLabelHistoryPersistenceTest {
val appsSafetyLabelHistory =
AppsSafetyLabelHistory(
listOf(
- AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1))))
+ AppSafetyLabelHistory(AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1))
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
val safetyLabelDiffs: List<AppSafetyLabelDiff> =
@@ -324,7 +379,10 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2))))
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
val safetyLabelDiffs: List<AppSafetyLabelDiff> =
@@ -340,7 +398,10 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(SAFETY_LABEL_PKG_2_V2, SAFETY_LABEL_PKG_2_V3))))
+ listOf(SAFETY_LABEL_PKG_2_V2, SAFETY_LABEL_PKG_2_V3)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
val safetyLabelDiffs: List<AppSafetyLabelDiff> =
@@ -357,11 +418,14 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)
+ ),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(
- SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2, SAFETY_LABEL_PKG_2_V3))))
+ listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2, SAFETY_LABEL_PKG_2_V3)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
val safetyLabelDiffs =
@@ -371,7 +435,9 @@ class AppsSafetyLabelHistoryPersistenceTest {
.isEqualTo(
listOf(
AppSafetyLabelDiff(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2),
- AppSafetyLabelDiff(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V3)))
+ AppSafetyLabelDiff(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V3)
+ )
+ )
}
@Test
@@ -389,11 +455,14 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)
+ ),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(
- SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2, SAFETY_LABEL_PKG_2_V3))))
+ listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2, SAFETY_LABEL_PKG_2_V3)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
val lastUpdatedTimes =
@@ -403,7 +472,9 @@ class AppsSafetyLabelHistoryPersistenceTest {
.isEqualTo(
mapOf(
AppInfo(PACKAGE_NAME_1) to DATE_2022_10_14,
- AppInfo(PACKAGE_NAME_2) to DATE_2022_12_30))
+ AppInfo(PACKAGE_NAME_2) to DATE_2022_12_30
+ )
+ )
}
@Test
@@ -413,14 +484,20 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)
+ ),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2))))
+ listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
AppsSafetyLabelHistoryPersistence.deleteSafetyLabelsForApps(
- setOf(AppInfo(PACKAGE_NAME_2)), dataFile)
+ setOf(AppInfo(PACKAGE_NAME_2)),
+ dataFile
+ )
assertThat(AppsSafetyLabelHistoryPersistence.read(dataFile).appsSafetyLabelHistory)
.isEqualTo(
@@ -428,7 +505,11 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)))))
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2)
+ )
+ )
+ )
+ )
}
@Test
@@ -438,11 +519,14 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(
- SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)
+ ),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2))))
+ listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
AppsSafetyLabelHistoryPersistence.deleteSafetyLabelsOlderThan(DATE_2022_12_30, dataFile)
@@ -452,9 +536,16 @@ class AppsSafetyLabelHistoryPersistenceTest {
AppsSafetyLabelHistory(
listOf(
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V3)),
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V3)
+ ),
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_2), listOf(SAFETY_LABEL_PKG_2_V2)))))
+ AppInfo(PACKAGE_NAME_2),
+ listOf(SAFETY_LABEL_PKG_2_V2)
+ )
+ )
+ )
+ )
}
@Test
@@ -464,11 +555,14 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(
- SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)
+ ),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2))))
+ listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2)
+ )
+ )
+ )
AppsSafetyLabelHistoryPersistence.write(dataFile, appsSafetyLabelHistory)
AppsSafetyLabelHistoryPersistence.deleteSafetyLabelsOlderThan(DATE_2022_10_14, dataFile)
@@ -479,10 +573,15 @@ class AppsSafetyLabelHistoryPersistenceTest {
listOf(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)),
+ listOf(SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)
+ ),
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_2),
- listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2)))))
+ listOf(SAFETY_LABEL_PKG_2_V1, SAFETY_LABEL_PKG_2_V2)
+ )
+ )
+ )
+ )
}
@Test
@@ -531,7 +630,9 @@ class AppsSafetyLabelHistoryPersistenceTest {
DeviceConfig.getInt(
eq(NAMESPACE_PRIVACY),
eq(PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP),
- anyInt()))
+ anyInt()
+ )
+ )
.thenReturn(max)
}
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryTest.kt
index dedb0b1cd..74eba4e4c 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/AppsSafetyLabelHistoryTest.kt
@@ -49,7 +49,9 @@ class AppsSafetyLabelHistoryTest {
fun createAppSafetyLabelHistory_requiresAllSafetyLabelsHaveSameApp() {
Assert.assertThrows(IllegalArgumentException::class.java) {
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_2_V1))
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_2_V1)
+ )
}
}
@@ -57,7 +59,9 @@ class AppsSafetyLabelHistoryTest {
fun createAppSafetyLabelHistory_requiresOrderedByReceivedAt() {
Assert.assertThrows(IllegalArgumentException::class.java) {
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V1))
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V1)
+ )
}
}
@@ -65,7 +69,9 @@ class AppsSafetyLabelHistoryTest {
fun withSafetyLabel_forDifferentApp_throwsIllegalArgumentException() {
val appSafetyLabelHistory =
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V3))
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V3)
+ )
Assert.assertThrows(IllegalArgumentException::class.java) {
appSafetyLabelHistory.withSafetyLabel(SAFETY_LABEL_PKG_2_V1, 20)
@@ -76,25 +82,34 @@ class AppsSafetyLabelHistoryTest {
fun withSafetyLabel_returnsOrderdSafetyLabels() {
val appSafetyLabelHistory =
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V3))
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V3)
+ )
assertThat(appSafetyLabelHistory.withSafetyLabel(SAFETY_LABEL_PKG_1_V2, 20))
.isEqualTo(
AppSafetyLabelHistory(
AppInfo(PACKAGE_NAME_1),
- listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)))
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)
+ )
+ )
}
@Test
fun withSafetyLabel_dropsOldLabelsWhenMaxPersisted() {
val appSafetyLabelHistory =
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V3))
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V1, SAFETY_LABEL_PKG_1_V3)
+ )
assertThat(appSafetyLabelHistory.withSafetyLabel(SAFETY_LABEL_PKG_1_V2, 2))
.isEqualTo(
AppSafetyLabelHistory(
- AppInfo(PACKAGE_NAME_1), listOf(SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)))
+ AppInfo(PACKAGE_NAME_1),
+ listOf(SAFETY_LABEL_PKG_1_V2, SAFETY_LABEL_PKG_1_V3)
+ )
+ )
}
@Test
@@ -105,7 +120,10 @@ class AppsSafetyLabelHistoryTest {
val safetyLabelForPersistence =
SafetyLabel.extractLocationSharingSafetyLabel(
- PACKAGE_NAME_1, DATE_2022_09_01, appMetadataSafetyLabel)
+ PACKAGE_NAME_1,
+ DATE_2022_09_01,
+ appMetadataSafetyLabel
+ )
assertThat(safetyLabelForPersistence)
.isEqualTo(SafetyLabel(AppInfo(PACKAGE_NAME_1), DATE_2022_09_01, DataLabel(mapOf())))
@@ -119,14 +137,19 @@ class AppsSafetyLabelHistoryTest {
val safetyLabelForPersistence =
SafetyLabel.extractLocationSharingSafetyLabel(
- PACKAGE_NAME_1, DATE_2022_10_10, appMetadataSafetyLabel)
+ PACKAGE_NAME_1,
+ DATE_2022_10_10,
+ appMetadataSafetyLabel
+ )
assertThat(safetyLabelForPersistence)
.isEqualTo(
SafetyLabel(
AppInfo(PACKAGE_NAME_1),
DATE_2022_10_10,
- DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(false)))))
+ DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(false)))
+ )
+ )
}
@Test
@@ -137,14 +160,19 @@ class AppsSafetyLabelHistoryTest {
val safetyLabelForPersistence =
SafetyLabel.extractLocationSharingSafetyLabel(
- PACKAGE_NAME_2, DATE_2022_10_10, appMetadataSafetyLabel)
+ PACKAGE_NAME_2,
+ DATE_2022_10_10,
+ appMetadataSafetyLabel
+ )
assertThat(safetyLabelForPersistence)
.isEqualTo(
SafetyLabel(
AppInfo(PACKAGE_NAME_2),
DATE_2022_10_10,
- DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(true)))))
+ DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(true)))
+ )
+ )
}
@Test
@@ -155,7 +183,10 @@ class AppsSafetyLabelHistoryTest {
val safetyLabelForPersistence =
SafetyLabel.extractLocationSharingSafetyLabel(
- PACKAGE_NAME_2, DATE_2022_10_10, appMetadataSafetyLabel)
+ PACKAGE_NAME_2,
+ DATE_2022_10_10,
+ appMetadataSafetyLabel
+ )
assertThat(safetyLabelForPersistence)
.isEqualTo(SafetyLabel(AppInfo(PACKAGE_NAME_2), DATE_2022_10_10, DataLabel(mapOf())))
@@ -170,14 +201,19 @@ class AppsSafetyLabelHistoryTest {
val safetyLabelForPersistence =
SafetyLabel.extractLocationSharingSafetyLabel(
- PACKAGE_NAME_2, DATE_2022_10_10, appMetadataSafetyLabel)
+ PACKAGE_NAME_2,
+ DATE_2022_10_10,
+ appMetadataSafetyLabel
+ )
assertThat(safetyLabelForPersistence)
.isEqualTo(
SafetyLabel(
AppInfo(PACKAGE_NAME_2),
DATE_2022_10_10,
- DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(true)))))
+ DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(true)))
+ )
+ )
}
/** Companion object for [AppsSafetyLabelHistoryTest]. */
@@ -211,8 +247,10 @@ class AppsSafetyLabelHistoryTest {
PersistableBundle().apply {
putIntArray(
PURPOSES_KEY,
- listOf(PURPOSE_FRAUD_PREVENTION_SECURITY).toIntArray())
- })
+ listOf(PURPOSE_FRAUD_PREVENTION_SECURITY).toIntArray()
+ )
+ }
+ )
}
return PersistableBundle().apply {
@@ -227,7 +265,8 @@ class AppsSafetyLabelHistoryTest {
APPROX_LOCATION,
PersistableBundle().apply {
putIntArray(PURPOSES_KEY, listOf(PURPOSE_ADVERTISING).toIntArray())
- })
+ }
+ )
}
return PersistableBundle().apply {
@@ -242,7 +281,8 @@ class AppsSafetyLabelHistoryTest {
FINANCIAL_PURCHASE_HISTORY,
PersistableBundle().apply {
putIntArray(PURPOSES_KEY, listOf(PURPOSE_ADVERTISING).toIntArray())
- })
+ }
+ )
}
return PersistableBundle().apply {
@@ -257,7 +297,8 @@ class AppsSafetyLabelHistoryTest {
APPROX_LOCATION,
PersistableBundle().apply {
putIntArray(PURPOSES_KEY, listOf(PURPOSE_ADVERTISING).toIntArray())
- })
+ }
+ )
}
val financeBundle =
PersistableBundle().apply {
@@ -265,7 +306,8 @@ class AppsSafetyLabelHistoryTest {
FINANCIAL_PURCHASE_HISTORY,
PersistableBundle().apply {
putIntArray(PURPOSES_KEY, listOf(PURPOSE_ADVERTISING).toIntArray())
- })
+ }
+ )
}
return PersistableBundle().apply {
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/SafetyLabelChangesJobServiceTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/SafetyLabelChangesJobServiceTest.kt
index f6395f8ea..4d206a2f4 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/SafetyLabelChangesJobServiceTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/SafetyLabelChangesJobServiceTest.kt
@@ -138,8 +138,8 @@ class SafetyLabelChangesJobServiceTest {
val captor = ArgumentCaptor.forClass(JobInfo::class.java)
verify(mockJobScheduler, times(2)).schedule(captor.capture())
val capturedJobIds = captor.getAllValues()
- assertThat(capturedJobIds[0].id).isEqualTo(
- Constants.SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID)
+ assertThat(capturedJobIds[0].id)
+ .isEqualTo(Constants.SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID)
assertThat(capturedJobIds[1].id)
.isEqualTo(Constants.SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID)
}
@@ -155,7 +155,9 @@ class SafetyLabelChangesJobServiceTest {
DeviceConfig.getBoolean(
eq(DeviceConfig.NAMESPACE_PRIVACY),
eq(SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED),
- anyBoolean()))
+ anyBoolean()
+ )
+ )
.thenReturn(flagValue)
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/TestSafetyLabels.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/TestSafetyLabels.kt
index 19c5adddd..45b57662d 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/TestSafetyLabels.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetylabel/TestSafetyLabels.kt
@@ -42,21 +42,24 @@ object TestSafetyLabels {
SafetyLabel(
AppInfo(PACKAGE_NAME_1),
DATE_2022_09_01,
- DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(true))))
+ DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(true)))
+ )
/** A Safety label for [PACKAGE_NAME_1]. */
val SAFETY_LABEL_PKG_1_V2: SafetyLabel =
SafetyLabel(
AppInfo(PACKAGE_NAME_1),
DATE_2022_10_14,
- DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(false))))
+ DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(false)))
+ )
/** A Safety label for [PACKAGE_NAME_1]. */
val SAFETY_LABEL_PKG_1_V3: SafetyLabel =
SafetyLabel(
AppInfo(PACKAGE_NAME_1),
DATE_2022_12_10,
- DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(false))))
+ DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(false)))
+ )
/** A Safety label for [PACKAGE_NAME_2]. */
val SAFETY_LABEL_PKG_2_V1: SafetyLabel =
@@ -66,7 +69,10 @@ object TestSafetyLabels {
DataLabel(
mapOf(
LOCATION_CATEGORY to DataCategory(true),
- FINANCIAL_CATEGORY to DataCategory(false))))
+ FINANCIAL_CATEGORY to DataCategory(false)
+ )
+ )
+ )
/** A Safety label for [PACKAGE_NAME_2]. */
val SAFETY_LABEL_PKG_2_V2: SafetyLabel =
@@ -77,12 +83,14 @@ object TestSafetyLabels {
SafetyLabel(
AppInfo(PACKAGE_NAME_2),
DATE_2022_12_30,
- DataLabel(mapOf(FINANCIAL_CATEGORY to DataCategory(true))))
+ DataLabel(mapOf(FINANCIAL_CATEGORY to DataCategory(true)))
+ )
/** A Safety label for [PACKAGE_NAME_3]. */
val SAFETY_LABEL_PKG_3_V1: SafetyLabel =
SafetyLabel(
AppInfo(PACKAGE_NAME_3),
DATE_2022_10_10,
- DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(true))))
+ DataLabel(mapOf(LOCATION_CATEGORY to DataCategory(true)))
+ )
}
diff --git a/PermissionController/tests/outofprocess/src/com/android/permissioncontroller/tests/outofprocess/DumpTest.kt b/PermissionController/tests/outofprocess/src/com/android/permissioncontroller/tests/outofprocess/DumpTest.kt
index 4963a4683..707d7678d 100644
--- a/PermissionController/tests/outofprocess/src/com/android/permissioncontroller/tests/outofprocess/DumpTest.kt
+++ b/PermissionController/tests/outofprocess/src/com/android/permissioncontroller/tests/outofprocess/DumpTest.kt
@@ -23,11 +23,11 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.android.permissioncontroller.PermissionControllerProto.PermissionControllerDumpProto
import com.google.common.truth.Truth.assertThat
import com.google.protobuf.InvalidProtocolBufferException
+import java.nio.charset.StandardCharsets.UTF_8
import org.junit.Assert.fail
import org.junit.Assume.assumeTrue
import org.junit.Test
import org.junit.runner.RunWith
-import java.nio.charset.StandardCharsets.UTF_8
@RunWith(AndroidJUnit4::class)
class DumpTest {
@@ -36,8 +36,8 @@ class DumpTest {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
private fun getDump(): PermissionControllerDumpProto {
- val dumpFile = instrumentation.getUiAutomation()
- .executeShellCommand("dumpsys permissionmgr --proto")
+ val dumpFile =
+ instrumentation.getUiAutomation().executeShellCommand("dumpsys permissionmgr --proto")
val dump = AutoCloseInputStream(dumpFile).readBytes()
try {
@@ -66,6 +66,6 @@ class DumpTest {
assumeTrue(dump.autoRevoke.usersList.isNotEmpty())
assertThat(dump.autoRevoke.usersList[myUserId()].packagesList.map { it.packageName })
- .contains(OS_PKG)
+ .contains(OS_PKG)
}
}
diff --git a/PermissionController/tests/permissionui/Android.bp b/PermissionController/tests/permissionui/Android.bp
index 704307c1f..9a898d88b 100644
--- a/PermissionController/tests/permissionui/Android.bp
+++ b/PermissionController/tests/permissionui/Android.bp
@@ -60,6 +60,8 @@ android_test {
":CtsAppThatRequestsLocationPermission29",
":PermissionUiUseStoragePermissionApp",
":PermissionUiUseCameraPermissionApp",
+ ":PermissionUiUseHealthConnectPermissionApp",
+ ":PermissionUiInvalidUseHealthConnectPermissionApp",
":PermissionUiDefineAdditionalPermissionApp",
":PermissionUiUseAdditionalPermissionApp",
":PermissionUiUseTwoAdditionalPermissionsApp",
diff --git a/PermissionController/tests/permissionui/AndroidTest.xml b/PermissionController/tests/permissionui/AndroidTest.xml
index 62fc6ed3f..9cea3b243 100644
--- a/PermissionController/tests/permissionui/AndroidTest.xml
+++ b/PermissionController/tests/permissionui/AndroidTest.xml
@@ -43,6 +43,10 @@
value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiUseStoragePermissionApp.apk" />
<option name="push-file" key="PermissionUiUseCameraPermissionApp.apk"
value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiUseCameraPermissionApp.apk" />
+ <option name="push-file" key="PermissionUiUseHealthConnectPermissionApp.apk"
+ value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiUseHealthConnectPermissionApp.apk" />
+ <option name="push-file" key="PermissionUiInvalidUseHealthConnectPermissionApp.apk"
+ value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp.apk" />
<option name="push-file" key="PermissionUiDefineAdditionalPermissionApp.apk"
value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiDefineAdditionalPermissionApp.apk" />
<option name="push-file" key="PermissionUiUseAdditionalPermissionApp.apk"
diff --git a/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/Android.bp b/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/Android.bp
new file mode 100644
index 000000000..c366f9a0c
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "packages_modules_Permission_PermissionController_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "packages_modules_Permission_PermissionController_license",
+ ],
+}
+
+android_test_helper_app {
+ name: "PermissionUiInvalidUseHealthConnectPermissionApp",
+
+ srcs: ["src/**/*.kt"],
+
+ sdk_version: "34",
+}
diff --git a/PermissionController/res/values-w764dp-v33/dimens.xml b/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/AndroidManifest.xml
index 78b4675f2..c5e0f4906 100644
--- a/PermissionController/res/values-w764dp-v33/dimens.xml
+++ b/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/AndroidManifest.xml
@@ -1,5 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2022 The Android Open Source Project
+ ~ Copyright (C) 2020 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.
@@ -14,6 +15,12 @@
~ limitations under the License.
-->
-<resources>
- <dimen name="sc_button_horizontal_padding">@dimen/sc_large_screen_button_padding</dimen>
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.permissioncontroller.tests.appthatrequestpermission">
+
+ <uses-permission android:name="android.permission.health.READ_FLOORS_CLIMBED" />
+ <uses-permission android:name="android.permission.health.READ_STEPS" />
+
+ <application android:label="HealthConnectRequestApp" />
+</manifest>
+
diff --git a/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt b/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt
new file mode 100644
index 000000000..98089208a
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.tests.appthatrequestpermission
+
+import android.app.Activity
+
+class DummyActivity : Activity()
diff --git a/PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/Android.bp b/PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/Android.bp
new file mode 100644
index 000000000..10788a116
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "packages_modules_Permission_PermissionController_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "packages_modules_Permission_PermissionController_license",
+ ],
+}
+
+android_test_helper_app {
+ name: "PermissionUiUseHealthConnectPermissionApp",
+
+ srcs: ["src/**/*.kt"],
+
+ sdk_version: "34",
+}
diff --git a/PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/AndroidManifest.xml b/PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/AndroidManifest.xml
new file mode 100644
index 000000000..d5903a543
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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="com.android.permissioncontroller.tests.appthatrequestpermission">
+
+ <uses-permission android:name="android.permission.health.READ_FLOORS_CLIMBED" />
+ <uses-permission android:name="android.permission.health.READ_STEPS" />
+
+ <application android:label="HealthConnectRequestApp">
+ <activity android:name=".DummyActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
+ <category android:name="android.intent.category.HEALTH_PERMISSIONS" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+
diff --git a/PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt b/PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt
new file mode 100644
index 000000000..98089208a
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiUseHealthConnectPermissionApp/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.tests.appthatrequestpermission
+
+import android.app.Activity
+
+class DummyActivity : Activity()
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionGroupPreferenceUtils.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionGroupPreferenceUtils.kt
index 175b7a701..8e1d6a831 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionGroupPreferenceUtils.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionGroupPreferenceUtils.kt
@@ -26,31 +26,33 @@ private const val SUMMARY_TEXT = "apps allowed"
* Read the {@link UsageCount} of the group of the permission from the Ui.
*
* @param groupLabel label fo the group the count should be read for
- *
* @return usage counts for the group of the permission
*/
fun getUsageCountsFromUi(groupLabel: CharSequence): UsageCount {
waitFindObject(By.text(groupLabel.toString()))
return getEventually {
- val summaryText = waitFindObject(By.hasChild(By.text(groupLabel.toString()))
- .hasChild(By.textContains(SUMMARY_TEXT))).findObject(By.textContains(SUMMARY_TEXT)).text
+ val summaryText =
+ waitFindObject(
+ By.hasChild(By.text(groupLabel.toString()))
+ .hasChild(By.textContains(SUMMARY_TEXT))
+ )
+ .findObject(By.textContains(SUMMARY_TEXT))
+ .text
// Matches two numbers out of the summary line, i.e. "...3...12..." -> "3", "12"
- val groups = Regex("^[^\\d]*(\\d+)[^\\d]*(\\d+)[^\\d]*\$")
- .find(summaryText)?.groupValues
- ?: throw Exception("No usage counts found")
+ val groups =
+ Regex("^[^\\d]*(\\d+)[^\\d]*(\\d+)[^\\d]*\$").find(summaryText)?.groupValues
+ ?: throw Exception("No usage counts found")
UsageCount(groups[1].toInt(), groups[2].toInt())
}
}
-/**
- * Usage counts as read via {@link #getUsageCountsFromUi}.
- */
+/** Usage counts as read via {@link #getUsageCountsFromUi}. */
data class UsageCount(
/** Number of apps with permission granted */
val granted: Int,
/** Number of apps that request permissions */
val total: Int
-) \ No newline at end of file
+)
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionHub2Test.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionHub2Test.kt
index 6f143d2db..0b1269a2f 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionHub2Test.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionHub2Test.kt
@@ -35,9 +35,7 @@ import com.google.common.truth.Truth.assertThat
import org.junit.AfterClass
import org.junit.BeforeClass
-/**
- * Super class with utilities for testing permission hub 2 code
- */
+/** Super class with utilities for testing permission hub 2 code */
open class PermissionHub2Test {
private val APP = "com.android.permissioncontroller.tests.appthatrequestpermission"
@@ -54,13 +52,18 @@ open class PermissionHub2Test {
fun enablePermissionHub2() {
runWithShellPermissionIdentity {
- wasPermissionHubEnabled = DeviceConfig.getBoolean(NAMESPACE_PRIVACY,
- PROPERTY_PERMISSIONS_HUB_2_ENABLED, false)
+ wasPermissionHubEnabled =
+ DeviceConfig.getBoolean(
+ NAMESPACE_PRIVACY,
+ PROPERTY_PERMISSIONS_HUB_2_ENABLED,
+ false
+ )
}
if (!wasPermissionHubEnabled) {
runShellCommand(
- "device_config put privacy $PROPERTY_PERMISSIONS_HUB_2_ENABLED true")
+ "device_config put privacy $PROPERTY_PERMISSIONS_HUB_2_ENABLED true"
+ )
}
}
@@ -69,34 +72,43 @@ open class PermissionHub2Test {
fun disablePermissionHub2() {
if (!wasPermissionHubEnabled) {
runShellCommand(
- "device_config put privacy $PROPERTY_PERMISSIONS_HUB_2_ENABLED false")
+ "device_config put privacy $PROPERTY_PERMISSIONS_HUB_2_ENABLED false"
+ )
}
}
}
- /**
- * Make {@value #APP} access the camera
- */
+ /** Make {@value #APP} access the camera */
protected fun accessCamera() {
// App needs to be in foreground to be able to access camera
context.startActivity(
- Intent().setComponent(ComponentName.createRelative(APP, ".DummyActivity"))
- .setFlags(FLAG_ACTIVITY_NEW_TASK))
+ Intent()
+ .setComponent(ComponentName.createRelative(APP, ".DummyActivity"))
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ )
runWithShellPermissionIdentity {
eventually {
assertThat(
- context.packageManager.getPermissionFlags(CAMERA, APP, myUserHandle()) and
- FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
- ).isNotEqualTo(0)
+ context.packageManager.getPermissionFlags(CAMERA, APP, myUserHandle()) and
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+ )
+ .isNotEqualTo(0)
}
eventually {
assertThat(
- context.getSystemService(AppOpsManager::class.java).startOp(
- OPSTR_CAMERA, context.packageManager.getPackageUid(APP, 0), APP, null, null
+ context
+ .getSystemService(AppOpsManager::class.java)
+ .startOp(
+ OPSTR_CAMERA,
+ context.packageManager.getPackageUid(APP, 0),
+ APP,
+ null,
+ null
+ )
)
- ).isEqualTo(MODE_ALLOWED)
+ .isEqualTo(MODE_ALLOWED)
}
}
}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/UiUtils.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/UiUtils.kt
index 25aa79632..fa741d3eb 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/UiUtils.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/UiUtils.kt
@@ -16,12 +16,10 @@
package com.android.permissioncontroller.permissionui
-import androidx.test.uiautomator.UiDevice
import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
-/**
- * Wake up screen
- */
+/** Wake up screen */
fun wakeUpScreen() {
val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
@@ -29,9 +27,7 @@ fun wakeUpScreen() {
uiDevice.executeShellCommand("wm dismiss-keyguard")
}
-/**
- * Press the home button
- */
+/** Press the home button */
fun pressHome() {
val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
uiDevice.pressHome()
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AllAppPermissionsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AllAppPermissionsFragmentTest.kt
index d5f574327..b460493ff 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AllAppPermissionsFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AllAppPermissionsFragmentTest.kt
@@ -19,8 +19,8 @@ package com.android.permissioncontroller.permissionui.ui
import android.content.Intent
import android.permission.cts.PermissionUtils.install
import android.permission.cts.PermissionUtils.uninstallApp
-import androidx.test.uiautomator.By
import androidx.test.ext.junit.runners.AndroidJUnit4
+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
@@ -37,11 +37,9 @@ private const val MORE_OPTIONS = "More options"
private const val ALL_PERMISSIONS = "All permissions"
/**
- * Simple tests for {@link AllAppPermissionsFragment}
- * Currently, does NOT run on TV.
- * TODO(b/178576541): Adapt and run on TV.
- * Run with:
- * atest AllAppPermissionsFragmentTest
+ * Simple tests for {@link AllAppPermissionsFragment} Currently, does NOT run on TV.
+ *
+ * TODO(b/178576541): Adapt and run on TV. Run with: atest AllAppPermissionsFragmentTest
*/
@RunWith(AndroidJUnit4::class)
class AllAppPermissionsFragmentTest : BasePermissionUiTest() {
@@ -62,8 +60,7 @@ class AllAppPermissionsFragmentTest : BasePermissionUiTest() {
private val TIMEOUT_SHORT = 500L
- @Before
- fun assumeNotTelevision() = assumeFalse(isTelevision)
+ @Before fun assumeNotTelevision() = assumeFalse(isTelevision)
@Before
fun wakeScreenUp() {
@@ -76,12 +73,13 @@ class AllAppPermissionsFragmentTest : BasePermissionUiTest() {
install(PERMISSION_USER_APK)
runWithShellPermissionIdentity {
- instrumentationContext.startActivity(Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
- .apply {
+ instrumentationContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
putExtra(Intent.EXTRA_PACKAGE_NAME, USER_PKG)
- })
+ }
+ )
}
waitFindObject(By.descContains(MORE_OPTIONS)).click()
@@ -98,30 +96,22 @@ class AllAppPermissionsFragmentTest : BasePermissionUiTest() {
waitFindObject(By.text(PERM_LABEL))
install(TWO_PERMISSION_USER_APK)
- eventually {
- waitFindObject(By.text(SECOND_PERM_LABEL))
- }
+ eventually { waitFindObject(By.text(SECOND_PERM_LABEL)) }
}
@Test
fun permissionsAreRemovedWhenAppIsUpdated() {
install(TWO_PERMISSION_USER_APK)
- eventually {
- waitFindObject(By.text(SECOND_PERM_LABEL))
- }
+ eventually { waitFindObject(By.text(SECOND_PERM_LABEL)) }
install(PERMISSION_USER_APK)
- eventually {
- assertNull(waitFindObjectOrNull(By.text(SECOND_PERM_LABEL), TIMEOUT_SHORT))
- }
+ eventually { assertNull(waitFindObjectOrNull(By.text(SECOND_PERM_LABEL), TIMEOUT_SHORT)) }
}
@Test
fun activityIsClosedWhenUserIsUninstalled() {
uninstallApp(USER_PKG)
- eventually {
- assertNull(waitFindObjectOrNull(By.text(ALL_PERMISSIONS), TIMEOUT_SHORT))
- }
+ eventually { assertNull(waitFindObjectOrNull(By.text(ALL_PERMISSIONS), TIMEOUT_SHORT)) }
}
@After
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AppPermissionFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AppPermissionFragmentTest.kt
index 7b058d004..b4893dc83 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AppPermissionFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AppPermissionFragmentTest.kt
@@ -21,8 +21,8 @@ import android.os.UserHandle
import android.os.UserHandle.myUserId
import android.permission.cts.PermissionUtils.install
import android.permission.cts.PermissionUtils.uninstallApp
-import androidx.test.uiautomator.By
import androidx.test.ext.junit.runners.AndroidJUnit4
+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.waitFindObjectOrNull
@@ -37,11 +37,9 @@ import org.junit.runner.RunWith
private const val APP_PERMISSIONS = "App permissions"
/**
- * Simple tests for {@link AppPermissionFragment}
- * Currently, does NOT run on TV.
- * TODO(b/178576541): Adapt and run on TV.
- * Run with:
- * atest AppPermissionFragmentTest
+ * Simple tests for {@link AppPermissionFragment} Currently, does NOT run on TV.
+ *
+ * TODO(b/178576541): Adapt and run on TV. Run with: atest AppPermissionFragmentTest
*/
@RunWith(AndroidJUnit4::class)
class AppPermissionFragmentTest : BasePermissionUiTest() {
@@ -56,8 +54,7 @@ class AppPermissionFragmentTest : BasePermissionUiTest() {
private val PERM = "com.android.permissioncontroller.tests.A"
- @Before
- fun assumeNotTelevision() = assumeFalse(isTelevision)
+ @Before fun assumeNotTelevision() = assumeFalse(isTelevision)
@Before
fun wakeScreenUp() {
@@ -73,31 +70,29 @@ class AppPermissionFragmentTest : BasePermissionUiTest() {
@Before
fun startManagePermissionAppsActivity() {
runWithShellPermissionIdentity {
- instrumentationContext.startActivity(Intent(Intent.ACTION_MANAGE_APP_PERMISSION).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- putExtra(Intent.EXTRA_PACKAGE_NAME, USER_PKG)
- putExtra(Intent.EXTRA_PERMISSION_NAME, PERM)
- putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, PERM)
- putExtra(Intent.EXTRA_USER, UserHandle.of(myUserId()))
- putExtra("com.android.permissioncontroller.extra.CALLER_NAME", "")
- })
+ instrumentationContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSION).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ putExtra(Intent.EXTRA_PACKAGE_NAME, USER_PKG)
+ putExtra(Intent.EXTRA_PERMISSION_NAME, PERM)
+ putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, PERM)
+ putExtra(Intent.EXTRA_USER, UserHandle.of(myUserId()))
+ putExtra("com.android.permissioncontroller.extra.CALLER_NAME", "")
+ }
+ )
}
}
@Test
fun activityIsClosedWhenUserIsUninstalled() {
uninstallApp(USER_PKG)
- eventually {
- assertNull(waitFindObjectOrNull(By.text(APP_PERMISSIONS)))
- }
+ eventually { assertNull(waitFindObjectOrNull(By.text(APP_PERMISSIONS))) }
}
@Test
fun activityIsClosedWhenDefinerIsUninstalled() {
uninstallApp(DEFINER_PKG)
- eventually {
- assertNull(waitFindObjectOrNull(By.text(APP_PERMISSIONS)))
- }
+ eventually { assertNull(waitFindObjectOrNull(By.text(APP_PERMISSIONS))) }
}
@After
@@ -105,4 +100,4 @@ class AppPermissionFragmentTest : BasePermissionUiTest() {
uninstallApp(DEFINER_PKG)
uninstallApp(USER_PKG)
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/BasePermissionUiTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/BasePermissionUiTest.kt
index 7d2c644c5..2cb7e7a5c 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/BasePermissionUiTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/BasePermissionUiTest.kt
@@ -29,11 +29,11 @@ abstract class BasePermissionUiTest {
protected val instrumentationContext = instrumentation.context!!
protected val targetContext = instrumentation.targetContext!!
private val packageManager = instrumentationContext.packageManager!!
- protected val isTelevision = packageManager.run {
- hasSystemFeature(PackageManager.FEATURE_LEANBACK) ||
- hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)
- }
+ protected val isTelevision =
+ packageManager.run {
+ hasSystemFeature(PackageManager.FEATURE_LEANBACK) ||
+ hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)
+ }
- @Rule
- fun disableAnimationsRule() = DisableAnimationRule()
-} \ No newline at end of file
+ @Rule fun disableAnimationsRule() = DisableAnimationRule()
+}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/CustomPermissionAppsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/CustomPermissionAppsFragmentTest.kt
index 988cbca73..fb1214d70 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/CustomPermissionAppsFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/CustomPermissionAppsFragmentTest.kt
@@ -17,8 +17,8 @@
package com.android.permissioncontroller.permissionui.ui
import android.permission.cts.PermissionUtils.uninstallApp
-import androidx.test.uiautomator.By
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.uiautomator.By
import com.android.compatibility.common.util.SystemUtil.eventually
import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull
import org.junit.Assert.assertNull
@@ -32,24 +32,24 @@ private const val PERMISSION_APPS_DESCRIPTION = "Apps with this permission"
* Simple tests for {@link PermissionAppsFragment} when showing custom permission
*
* Currently, does NOT run on TV (same as the other tests that extend [PermissionAppsFragmentTest]).
+ *
* TODO(b/178576541): Adapt and run on TV.
*/
@RunWith(AndroidJUnit4::class)
-class CustomPermissionAppsFragmentTest : PermissionAppsFragmentTest(
- "/data/local/tmp/permissioncontroller/tests/permissionui" +
- "/PermissionUiUseAdditionalPermissionApp.apk",
- "com.android.permissioncontroller.tests.appthatrequestpermission",
- "com.android.permissioncontroller.tests.A",
- "/data/local/tmp/permissioncontroller/tests/permissionui" +
- "/PermissionUiDefineAdditionalPermissionApp.apk",
- "com.android.permissioncontroller.tests.appthatdefinespermission"
-) {
+class CustomPermissionAppsFragmentTest :
+ PermissionAppsFragmentTest(
+ "/data/local/tmp/permissioncontroller/tests/permissionui" +
+ "/PermissionUiUseAdditionalPermissionApp.apk",
+ "com.android.permissioncontroller.tests.appthatrequestpermission",
+ "com.android.permissioncontroller.tests.A",
+ "/data/local/tmp/permissioncontroller/tests/permissionui" +
+ "/PermissionUiDefineAdditionalPermissionApp.apk",
+ "com.android.permissioncontroller.tests.appthatdefinespermission"
+ ) {
@Ignore("b/155112992")
@Test
fun fragmentIsClosedWhenPermissionIsRemoved() {
uninstallApp(definerApk!!)
- eventually {
- assertNull(waitFindObjectOrNull(By.text(PERMISSION_APPS_DESCRIPTION)))
- }
+ eventually { assertNull(waitFindObjectOrNull(By.text(PERMISSION_APPS_DESCRIPTION))) }
}
}
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
new file mode 100644
index 000000000..2bf39aab3
--- /dev/null
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.permissionui.ui
+
+import android.content.Intent
+import android.os.Build
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+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.wakeUpScreen
+import org.junit.After
+import org.junit.Assert.assertNull
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Simple tests for {@link AllAppPermissionsFragment} for Health Connect behaviors Currently, does
+ * NOT run on TV.
+ *
+ * TODO(b/178576541): Adapt and run on TV. Run with: atest HealthConnectAllAppPermissionFragmentTest
+ */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class HealthConnectAllAppPermissionFragmentTest : BasePermissionUiTest() {
+ @Before fun assumeNotTelevision() = assumeFalse(isTelevision)
+
+ @Before
+ fun wakeScreenUp() {
+ wakeUpScreen()
+ }
+
+ @After
+ fun uninstallTestApp() {
+ uninstallTestApps()
+ }
+ @Test
+ fun usedHealthConnectPermissionsAreListed() {
+ installTestAppThatUsesHealthConnectPermission()
+
+ startManageAppPermissionsActivity()
+
+ eventually {
+ waitFindObject(By.text(HEALTH_CONNECT_LABEL))
+ waitFindObject(By.text(HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED_LABEL))
+ waitFindObject(By.text(HEALTH_CONNECT_PERMISSION_READ_STEPS_LABEL))
+ }
+ }
+
+ @Test
+ fun invalidUngrantedUsedHealthConnectPermissionsAreNotListed() {
+ installInvalidTestAppThatUsesHealthConnectPermission()
+
+ startManageAppPermissionsActivity()
+
+ eventually {
+ assertNull(waitFindObjectOrNull(By.text(HEALTH_CONNECT_LABEL), TIMEOUT_SHORT))
+ assertNull(
+ waitFindObjectOrNull(
+ By.text(HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED_LABEL),
+ TIMEOUT_SHORT
+ )
+ )
+ assertNull(
+ waitFindObjectOrNull(
+ By.text(HEALTH_CONNECT_PERMISSION_READ_STEPS_LABEL),
+ TIMEOUT_SHORT
+ )
+ )
+ }
+ }
+
+ @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() {
+ runWithShellPermissionIdentity {
+ instrumentationContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ putExtra(Intent.EXTRA_PACKAGE_NAME, PERM_USER_PACKAGE)
+ }
+ )
+ }
+
+ waitFindObject(By.descContains(MORE_OPTIONS)).click()
+ waitFindObject(By.text(ALL_PERMISSIONS)).click()
+ }
+
+ companion object {
+ // Health connect label uses a non breaking space
+ private const val HEALTH_CONNECT_LABEL = "Health\u00A0Connect"
+ private const val HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED =
+ "android.permission.health.READ_FLOORS_CLIMBED"
+ private const val HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED_LABEL =
+ "Read floors climbed"
+ private const val HEALTH_CONNECT_PERMISSION_READ_STEPS_LABEL = "Read steps"
+
+ private const val MORE_OPTIONS = "More options"
+ private const val ALL_PERMISSIONS = "All permissions"
+
+ private val TIMEOUT_SHORT = 500L
+ }
+}
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
new file mode 100644
index 000000000..640354e56
--- /dev/null
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.permissionui.ui
+
+import android.content.Intent
+import android.os.Build
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+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.wakeUpScreen
+import org.junit.After
+import org.junit.Assert.assertNull
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Simple tests for {@link AppPermissionsFragment} for Health Connect behaviors Currently, does NOT
+ * run on TV.
+ *
+ * TODO(b/178576541): Adapt and run on TV. Run with: atest HealthConnectAppPermissionFragmentTest
+ */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class HealthConnectAppPermissionFragmentTest : BasePermissionUiTest() {
+ @Before fun assumeNotTelevision() = assumeFalse(isTelevision)
+
+ @Before
+ fun wakeScreenUp() {
+ wakeUpScreen()
+ }
+
+ @After
+ fun uninstallTestApp() {
+ uninstallTestApps()
+ }
+ @Test
+ fun usedHealthConnectPermissionsAreListed() {
+ installTestAppThatUsesHealthConnectPermission()
+
+ startManageAppPermissionsActivity()
+
+ eventually { waitFindObject(By.text(HEALTH_CONNECT_LABEL)) }
+ }
+
+ @Test
+ fun invalidUngrantedUsedHealthConnectPermissionsAreNotListed() {
+ installInvalidTestAppThatUsesHealthConnectPermission()
+
+ startManageAppPermissionsActivity()
+
+ // TODO(b/288286032): update to use waitUntilObjectGone
+ eventually {
+ assertNull(waitFindObjectOrNull(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(
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ putExtra(Intent.EXTRA_PACKAGE_NAME, PERM_USER_PACKAGE)
+ }
+ )
+ }
+ }
+
+ companion object {
+ // Health connect label uses a non breaking space
+ private const val HEALTH_CONNECT_LABEL = "Health\u00A0Connect"
+ private const val HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED =
+ "android.permission.health.READ_FLOORS_CLIMBED"
+
+ private val TIMEOUT_SHORT = 500L
+ }
+}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/LocationPermissionAppsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/LocationPermissionAppsFragmentTest.kt
index 3b48d7235..c1cfd30af 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/LocationPermissionAppsFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/LocationPermissionAppsFragmentTest.kt
@@ -24,11 +24,13 @@ import org.junit.runner.RunWith
* Simple tests for {@link PermissionAppsFragment} when showing location permission
*
* Currently, does NOT run on TV (same as the other tests that extend [PermissionAppsFragmentTest]).
+ *
* TODO(b/178576541): Adapt and run on TV.
*/
@RunWith(AndroidJUnit4::class)
-class LocationPermissionAppsFragmentTest : PermissionAppsFragmentTest(
- "/data/local/tmp/permissioncontroller/tests/permissionui/AppThatRequestsLocation.apk",
- "android.permission.cts.appthatrequestpermission",
- ACCESS_COARSE_LOCATION
-)
+class LocationPermissionAppsFragmentTest :
+ PermissionAppsFragmentTest(
+ "/data/local/tmp/permissioncontroller/tests/permissionui/AppThatRequestsLocation.apk",
+ "android.permission.cts.appthatrequestpermission",
+ ACCESS_COARSE_LOCATION
+ )
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/PermissionAppsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/PermissionAppsFragmentTest.kt
index a5453d9e7..14c77776c 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/PermissionAppsFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/PermissionAppsFragmentTest.kt
@@ -37,6 +37,7 @@ import org.junit.Test
* <p>Leave abstract to prevent the test runner from trying to run it
*
* Currently, none of the tests that extend [PermissionAppsFragmentTest] run on TV.
+ *
* TODO(b/178576541): Adapt and run on TV.
*/
abstract class PermissionAppsFragmentTest(
@@ -47,8 +48,7 @@ abstract class PermissionAppsFragmentTest(
val definerPkg: String? = null
) : BasePermissionUiTest() {
- @Before
- fun assumeNotTelevision() = assumeFalse(isTelevision)
+ @Before fun assumeNotTelevision() = assumeFalse(isTelevision)
@Before
fun wakeScreenUp() {
@@ -65,11 +65,12 @@ abstract class PermissionAppsFragmentTest(
@Before
fun startManagePermissionAppsActivity() {
runWithShellPermissionIdentity {
- instrumentationContext.startActivity(Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
- .apply {
+ instrumentationContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_PERMISSION_APPS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(Intent.EXTRA_PERMISSION_NAME, perm)
- })
+ }
+ )
}
}
@@ -78,9 +79,7 @@ abstract class PermissionAppsFragmentTest(
assertNull(waitFindObjectOrNull(By.text(userPkg)))
install(userApk)
- eventually {
- waitFindObject(By.text(userPkg))
- }
+ eventually { waitFindObject(By.text(userPkg)) }
}
// TODO(b/280652042) Slow tests aren't good
@@ -89,14 +88,10 @@ abstract class PermissionAppsFragmentTest(
assertNull(waitFindObjectOrNull(By.text(userPkg)))
install(userApk)
- eventually {
- waitFindObject(By.text(userPkg))
- }
+ eventually { waitFindObject(By.text(userPkg)) }
uninstallApp(userPkg)
- eventually {
- assertNull(waitFindObjectOrNull(By.text(userPkg)))
- }
+ eventually { assertNull(waitFindObjectOrNull(By.text(userPkg))) }
}
@After
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/StoragePermissionAppsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/StoragePermissionAppsFragmentTest.kt
index ada143613..5046bea85 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/StoragePermissionAppsFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/StoragePermissionAppsFragmentTest.kt
@@ -24,12 +24,14 @@ import org.junit.runner.RunWith
* Simple tests for {@link PermissionAppsFragment} when showing location permission
*
* Currently, does NOT run on TV (same as the other tests that extend [PermissionAppsFragmentTest]).
+ *
* TODO(b/178576541): Adapt and run on TV.
*/
@RunWith(AndroidJUnit4::class)
-class StoragePermissionAppsFragmentTest : PermissionAppsFragmentTest(
- "/data/local/tmp/permissioncontroller/tests/permissionui" +
- "/PermissionUiUseStoragePermissionApp.apk",
- "com.android.permissioncontroller.tests.appthatrequestpermission",
- READ_EXTERNAL_STORAGE
-)
+class StoragePermissionAppsFragmentTest :
+ PermissionAppsFragmentTest(
+ "/data/local/tmp/permissioncontroller/tests/permissionui" +
+ "/PermissionUiUseStoragePermissionApp.apk",
+ "com.android.permissioncontroller.tests.appthatrequestpermission",
+ READ_EXTERNAL_STORAGE
+ )
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt
index 06e25485e..e715b03e3 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt
@@ -24,6 +24,10 @@ import android.permission.cts.PermissionUtils.uninstallApp
private const val APK_DIRECTORY = "/data/local/tmp/permissioncontroller/tests/permissionui/"
private const val LOCATION_PERM_USER_APK = "$APK_DIRECTORY/AppThatRequestsLocation.apk"
private const val CAMERA_PERM_USER_APK = "$APK_DIRECTORY/PermissionUiUseCameraPermissionApp.apk"
+private const val HEALTH_CONNECT_PERMISSION_USER_APK =
+ "$APK_DIRECTORY/PermissionUiUseHealthConnectPermissionApp.apk"
+private const val INVALID_HEALTH_CONNECT_PERMISSION_USER_APK =
+ "$APK_DIRECTORY/PermissionUiInvalidUseHealthConnectPermissionApp.apk"
private const val ADDITIONAL_PERM_USER_APK =
"$APK_DIRECTORY/PermissionUiUseAdditionalPermissionApp.apk"
private const val TWO_ADDITIONAL_PERM_USER_APK =
@@ -32,10 +36,9 @@ private const val ADDITIONAL_PERM_DEFINER_APK =
"$APK_DIRECTORY/PermissionUiDefineAdditionalPermissionApp.apk"
// All 4 of the AppThatUses_X_Permission(s) applications share the same package name.
-private const val PERM_USER_PACKAGE =
- "com.android.permissioncontroller.tests.appthatrequestpermission"
private const val PERM_DEFINER_PACKAGE =
"com.android.permissioncontroller.tests.appthatdefinespermission"
+const val PERM_USER_PACKAGE = "com.android.permissioncontroller.tests.appthatrequestpermission"
const val CAMERA_TEST_APP_LABEL = "CameraRequestApp"
@@ -48,9 +51,18 @@ const val TEST_APP_DEFINED_PERMISSION_B_LABEL = "Permission B"
const val TEST_APP_DEFINED_PERMISSION_C_LABEL = "Permission C"
fun installTestAppThatRequestsLocation() = install(LOCATION_PERM_USER_APK)
+
fun installTestAppThatUsesCameraPermission() = install(CAMERA_PERM_USER_APK)
+
+fun installTestAppThatUsesHealthConnectPermission() = install(HEALTH_CONNECT_PERMISSION_USER_APK)
+
+fun installInvalidTestAppThatUsesHealthConnectPermission() =
+ install(INVALID_HEALTH_CONNECT_PERMISSION_USER_APK)
+
fun installTestAppThatUsesAdditionalPermission() = install(ADDITIONAL_PERM_USER_APK)
+
fun installTestAppThatUsesTwoAdditionalPermissions() = install(TWO_ADDITIONAL_PERM_USER_APK)
+
fun installTestAppThatDefinesAdditionalPermissions() = install(ADDITIONAL_PERM_DEFINER_APK)
fun uninstallTestApps() {
@@ -58,4 +70,4 @@ fun uninstallTestApps() {
uninstallApp(PERM_DEFINER_PACKAGE)
}
-fun grantTestAppPermission(permission: String) = grantPermission(PERM_USER_PACKAGE, permission) \ No newline at end of file
+fun grantTestAppPermission(permission: String) = grantPermission(PERM_USER_PACKAGE, permission)
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/BaseHandheldPermissionUiTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/BaseHandheldPermissionUiTest.kt
index 66e254155..7f005e489 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/BaseHandheldPermissionUiTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/BaseHandheldPermissionUiTest.kt
@@ -21,6 +21,5 @@ import org.junit.Assume.assumeFalse
import org.junit.Before
abstract class BaseHandheldPermissionUiTest : BasePermissionUiTest() {
- @Before
- fun assumeNotTelevision() = assumeFalse(isTelevision)
-} \ No newline at end of file
+ @Before fun assumeNotTelevision() = assumeFalse(isTelevision)
+}
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 c41dacc96..4f2360aaa 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
@@ -21,8 +21,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 androidx.test.uiautomator.By
import androidx.test.ext.junit.runners.AndroidJUnit4
+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
@@ -34,9 +34,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-/**
- * Simple tests for {@link ManageCustomPermissionsFragment}
- */
+/** Simple tests for {@link ManageCustomPermissionsFragment} */
@RunWith(AndroidJUnit4::class)
class ManageCustomPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
private val ONE_PERMISSION_DEFINER_APK =
@@ -57,9 +55,11 @@ class ManageCustomPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
wakeUpScreen()
runWithShellPermissionIdentity {
- instrumentationContext.startActivity(Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- })
+ instrumentationContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
}
}
@@ -88,14 +88,10 @@ class ManageCustomPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
val original = getUsageCountsFromUi(PERM_LABEL)
grantPermission(USER_PKG, PERM)
- eventually {
- assertThat(getUsageCountsFromUi(PERM_LABEL)).isNotEqualTo(original)
- }
+ eventually { assertThat(getUsageCountsFromUi(PERM_LABEL)).isNotEqualTo(original) }
revokePermission(USER_PKG, PERM)
- eventually {
- assertThat(getUsageCountsFromUi(PERM_LABEL)).isEqualTo(original)
- }
+ eventually { assertThat(getUsageCountsFromUi(PERM_LABEL)).isEqualTo(original) }
}
@After
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 5d0fae9a1..0181903aa 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
@@ -22,8 +22,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 androidx.test.uiautomator.By
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.uiautomator.By
import com.android.compatibility.common.util.SystemUtil.eventually
import com.android.compatibility.common.util.SystemUtil.getEventually
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
@@ -36,9 +36,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-/**
- * Simple tests for {@link ManageStandardPermissionsFragment}
- */
+/** Simple tests for {@link ManageStandardPermissionsFragment} */
@RunWith(AndroidJUnit4::class)
class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
private val LOCATION_USER_APK =
@@ -68,14 +66,15 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
waitFindObjectOrNull(By.textContains(ADDITIONAL_PERMISSIONS_LABEL)) ?: return 0
// Sometimes the entire preference disappears while it's searching for the app count
// (during uninstalling). Hence also return the count as 0 if count doesn't exist
- val additionalPermissionsSummary = waitFindObjectOrNull(
- By.textContains(ADDITIONAL_PERMISSIONS_SUMMARY)) ?: return 0
+ val additionalPermissionsSummary =
+ waitFindObjectOrNull(By.textContains(ADDITIONAL_PERMISSIONS_SUMMARY)) ?: return 0
val additionalPermissionsSummaryText = additionalPermissionsSummary.getText()
// Matches a single number out of the summary line, i.e. "...3..." -> "3"
return getEventually {
Regex("^[^\\d]*(\\d+)[^\\d]*\$")
- .find(additionalPermissionsSummaryText)!!.groupValues[1]
+ .find(additionalPermissionsSummaryText)!!
+ .groupValues[1]
.toInt()
}
}
@@ -85,9 +84,11 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
wakeUpScreen()
runWithShellPermissionIdentity {
- instrumentationContext.startActivity(Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- })
+ instrumentationContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
}
reuninstallApp(LOCATION_USER_APK, LOCATION_USER_PKG)
@@ -114,15 +115,11 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
val original = getUsageCountsFromUi(locationGroupLabel)
install(LOCATION_USER_APK)
- eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel)).isNotEqualTo(original)
- }
+ eventually { assertThat(getUsageCountsFromUi(locationGroupLabel)).isNotEqualTo(original) }
uninstallApp(LOCATION_USER_PKG)
reuninstallApp(LOCATION_USER_APK, LOCATION_USER_PKG)
- eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel)).isEqualTo(original)
- }
+ eventually { assertThat(getUsageCountsFromUi(locationGroupLabel)).isEqualTo(original) }
}
@Test
@@ -131,8 +128,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(LOCATION_USER_APK)
eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel).total)
- .isEqualTo(original.total + 1)
+ assertThat(getUsageCountsFromUi(locationGroupLabel).total).isEqualTo(original.total + 1)
}
grantPermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
@@ -149,16 +145,14 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(LOCATION_USER_APK)
grantPermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel).total)
- .isNotEqualTo(original.total)
+ assertThat(getUsageCountsFromUi(locationGroupLabel).total).isNotEqualTo(original.total)
assertThat(getUsageCountsFromUi(locationGroupLabel).granted)
.isNotEqualTo(original.granted)
}
revokePermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel).granted)
- .isEqualTo(original.granted)
+ assertThat(getUsageCountsFromUi(locationGroupLabel).granted).isEqualTo(original.granted)
}
}
@@ -169,8 +163,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(ADDITIONAL_DEFINER_APK)
install(ADDITIONAL_USER_APK)
eventually {
- assertThat(getAdditionalPermissionCount())
- .isEqualTo(additionalPermissionBefore + 1)
+ assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore + 1)
}
}
@@ -181,8 +174,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(ADDITIONAL_DEFINER_APK)
install(ADDITIONAL_USER_APK)
eventually {
- assertThat(getAdditionalPermissionCount())
- .isNotEqualTo(additionalPermissionBefore)
+ assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore)
}
uninstallApp(ADDITIONAL_USER_PKG)
@@ -198,8 +190,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(ADDITIONAL_DEFINER_APK)
install(ADDITIONAL_USER_APK)
eventually {
- assertThat(getAdditionalPermissionCount())
- .isNotEqualTo(additionalPermissionBefore)
+ assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore)
}
uninstallApp(ADDITIONAL_DEFINER_PKG)
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ReviewOngoingUsageFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ReviewOngoingUsageFragmentTest.kt
index ec9971cb4..dc654047d 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ReviewOngoingUsageFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ReviewOngoingUsageFragmentTest.kt
@@ -30,9 +30,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
-/**
- * Simple tests for {@link ReviewOngoingUsageFragment}
- */
+/** Simple tests for {@link ReviewOngoingUsageFragment} */
class ReviewOngoingUsageFragmentTest : PermissionHub2Test() {
@Before
@@ -46,14 +44,15 @@ class ReviewOngoingUsageFragmentTest : PermissionHub2Test() {
@Test
fun cameraAccessShouldBeShown() {
runWithShellPermissionIdentity {
- context.startActivity(Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- })
+ context.startActivity(
+ Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
}
waitFindObject(By.textContains(CAMERA_TEST_APP_LABEL)).click()
}
- @After
- fun cleanUp() = uninstallTestApps()
-} \ No newline at end of file
+ @After fun cleanUp() = uninstallTestApps()
+}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/v31/PermissionUsageFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/v31/PermissionUsageFragmentTest.kt
index 2869e1863..6286b1131 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/v31/PermissionUsageFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/v31/PermissionUsageFragmentTest.kt
@@ -29,8 +29,8 @@ 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.permissioncontroller.permissionui.PermissionHub2Test
-import com.android.permissioncontroller.permissionui.wakeUpScreen
import com.android.permissioncontroller.permissionui.pressHome
+import com.android.permissioncontroller.permissionui.wakeUpScreen
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -62,8 +62,10 @@ class PermissionUsageFragmentTest : PermissionHub2Test() {
runWithShellPermissionIdentity {
context.startActivity(
Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- })
+ }
+ )
}
eventually {
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsFragmentTest.kt
index f3171c7d5..9794d787d 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsFragmentTest.kt
@@ -27,42 +27,49 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-/**
- * Run with:
- * atest ManagePermissionsFragmentTest
- */
+/** Run with: atest ManagePermissionsFragmentTest */
@RunWith(AndroidJUnit4::class)
class ManagePermissionsFragmentTest : TelevisionUiBaseTest() {
- @Before
- fun launch() = launchPermissionController()
+ @Before fun launch() = launchPermissionController()
@Test
fun test_bodySensors_permissionGroup_isNotShown() {
- assertFalse("Found \"Body sensors\" permission",
- uiDevice.hasElementWithTitle(bodySensorsPermissionLabel))
+ assertFalse(
+ "Found \"Body sensors\" permission",
+ uiDevice.hasElementWithTitle(bodySensorsPermissionLabel)
+ )
}
@Test
fun test_camera_permissionGroup_isShown_whenUsed() {
// Make sure Camera permission group is not shown at first.
- assertFalse("Found \"Camera\" permission",
- uiDevice.hasElementWithTitle(cameraPermissionLabel))
+ assertFalse(
+ "Found \"Camera\" permission",
+ uiDevice.hasElementWithTitle(cameraPermissionLabel)
+ )
// Install app that uses Camera permission...
installTestAppThatUsesCameraPermission()
// ... grant the permission ...
grantTestAppPermission(CAMERA)
// ... make sure now the Camera permission is shown.
- assertTrue("Could not find \"Camera\" permission",
- uiDevice.focusOnElementWithTitle(cameraPermissionLabel))
+ assertTrue(
+ "Could not find \"Camera\" permission",
+ uiDevice.focusOnElementWithTitle(cameraPermissionLabel)
+ )
}
@Test
fun test_otherPermissions_Button_isShown() {
uiDevice.navigateToTheBottom()
- assertEquals("The last item should be the \"Other permissions\" button",
- otherPermissionsLabel, uiDevice.focusedElementTitle)
- assertTrue("\"Other permissions\" button should be clickable",
- uiDevice.focusedElement.isClickable)
+ assertEquals(
+ "The last item should be the \"Other permissions\" button",
+ otherPermissionsLabel,
+ uiDevice.focusedElementTitle
+ )
+ assertTrue(
+ "\"Other permissions\" button should be clickable",
+ uiDevice.focusedElement.isClickable
+ )
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsOtherFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsOtherFragmentTest.kt
index f7cf27c72..2d338906b 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsOtherFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsOtherFragmentTest.kt
@@ -29,10 +29,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-/**
- * Run with:
- * atest ManagePermissionsOtherFragmentTest
- */
+/** Run with: atest ManagePermissionsOtherFragmentTest */
@RunWith(AndroidJUnit4::class)
class ManagePermissionsOtherFragmentTest : TelevisionUiBaseTest() {
@@ -45,20 +42,27 @@ class ManagePermissionsOtherFragmentTest : TelevisionUiBaseTest() {
uiDevice.waitForIdle()
// Making sure are on the "Other permission" page now.
// For this we can check the Fragment's title.
- assertEquals("\"Other permissions\" didn't open",
- otherPermissionsLabel, uiDevice.fragmentDecorTitle)
+ assertEquals(
+ "\"Other permissions\" didn't open",
+ otherPermissionsLabel,
+ uiDevice.fragmentDecorTitle
+ )
}
@Test
fun bodySensors_permissionGroup_isNotShown() {
- assertFalse("Found \"Body sensors\" permission",
- uiDevice.focusOnElementWithTitle(bodySensorsPermissionLabel))
+ assertFalse(
+ "Found \"Body sensors\" permission",
+ uiDevice.focusOnElementWithTitle(bodySensorsPermissionLabel)
+ )
}
@Test
fun additionalPermissions_section_isNotShown_ifAllUnused() {
- assertFalse("\"Additional permissions\" section is shown",
- uiDevice.hasElementWithTitle(additionalPermissionsLabel))
+ assertFalse(
+ "\"Additional permissions\" section is shown",
+ uiDevice.hasElementWithTitle(additionalPermissionsLabel)
+ )
}
@Test
@@ -72,10 +76,14 @@ class ManagePermissionsOtherFragmentTest : TelevisionUiBaseTest() {
grantTestAppPermission(TEST_APP_DEFINED_PERMISSION_A)
// Make sure the "Additional permissions" section is now shown...
- assertTrue("\"Additional permissions\" section should be shown",
- uiDevice.hasElementWithTitle(additionalPermissionsLabel))
+ assertTrue(
+ "\"Additional permissions\" section should be shown",
+ uiDevice.hasElementWithTitle(additionalPermissionsLabel)
+ )
// ... and that we now have "Permission A" row.
- assertTrue("Could not find \"Permission A\" row",
- uiDevice.focusOnElementWithTitle(TEST_APP_DEFINED_PERMISSION_A_LABEL))
+ assertTrue(
+ "Could not find \"Permission A\" row",
+ uiDevice.focusOnElementWithTitle(TEST_APP_DEFINED_PERMISSION_A_LABEL)
+ )
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUiBaseTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUiBaseTest.kt
index c289afa6e..f56544660 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUiBaseTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUiBaseTest.kt
@@ -30,8 +30,7 @@ abstract class TelevisionUiBaseTest : BasePermissionUiTest() {
val otherPermissionsLabel = "Other permissions"
val additionalPermissionsLabel = "Additional permissions"
- @Before
- fun assumeTelevision() = assumeTrue(isTelevision)
+ @Before fun assumeTelevision() = assumeTrue(isTelevision)
@Before
fun wakeUpAndGoToHomeScreen() {
@@ -39,16 +38,17 @@ abstract class TelevisionUiBaseTest : BasePermissionUiTest() {
uiDevice.pressHome()
}
- @After
- fun cleanUp() = uninstallTestApps()
+ @After fun cleanUp() = uninstallTestApps()
protected fun launchPermissionController() {
SystemUtil.runWithShellPermissionIdentity {
- instrumentationContext.startActivity(Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- })
+ instrumentationContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
}
uiDevice.waitForIdle()
}
-} \ No newline at end of file
+}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUtils.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUtils.kt
index b221eae70..8caac313e 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUtils.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/TelevisionUtils.kt
@@ -34,17 +34,20 @@ val UiDevice.fragmentDecorTitle: String?
get() = wait(Until.findObject(SELECTOR_RES_ID_PC_DECOR_TITLE), WAIT_DELAY)?.text
val UiDevice.focusedElement: UiObject2
- get() = wait(Until.findObject(SELECTOR_FOCUSED), WAIT_DELAY)
- ?: error("Focused item is not found")
+ get() =
+ wait(Until.findObject(SELECTOR_FOCUSED), WAIT_DELAY) ?: error("Focused item is not found")
private val UiObject2.titleElement: UiObject2
- get() = wait(Until.findObject(SELECTOR_RES_ID_ANDROID_TITLE), WAIT_DELAY)
- ?: error("Could not retrieve title")
+ get() =
+ wait(Until.findObject(SELECTOR_RES_ID_ANDROID_TITLE), WAIT_DELAY)
+ ?: error("Could not retrieve title")
val UiDevice.focusedElementTitle: String?
get() {
repeat(RETRIES) {
- try { return focusedElement.titleElement.text } catch (e: StaleObjectException) {}
+ try {
+ return focusedElement.titleElement.text
+ } catch (e: StaleObjectException) {}
}
error("Could not get title text")
}
@@ -63,13 +66,13 @@ fun UiDevice.navigateToTheTop() {
while (navigateUp()) {}
}
-fun UiDevice.focusOnElementWithTitle(title: CharSequence): Boolean =
- checkAllItemsIfNeeded { focusedElementTitle == title }
+fun UiDevice.focusOnElementWithTitle(title: CharSequence): Boolean = checkAllItemsIfNeeded {
+ focusedElementTitle == title
+}
-fun UiDevice.hasElementWithTitle(title: CharSequence): Boolean =
- checkAllItemsIfNeeded {
- hasObject(By.copy(SELECTOR_RES_ID_ANDROID_TITLE).text(title.toString()))
- }
+fun UiDevice.hasElementWithTitle(title: CharSequence): Boolean = checkAllItemsIfNeeded {
+ hasObject(By.copy(SELECTOR_RES_ID_ANDROID_TITLE).text(title.toString()))
+}
private fun UiDevice.checkAllItemsIfNeeded(predicate: () -> Boolean): Boolean {
// Let's do one quick check first, right where we are. If it does not work - we'll do the walk.
@@ -93,4 +96,4 @@ private fun UiDevice.navigate(action: () -> Unit): Boolean {
waitForIdle()
return prevFocusedTitle != focusedElementTitle
-} \ No newline at end of file
+}
diff --git a/SafetyCenter/Resources/res/values-ar/strings.xml b/SafetyCenter/Resources/res/values-ar/strings.xml
index 2a97af99a..14ec20a51 100644
--- a/SafetyCenter/Resources/res/values-ar/strings.xml
+++ b/SafetyCenter/Resources/res/values-ar/strings.xml
@@ -24,7 +24,7 @@
<string name="lock_screen_summary_disabled" msgid="354071230916616692">"ما مِن معلومات بعد."</string>
<string name="lock_screen_search_terms" msgid="2678486357779794826">"قفل الجهاز، قفل الشاشة، شاشة القفل، شاشة قفل، كلمة المرور، رقم التعريف الشخصي، نقش"</string>
<string name="biometrics_title" msgid="5859504610285212938">"المقاييس الحيوية"</string>
- <string name="biometrics_search_terms" msgid="6040319118762671981">"بصمة الإصبع، إصبع، إضافة بصمة إصبع، فتح الجهاز بالتعرف على الوجه، وجه"</string>
+ <string name="biometrics_search_terms" msgid="6040319118762671981">"بصمة الإصبع، إصبع، إضافة بصمة إصبع، فتح الجهاز ببصمة الوجه، وجه"</string>
<string name="privacy_sources_title" msgid="4061110826457365957">"الخصوصية"</string>
<string name="privacy_sources_summary" msgid="4089719981155120864">"لوحة البيانات والأذونات وعناصر التحكّم"</string>
<string name="permission_usage_title" msgid="3633779688945350407">"لوحة بيانات الخصوصية"</string>
diff --git a/SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml b/SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml
index c4328fe12..628477b14 100644
--- a/SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml
+++ b/SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml
@@ -22,7 +22,7 @@
<string name="lock_screen_sources_summary" msgid="7220439741282516496"></string>
<string name="lock_screen_title" msgid="4069104894527169877">"Otključavanje ekrana"</string>
<string name="lock_screen_summary_disabled" msgid="354071230916616692">"Još nema informacija"</string>
- <string name="lock_screen_search_terms" msgid="2678486357779794826">"zaključavanje uređaja, zaključavanje ekrana, zaključani ekran, lozinka, PIN, šablon"</string>
+ <string name="lock_screen_search_terms" msgid="2678486357779794826">"zaključavanje uređaja, otključavanje ekrana, zaključani ekran, lozinka, PIN, šablon"</string>
<string name="biometrics_title" msgid="5859504610285212938">"Biometrija"</string>
<string name="biometrics_search_terms" msgid="6040319118762671981">"otisak prsta, prst, dodaj otisak prsta, otključavanje licem, lice"</string>
<string name="privacy_sources_title" msgid="4061110826457365957">"Privatnost"</string>
diff --git a/SafetyCenter/Resources/res/values-bs/strings.xml b/SafetyCenter/Resources/res/values-bs/strings.xml
index f24d6415f..cc1ba4b1f 100644
--- a/SafetyCenter/Resources/res/values-bs/strings.xml
+++ b/SafetyCenter/Resources/res/values-bs/strings.xml
@@ -22,7 +22,7 @@
<string name="lock_screen_sources_summary" msgid="7220439741282516496"></string>
<string name="lock_screen_title" msgid="4069104894527169877">"Zaključavanje ekrana"</string>
<string name="lock_screen_summary_disabled" msgid="354071230916616692">"Još uvijek nema informacija"</string>
- <string name="lock_screen_search_terms" msgid="2678486357779794826">"zaključavanje uređaja, zaključavanje ekrana, zaključan ekran, lozinka, pin, uzorak"</string>
+ <string name="lock_screen_search_terms" msgid="2678486357779794826">"zaključavanje uređaja, zaključavanje ekrana, zaključani ekran, lozinka, pin, uzorak"</string>
<string name="biometrics_title" msgid="5859504610285212938">"Biometrija"</string>
<string name="biometrics_search_terms" msgid="6040319118762671981">"otisak prsta, prst, dodajte otisak prsta, otključavanje licem, lice"</string>
<string name="privacy_sources_title" msgid="4061110826457365957">"Privatnost"</string>
diff --git a/SafetyCenter/Resources/res/values-eu-v34/strings.xml b/SafetyCenter/Resources/res/values-eu-v34/strings.xml
index 20f2cfbaf..f1a070e4d 100644
--- a/SafetyCenter/Resources/res/values-eu-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-eu-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Kokapen-datuak partekatzeko aukeraren berritasunak"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"datuak, datuak partekatzeko aukera, datuak partekatzeko aukeraren berritasunak, kokapen-datuak partekatzeko aukeraren berritasunak, partekatzea"</string>
<string name="advanced_title" msgid="6259362998269627310">"Beste ezarpen batzuk"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"Segurtasun eta pribatutasun gehiago"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"Segurtasun eta pribatutasun handiagoak"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"Betetze automatikoa, jakinarazpenak eta abar"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"Laneko gidalerroei buruzko informazioa"</string>
diff --git a/SafetyCenter/Resources/res/values-fa-v34/strings.xml b/SafetyCenter/Resources/res/values-fa-v34/strings.xml
index d8bf2b762..92a2168de 100644
--- a/SafetyCenter/Resources/res/values-fa-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-fa-v34/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="lock_screen_sources_title" msgid="5493678510117489865">"باز کردن قفل دستگاه"</string>
- <string name="biometrics_title_for_work" msgid="1842284049407771568">"گزینه‌های زیست‌سنجشی ویژه کار"</string>
+ <string name="biometrics_title_for_work" msgid="1842284049407771568">"داده‌های زیست‌سنجشی برای کار"</string>
<string name="privacy_sources_summary" msgid="4083646673569677049">"اجازه‌ها، داشبورد، کنترل‌ها"</string>
<string name="health_connect_title" msgid="8318152190040327804">"Health Connect"</string>
<string name="health_connect_search_terms" msgid="4998970586245680829">"‏سلامت، Health Connect"</string>
diff --git a/SafetyCenter/Resources/res/values-fa/strings.xml b/SafetyCenter/Resources/res/values-fa/strings.xml
index 41a3121ea..bdf2063f1 100644
--- a/SafetyCenter/Resources/res/values-fa/strings.xml
+++ b/SafetyCenter/Resources/res/values-fa/strings.xml
@@ -23,7 +23,7 @@
<string name="lock_screen_title" msgid="4069104894527169877">"قفل صفحه"</string>
<string name="lock_screen_summary_disabled" msgid="354071230916616692">"هنوز اطلاعاتی دردسترس نیست"</string>
<string name="lock_screen_search_terms" msgid="2678486357779794826">"قفل دستگاه، قفل صفحه، صفحه قفل، صفحه‌قفل، گذرواژه، پین، الگو"</string>
- <string name="biometrics_title" msgid="5859504610285212938">"زیست‌سنجشی"</string>
+ <string name="biometrics_title" msgid="5859504610285212938">"داده‌های زیست‌سنجشی"</string>
<string name="biometrics_search_terms" msgid="6040319118762671981">"اثر انگشت، انگشت، افزودن اثر انگشت، قفل‌گشایی با چهره، چهره"</string>
<string name="privacy_sources_title" msgid="4061110826457365957">"حریم خصوصی"</string>
<string name="privacy_sources_summary" msgid="4089719981155120864">"داشبورد، اجازه‌ها، کنترل‌ها"</string>
@@ -37,7 +37,7 @@
<string name="privacy_controls_summary" msgid="2402066941190435424">"کنترل دسترسی دستگاه به میکروفون، دوربین، و غیره"</string>
<string name="privacy_controls_search_terms" msgid="3774472175934304165">"حریم خصوصی، تنظیمات حریم خصوصی"</string>
<string name="advanced_title" msgid="8745436380690561172">"تنظیمات بیشتر"</string>
- <string name="advanced_security_title" msgid="1126833338772188155">"تنظیمات امنیتی بیشتر"</string>
+ <string name="advanced_security_title" msgid="1126833338772188155">"تنظیمات ایمنی بیشتر"</string>
<string name="advanced_security_summary" msgid="6172253327022425123">"رمزگذاری، اطلاعات اعتباری، و غیره"</string>
<string name="advanced_security_search_terms" msgid="3350609555814362075"></string>
<string name="advanced_privacy_title" msgid="1117725225706176643">"تنظیمات حریم خصوصی بیشتر"</string>
diff --git a/SafetyCenter/Resources/res/values-fi-v34/strings.xml b/SafetyCenter/Resources/res/values-fi-v34/strings.xml
index 3b0f8300b..b51af6e54 100644
--- a/SafetyCenter/Resources/res/values-fi-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-fi-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Sijaintidatan jakamisen päivitykset"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Data, datan jakaminen, datan jakamisen päivitykset, sijaintidatan jakamisen päivitykset, jakaminen"</string>
<string name="advanced_title" msgid="6259362998269627310">"Muut asetukset"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"Lisää tietoturva‑ ja yksityisyysasetuksia"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"Lisää turvallisuus‑ ja yksityisyysasetuksia"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"Automaattinen täyttö, ilmoitukset ja muuta"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"Työkäytäntötietosi"</string>
diff --git a/SafetyCenter/Resources/res/values-fi/strings.xml b/SafetyCenter/Resources/res/values-fi/strings.xml
index de46fb233..5d234cd05 100644
--- a/SafetyCenter/Resources/res/values-fi/strings.xml
+++ b/SafetyCenter/Resources/res/values-fi/strings.xml
@@ -30,7 +30,7 @@
<string name="permission_usage_title" msgid="3633779688945350407">"Yksityisyydenhallintapaneeli"</string>
<string name="permission_usage_summary" msgid="5323079206029964468">"Näytä, mitkä sovellukset ovat käyttäneet lupia äskettäin"</string>
<string name="permission_usage_search_terms" msgid="3852343592870257104">"Yksityisyys, yksityisyydenhallintapaneeli"</string>
- <string name="permission_manager_title" msgid="5277347862821255015">"Lupien ylläpito"</string>
+ <string name="permission_manager_title" msgid="5277347862821255015">"Lupienhallinta"</string>
<string name="permission_manager_summary" msgid="8099852107340970790">"Päätä sovelluksen pääsystä dataasi"</string>
<string name="permission_manager_search_terms" msgid="2895147613099694722">"Luvat, lupien hallinta"</string>
<string name="privacy_controls_title" msgid="5322875777945432395">"Yksityisyysasetukset"</string>
@@ -41,6 +41,6 @@
<string name="advanced_security_summary" msgid="6172253327022425123">"Salaus, kirjautumistiedot ja muuta"</string>
<string name="advanced_security_search_terms" msgid="3350609555814362075"></string>
<string name="advanced_privacy_title" msgid="1117725225706176643">"Lisää yksityisyysasetuksia"</string>
- <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automaattinen täyttö, toimintojen hallinta ja muuta"</string>
+ <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automaattinen täyttö, toiminnan hallinta ja muuta"</string>
<string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-fr-rCA/strings.xml b/SafetyCenter/Resources/res/values-fr-rCA/strings.xml
index a835c7139..a96007f58 100644
--- a/SafetyCenter/Resources/res/values-fr-rCA/strings.xml
+++ b/SafetyCenter/Resources/res/values-fr-rCA/strings.xml
@@ -28,10 +28,10 @@
<string name="privacy_sources_title" msgid="4061110826457365957">"Confidentialité"</string>
<string name="privacy_sources_summary" msgid="4089719981155120864">"Tableau de bord, autorisations, commandes"</string>
<string name="permission_usage_title" msgid="3633779688945350407">"Tableau de bord de confidentialité"</string>
- <string name="permission_usage_summary" msgid="5323079206029964468">"Affichez les applications qui ont récemment utilisé les autorisations"</string>
+ <string name="permission_usage_summary" msgid="5323079206029964468">"Affichez les applis qui ont récemment utilisé les autorisations"</string>
<string name="permission_usage_search_terms" msgid="3852343592870257104">"Confidentialité, Tableau de bord de confidentialité"</string>
<string name="permission_manager_title" msgid="5277347862821255015">"Gestionnaire des autorisations"</string>
- <string name="permission_manager_summary" msgid="8099852107340970790">"Gérez l\'accès des applications à vos données"</string>
+ <string name="permission_manager_summary" msgid="8099852107340970790">"Gérez l\'accès des applis à vos données"</string>
<string name="permission_manager_search_terms" msgid="2895147613099694722">"Autorisations, Gestionnaire des autorisations"</string>
<string name="privacy_controls_title" msgid="5322875777945432395">"Paramètres de confidentialité"</string>
<string name="privacy_controls_summary" msgid="2402066941190435424">"Contrôlez l\'accès de l\'appareil au microphone, à l\'appareil photo, etc."</string>
diff --git a/SafetyCenter/Resources/res/values-fr/strings.xml b/SafetyCenter/Resources/res/values-fr/strings.xml
index ef0d893ae..c05df5aed 100644
--- a/SafetyCenter/Resources/res/values-fr/strings.xml
+++ b/SafetyCenter/Resources/res/values-fr/strings.xml
@@ -28,7 +28,7 @@
<string name="privacy_sources_title" msgid="4061110826457365957">"Confidentialité"</string>
<string name="privacy_sources_summary" msgid="4089719981155120864">"Tableau de bord, autorisations, commandes"</string>
<string name="permission_usage_title" msgid="3633779688945350407">"Tableau de bord Confidentialité"</string>
- <string name="permission_usage_summary" msgid="5323079206029964468">"Afficher les applis qui ont récemment utilisé des autorisations"</string>
+ <string name="permission_usage_summary" msgid="5323079206029964468">"Affichez les applis qui ont récemment utilisé des autorisations"</string>
<string name="permission_usage_search_terms" msgid="3852343592870257104">"confidentialité, tableau de bord confidentialité"</string>
<string name="permission_manager_title" msgid="5277347862821255015">"Gestionnaire d\'autorisations"</string>
<string name="permission_manager_summary" msgid="8099852107340970790">"Contrôler l\'accès des applis à vos données"</string>
diff --git a/SafetyCenter/Resources/res/values-hi-v34/strings.xml b/SafetyCenter/Resources/res/values-hi-v34/strings.xml
index 3a941dc97..c608d0f9e 100644
--- a/SafetyCenter/Resources/res/values-hi-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-hi-v34/strings.xml
@@ -26,7 +26,7 @@
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"डेटा, डेटा शेयर करना, डेटा शेयर करने के अपडेट, जगह की जानकारी शेयर करने के बारे में अपडेट, शेयर करना"</string>
<string name="advanced_title" msgid="6259362998269627310">"बेहतर सेटिंग"</string>
<string name="more_settings_title" msgid="9033454654010697185">"सुरक्षा और निजता की ज़्यादा सेटिंग"</string>
- <string name="more_settings_summary" msgid="7086620830002515710">"ऑटोमैटिक भरना, सूचनाएं वगैरह"</string>
+ <string name="more_settings_summary" msgid="7086620830002515710">"जानकारी अपने-आप भरने की सुविधा, सूचनाएं वगैरह"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"आपके ऑफ़िस की नीति के बारे में जानकारी"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-hy-v34/strings.xml b/SafetyCenter/Resources/res/values-hy-v34/strings.xml
index 158cec7c0..25d63a426 100644
--- a/SafetyCenter/Resources/res/values-hy-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-hy-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Տեղադրության մասին տվյալներով կիսվելու թույլտվության թարմացում"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Տվյալներ, Տվյալների փոխանցում, Տվյալներով կիսվելու եղանակի փոփոխություն, Տեղադրության մասին տվյալներով կիսվելու թույլտվության թարմացում, փոխանցում"</string>
<string name="advanced_title" msgid="6259362998269627310">"Այլ կարգավորումներ"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"Անվտանգության և գաղտնիության լրացուցիչ կարգավորումներ"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"Ապահովության և գաղտնիության լրացուցիչ կարգավորումներ"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"Ինքնալրացում, ծանուցումներ և ավելին"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"Տեղեկություններ աշխատանքային կանոնների մասին"</string>
diff --git a/SafetyCenter/Resources/res/values-in-v34/strings.xml b/SafetyCenter/Resources/res/values-in-v34/strings.xml
index 9d375e022..37061ad80 100644
--- a/SafetyCenter/Resources/res/values-in-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-in-v34/strings.xml
@@ -22,11 +22,11 @@
<string name="privacy_sources_summary" msgid="4083646673569677049">"Izin, dasbor, kontrol"</string>
<string name="health_connect_title" msgid="8318152190040327804">"Health Connect"</string>
<string name="health_connect_search_terms" msgid="4998970586245680829">"Health, Health Connect"</string>
- <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Pembaruan berbagi data untuk lokasi"</string>
+ <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Pembaruan berbagi data lokasi"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Data, Berbagi data, Pembaruan berbagi data, Pembaruan berbagi data untuk lokasi, berbagi"</string>
- <string name="advanced_title" msgid="6259362998269627310">"Setelan lainnya"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"Keamanan &amp; privasi lainnya"</string>
- <string name="more_settings_summary" msgid="7086620830002515710">"Isi otomatis, notifikasi, dan lainnya"</string>
+ <string name="advanced_title" msgid="6259362998269627310">"Setelan lain"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"Keamanan &amp; privasi lain"</string>
+ <string name="more_settings_summary" msgid="7086620830002515710">"Isi otomatis, notifikasi, dan lain-lain"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"Info kebijakan profil kerja Anda"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-it/strings.xml b/SafetyCenter/Resources/res/values-it/strings.xml
index 02f431a9a..a62391493 100644
--- a/SafetyCenter/Resources/res/values-it/strings.xml
+++ b/SafetyCenter/Resources/res/values-it/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="safetyCenterResourcesAppLabel" msgid="4043334186295695930">"Risorse Centro per la sicurezza online"</string>
- <string name="lock_screen_sources_title" msgid="3317906280484627707">"Blocco del dispositivo"</string>
+ <string name="lock_screen_sources_title" msgid="3317906280484627707">"Blocco dispositivo"</string>
<string name="lock_screen_sources_summary" msgid="7220439741282516496"></string>
<string name="lock_screen_title" msgid="4069104894527169877">"Blocco schermo"</string>
<string name="lock_screen_summary_disabled" msgid="354071230916616692">"Ancora nessuna informazione"</string>
diff --git a/SafetyCenter/Resources/res/values-ja/strings.xml b/SafetyCenter/Resources/res/values-ja/strings.xml
index 6e67998f4..b31b1690a 100644
--- a/SafetyCenter/Resources/res/values-ja/strings.xml
+++ b/SafetyCenter/Resources/res/values-ja/strings.xml
@@ -30,9 +30,9 @@
<string name="permission_usage_title" msgid="3633779688945350407">"プライバシー ダッシュボード"</string>
<string name="permission_usage_summary" msgid="5323079206029964468">"権限を最近使用したアプリが表示されます"</string>
<string name="permission_usage_search_terms" msgid="3852343592870257104">"プライバシー, プライバシー ダッシュボード"</string>
- <string name="permission_manager_title" msgid="5277347862821255015">"権限マネージャー"</string>
+ <string name="permission_manager_title" msgid="5277347862821255015">"権限マネージャ"</string>
<string name="permission_manager_summary" msgid="8099852107340970790">"アプリのデータアクセスを管理します"</string>
- <string name="permission_manager_search_terms" msgid="2895147613099694722">"権限, 権限マネージャー"</string>
+ <string name="permission_manager_search_terms" msgid="2895147613099694722">"権限, 権限マネージャ"</string>
<string name="privacy_controls_title" msgid="5322875777945432395">"プライバシー管理"</string>
<string name="privacy_controls_summary" msgid="2402066941190435424">"マイク、カメラなどへのデバイス アクセスを管理します"</string>
<string name="privacy_controls_search_terms" msgid="3774472175934304165">"プライバシー, プライバシー管理"</string>
diff --git a/SafetyCenter/Resources/res/values-kk-v34/strings.xml b/SafetyCenter/Resources/res/values-kk-v34/strings.xml
index 1efaa3f6b..362a4f700 100644
--- a/SafetyCenter/Resources/res/values-kk-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-kk-v34/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="lock_screen_sources_title" msgid="5493678510117489865">"Құрылғының құлпын ашу"</string>
- <string name="biometrics_title_for_work" msgid="1842284049407771568">"Жұмыс қолданбаларына арналған биометрика"</string>
+ <string name="biometrics_title_for_work" msgid="1842284049407771568">"Жұмыс қолданбаларына арналған биометрия"</string>
<string name="privacy_sources_summary" msgid="4083646673569677049">"Рұқсаттар, бақылау тақтасы, басқару элементтері"</string>
<string name="health_connect_title" msgid="8318152190040327804">"Health Connect"</string>
<string name="health_connect_search_terms" msgid="4998970586245680829">"Денсаулық, Health Connect"</string>
diff --git a/SafetyCenter/Resources/res/values-km-v34/strings.xml b/SafetyCenter/Resources/res/values-km-v34/strings.xml
index df7009b2e..31acc4de1 100644
--- a/SafetyCenter/Resources/res/values-km-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-km-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"ការធ្វើបច្ចុប្បន្នភាពការចែករំលែកទិន្នន័យសម្រាប់ទីតាំង"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"ទិន្នន័យ ការចែករំលែកទិន្នន័យ បច្ចុប្បន្នភាពការចែករំលែកទិន្នន័យ បច្ចុប្បន្នភាពការចែករំលែកទិន្នន័យសម្រាប់ទីតាំង ការចែករំលែក"</string>
<string name="advanced_title" msgid="6259362998269627310">"ការកំណត់​ផ្សេងទៀត"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"ឯកជនភាព និងសុវត្ថិភាពបន្ថែម"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"សន្តិសុខ និងឯកជនភាពបន្ថែម"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"បំពេញស្វ័យប្រវត្តិ ការជូនដំណឹង និងច្រើនទៀត"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"ព័ត៌មាន​អំពីគោលការណ៍ការងារ​របស់អ្នក"</string>
diff --git a/SafetyCenter/Resources/res/values-ky/strings.xml b/SafetyCenter/Resources/res/values-ky/strings.xml
index ca605b018..a862dac0d 100644
--- a/SafetyCenter/Resources/res/values-ky/strings.xml
+++ b/SafetyCenter/Resources/res/values-ky/strings.xml
@@ -31,7 +31,7 @@
<string name="permission_usage_summary" msgid="5323079206029964468">"Уруксаттарды жакында кайсы колдонмолор колдонгонун көрө аласыз"</string>
<string name="permission_usage_search_terms" msgid="3852343592870257104">"Купуялык, Купуялык тактасы"</string>
<string name="permission_manager_title" msgid="5277347862821255015">"Уруксаттарды башкаргыч"</string>
- <string name="permission_manager_summary" msgid="8099852107340970790">"Колдонмолорго маалыматыңыздын жеткиликтүүлүгүн көзөмөлдөйсүз"</string>
+ <string name="permission_manager_summary" msgid="8099852107340970790">"Колдонмолорго маалыматтарыңызды жеткиликтүү кыласыз"</string>
<string name="permission_manager_search_terms" msgid="2895147613099694722">"Уруксаттар, Уруксаттарды башкаргыч"</string>
<string name="privacy_controls_title" msgid="5322875777945432395">"Купуялыкты көзөмөлдөө каражаттары"</string>
<string name="privacy_controls_summary" msgid="2402066941190435424">"Түзмөктүн микрофонду, камераны жана башкаларды колдонуу мүмкүнчүлүгүн көзөмөлдөө"</string>
diff --git a/SafetyCenter/Resources/res/values-mr-v34/strings.xml b/SafetyCenter/Resources/res/values-mr-v34/strings.xml
index f2d620adc..cf257c396 100644
--- a/SafetyCenter/Resources/res/values-mr-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-mr-v34/strings.xml
@@ -26,7 +26,7 @@
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"डेटा, डेटा शेअरिंग, डेटा शेअरिंगसंबंधित अपडेट, स्थानासाठी डेटा शेअरिंगसंबंधित अपडेट, शेअरिंग"</string>
<string name="advanced_title" msgid="6259362998269627310">"इतर सेटिंग्ज"</string>
<string name="more_settings_title" msgid="9033454654010697185">"आणखी सुरक्षा आणि गोपनीयता"</string>
- <string name="more_settings_summary" msgid="7086620830002515710">"ऑटोफिल, सूचना आणि आणखी बरेच काही"</string>
+ <string name="more_settings_summary" msgid="7086620830002515710">"ऑटोफिल, नोटिफिकेशन आणि आणखी बरेच काही"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"तुमच्या कामासंबंधित धोरणाची माहिती"</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-ms-v34/strings.xml b/SafetyCenter/Resources/res/values-ms-v34/strings.xml
index 68102f138..fe204c5ec 100644
--- a/SafetyCenter/Resources/res/values-ms-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-ms-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Kemaskinian perkongsian data untuk lokasi"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Data, Perkongsian data, Kemaskinian perkongsian data, Kemaskinian perkongsian data untuk lokasi, perkongsian"</string>
<string name="advanced_title" msgid="6259362998269627310">"Tetapan lain"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"Lagi sekuriti &amp; privasi"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"Lagi keselamatan &amp; privasi"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"Autolengkap, pemberitahuan dan banyak lagi"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"Maklumat dasar kerja anda"</string>
diff --git a/SafetyCenter/Resources/res/values-my-v34/strings.xml b/SafetyCenter/Resources/res/values-my-v34/strings.xml
index 9b2b27707..6c6ee6ffd 100644
--- a/SafetyCenter/Resources/res/values-my-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-my-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"တည်နေရာအတွက် ဒေတာမျှဝေခြင်း အပ်ဒိတ်"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"ဒေတာ၊ ဒေတာမျှဝေခြင်း၊ ဒေတာမျှဝေခြင်း အပ်ဒိတ်၊ တည်နေရာအတွက် ဒေတာမျှဝေခြင်း အပ်ဒိတ်၊ မျှဝေခြင်း"</string>
<string name="advanced_title" msgid="6259362998269627310">"အခြား ဆက်တင်များ"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"နောက်ထပ် လုံခြုံရေးနှင့် ကိုယ်ရေးဒေတာ လုံခြုံမှု"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"နောက်ထပ် စက်နှင့်အချက်အလက်လုံခြုံရေး"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"အော်တိုဖြည့်၊ အကြောင်းကြားချက်များစသည်"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"သင့်အလုပ်ခွင်မူဝါဒ အချက်အလက်"</string>
diff --git a/SafetyCenter/Resources/res/values-my/strings.xml b/SafetyCenter/Resources/res/values-my/strings.xml
index d0d895ddb..6b5f682fc 100644
--- a/SafetyCenter/Resources/res/values-my/strings.xml
+++ b/SafetyCenter/Resources/res/values-my/strings.xml
@@ -28,12 +28,12 @@
<string name="privacy_sources_title" msgid="4061110826457365957">"ကိုယ်ရေးအချက်အလက်လုံခြုံမှု"</string>
<string name="privacy_sources_summary" msgid="4089719981155120864">"ဒက်ရှ်ဘုတ်၊ ခွင့်ပြုချက်များ၊ သတ်မှတ်ချက်များ"</string>
<string name="permission_usage_title" msgid="3633779688945350407">"ကိုယ်ရေးအချက်အလက်လုံခြုံမှု ဒက်ရှ်ဘုတ်"</string>
- <string name="permission_usage_summary" msgid="5323079206029964468">"ခွင့်ပြုချက်များ မကြာသေးမီကသုံးထားသည့် အက်ပ်များကို ပြသည်"</string>
+ <string name="permission_usage_summary" msgid="5323079206029964468">"လတ်တလောတွင် ခွင့်ပြုချက်များ အသုံးပြုထားသည့် အက်ပ်များကို ပြသည်"</string>
<string name="permission_usage_search_terms" msgid="3852343592870257104">"ကိုယ်ရေးအချက်အလက်လုံခြုံမှု၊ ကိုယ်ရေးအချက်အလက် လုံခြုံမှု ဒက်ရှ်ဘုတ်"</string>
<string name="permission_manager_title" msgid="5277347862821255015">"ခွင့်ပြုချက် မန်နေဂျာ"</string>
<string name="permission_manager_summary" msgid="8099852107340970790">"အက်ပ်၏ သင့်ဒေတာအသုံးပြုခွင့်ကို ထိန်းချုပ်သည်"</string>
<string name="permission_manager_search_terms" msgid="2895147613099694722">"ခွင့်ပြုချက်များ၊ ခွင့်ပြုချက်မန်နေဂျာ"</string>
- <string name="privacy_controls_title" msgid="5322875777945432395">"ကန့်သတ်ရန် ဆက်တင်များ"</string>
+ <string name="privacy_controls_title" msgid="5322875777945432395">"ကိုယ်ရေးအချက်အလက်လုံခြုံမှု ဆက်တင်များ"</string>
<string name="privacy_controls_summary" msgid="2402066941190435424">"မိုက်ခရိုဖုန်း၊ ကင်မရာ စသည်တို့ကို စက်ပစ္စည်းက ဝင်သုံးခွင့်အား ထိန်းချုပ်သည်"</string>
<string name="privacy_controls_search_terms" msgid="3774472175934304165">"ကိုယ်ရေးအချက်အလက်လုံခြုံမှု၊ ကန့်သတ်ရန် ဆက်တင်များ"</string>
<string name="advanced_title" msgid="8745436380690561172">"နောက်ထပ်ဆက်တင်များ"</string>
diff --git a/SafetyCenter/Resources/res/values-pa-v34/strings.xml b/SafetyCenter/Resources/res/values-pa-v34/strings.xml
index b30bcce37..f3597ffc9 100644
--- a/SafetyCenter/Resources/res/values-pa-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-pa-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"ਟਿਕਾਣੇ ਲਈ ਡਾਟਾ ਸਾਂਝਾਕਰਨ ਅੱਪਡੇਟ"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"ਡਾਟਾ, ਡਾਟਾ ਸਾਂਝਾਕਰਨ, ਡਾਟਾ ਸਾਂਝਾਕਰਨ ਅੱਪਡੇਟ, ਟਿਕਾਣੇ ਲਈ ਡਾਟਾ ਸਾਂਝਾਕਰਨ ਅੱਪਡੇਟ, ਸਾਂਝਾਕਰਨ"</string>
<string name="advanced_title" msgid="6259362998269627310">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"ਹੋਰ ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"ਹੋਰ ਸਿਕਿਊਰਟੀ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"ਆਟੋਫਿਲ, ਸੂਚਨਾਵਾਂ ਅਤੇ ਹੋਰ"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"ਤੁਹਾਡੀ ਕਾਰਜ ਨੀਤੀ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ"</string>
diff --git a/SafetyCenter/Resources/res/values-ru-v34/strings.xml b/SafetyCenter/Resources/res/values-ru-v34/strings.xml
index b8a78d5ed..397ef7b9d 100644
--- a/SafetyCenter/Resources/res/values-ru-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-ru-v34/strings.xml
@@ -22,10 +22,10 @@
<string name="privacy_sources_summary" msgid="4083646673569677049">"Разрешения, панель управления, параметры"</string>
<string name="health_connect_title" msgid="8318152190040327804">"Здоровье и спорт"</string>
<string name="health_connect_search_terms" msgid="4998970586245680829">"Здоровье, Здоровье и спорт"</string>
- <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Обновления в передаче данных о местоположении"</string>
+ <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Изменения в передаче геоданных"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Данные, Передача данных, Обновления передачи данных, Обновление доступа к данным, Обновление доступа к данным о местоположении"</string>
<string name="advanced_title" msgid="6259362998269627310">"Другие настройки"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"Дополнительные настройки безопасности и конфиденциальности"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"Дополнительные настройки защиты и конфиденциальности"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"Автозаполнение, уведомления и другие настройки"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"Сведения о правилах организации"</string>
diff --git a/SafetyCenter/Resources/res/values-sr/strings.xml b/SafetyCenter/Resources/res/values-sr/strings.xml
index 2cd42d24c..af0f51727 100644
--- a/SafetyCenter/Resources/res/values-sr/strings.xml
+++ b/SafetyCenter/Resources/res/values-sr/strings.xml
@@ -22,7 +22,7 @@
<string name="lock_screen_sources_summary" msgid="7220439741282516496"></string>
<string name="lock_screen_title" msgid="4069104894527169877">"Откључавање екрана"</string>
<string name="lock_screen_summary_disabled" msgid="354071230916616692">"Још нема информација"</string>
- <string name="lock_screen_search_terms" msgid="2678486357779794826">"закључавање уређаја, закључавање екрана, закључани екран, лозинка, PIN, шаблон"</string>
+ <string name="lock_screen_search_terms" msgid="2678486357779794826">"закључавање уређаја, откључавање екрана, закључани екран, лозинка, PIN, шаблон"</string>
<string name="biometrics_title" msgid="5859504610285212938">"Биометрија"</string>
<string name="biometrics_search_terms" msgid="6040319118762671981">"отисак прста, прст, додај отисак прста, откључавање лицем, лице"</string>
<string name="privacy_sources_title" msgid="4061110826457365957">"Приватност"</string>
diff --git a/SafetyCenter/Resources/res/values-sw-v34/strings.xml b/SafetyCenter/Resources/res/values-sw-v34/strings.xml
index d065e2e03..7df79a70c 100644
--- a/SafetyCenter/Resources/res/values-sw-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-sw-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Masasisho ya kushiriki data ya mahali"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Data, kushiriki Data, masasisho ya kushiriki Data, Masasisho ya kushiriki data ya mahali, kushiriki"</string>
<string name="advanced_title" msgid="6259362998269627310">"Mipangilio mingine"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"Usalama na faragha zaidi"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"Ulinzi na faragha zaidi"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"Kujaza kiotomatiki, arifa na zaidi"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"Maelezo ya sera ya kazini"</string>
diff --git a/SafetyCenter/Resources/res/values-th-v34/strings.xml b/SafetyCenter/Resources/res/values-th-v34/strings.xml
index e334d9b5e..3a085a7c8 100644
--- a/SafetyCenter/Resources/res/values-th-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-th-v34/strings.xml
@@ -25,7 +25,7 @@
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"การอัปเดตการแชร์ข้อมูลตำแหน่ง"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"ข้อมูล, การแชร์ข้อมูล, การอัปเดตการแชร์ข้อมูล, การอัปเดตการแชร์ข้อมูลตำแหน่ง, การแชร์"</string>
<string name="advanced_title" msgid="6259362998269627310">"การตั้งค่าอื่นๆ"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"ความปลอดภัยและความเป็นส่วนตัวเพิ่มเติม"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"การรักษาความปลอดภัยและความเป็นส่วนตัวเพิ่มเติม"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"การป้อนข้อความอัตโนมัติ การแจ้งเตือน และอื่นๆ"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"ข้อมูลนโยบายการทำงาน"</string>
diff --git a/SafetyCenter/Resources/res/values-v34/config.xml b/SafetyCenter/Resources/res/values-v34/config.xml
index db79e5924..c3e1e38a2 100644
--- a/SafetyCenter/Resources/res/values-v34/config.xml
+++ b/SafetyCenter/Resources/res/values-v34/config.xml
@@ -20,4 +20,6 @@
<string name="config_same_task_safety_source_ids" translatable="false">AndroidAccessibility,AndroidBackgroundLocation,AndroidBiometrics,AndroidHealthConnect,AndroidLockScreen,AndroidMoreSettings,AndroidNotificationListener,AndroidPermissionAutoRevoke,AndroidPermissionManager,AndroidPermissionUsage,AndroidPrivacyAppDataSharingUpdates,AndroidPrivacyControls,AndroidWorkPolicyInfo</string>
<!-- Comma separated list of safety source IDs to add an Intent Extra confirming they should be displayed as if opened by a settings UI page. -->
<string name="config_useSettingsHomepageIntentExtra" translatable="false">AndroidMoreSettings,TestSource</string>
+ <!-- Comma separated list of safety source IDs that should be refreshed on page open by default. -->
+ <string name="config_defaultRefreshOnPageOpenSources" translatable="false">AndroidBiometrics</string>
</resources>
diff --git a/SafetyCenter/Resources/res/values-zh-rCN-v34/strings.xml b/SafetyCenter/Resources/res/values-zh-rCN-v34/strings.xml
index 5b8a8fe8d..da468fa0e 100644
--- a/SafetyCenter/Resources/res/values-zh-rCN-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rCN-v34/strings.xml
@@ -20,9 +20,9 @@
<string name="lock_screen_sources_title" msgid="5493678510117489865">"设备解锁"</string>
<string name="biometrics_title_for_work" msgid="1842284049407771568">"适用于工作应用的生物识别"</string>
<string name="privacy_sources_summary" msgid="4083646673569677049">"权限、信息中心和控件"</string>
- <string name="health_connect_title" msgid="8318152190040327804">"Health Connect"</string>
- <string name="health_connect_search_terms" msgid="4998970586245680829">"健康, Health, Health Connect"</string>
- <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"位置数据分享方面的更新"</string>
+ <string name="health_connect_title" msgid="8318152190040327804">"健康数据共享"</string>
+ <string name="health_connect_search_terms" msgid="4998970586245680829">"健康, 健康数据共享, Health, Health Connect"</string>
+ <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"位置数据共享方面的更新"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"数据, 数据分享, 数据分享方面的更新, 位置数据分享方面的更新, 分享, Data, Data sharing, Data sharing updates, Data sharing updates for location, sharing"</string>
<string name="advanced_title" msgid="6259362998269627310">"其他设置"</string>
<string name="more_settings_title" msgid="9033454654010697185">"更多安全和隐私设置"</string>
diff --git a/SafetyCenter/Resources/res/values-zh-rHK-v34/strings.xml b/SafetyCenter/Resources/res/values-zh-rHK-v34/strings.xml
index a11abbde1..9da0c7694 100644
--- a/SafetyCenter/Resources/res/values-zh-rHK-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rHK-v34/strings.xml
@@ -21,11 +21,11 @@
<string name="biometrics_title_for_work" msgid="1842284049407771568">"用於工作應用程式的生物識別選項"</string>
<string name="privacy_sources_summary" msgid="4083646673569677049">"權限、資訊主頁、控制項"</string>
<string name="health_connect_title" msgid="8318152190040327804">"Health Connect"</string>
- <string name="health_connect_search_terms" msgid="4998970586245680829">"健康, Health Connect"</string>
+ <string name="health_connect_search_terms" msgid="4998970586245680829">"健康, 健康資料同步"</string>
<string name="app_data_sharing_updates_title" msgid="7428862330643262588">"位置資料分享更新"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"資料, 資料分享, 資料分享更新, 位置資料分享更新, 分享"</string>
<string name="advanced_title" msgid="6259362998269627310">"其他設定"</string>
- <string name="more_settings_title" msgid="9033454654010697185">"更多安全性和私隱權設定"</string>
+ <string name="more_settings_title" msgid="9033454654010697185">"更多安全性和私隱設定"</string>
<string name="more_settings_summary" msgid="7086620830002515710">"自動填入、通知等"</string>
<string name="more_settings_search_terms" msgid="1371913937610933955"></string>
<string name="work_policy_title" msgid="915692932391542104">"你的工作政策資料"</string>
diff --git a/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml b/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml
index 288a0566c..54bbf0f1c 100644
--- a/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml
@@ -20,9 +20,9 @@
<string name="lock_screen_sources_title" msgid="5493678510117489865">"裝置解鎖"</string>
<string name="biometrics_title_for_work" msgid="1842284049407771568">"工作應用程式的生物特徵辨識選項"</string>
<string name="privacy_sources_summary" msgid="4083646673569677049">"權限、資訊主頁、控制選項"</string>
- <string name="health_connect_title" msgid="8318152190040327804">"Health Connect"</string>
- <string name="health_connect_search_terms" msgid="4998970586245680829">"健康、Health Connect"</string>
- <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"位置資料分享更新"</string>
+ <string name="health_connect_title" msgid="8318152190040327804">"健康資料同步"</string>
+ <string name="health_connect_search_terms" msgid="4998970586245680829">"健康, 健康資料同步"</string>
+ <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"位置資料共用方式更新"</string>
<string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"資料, 資料分享, 資料分享更新, 位置資料分享更新, 分享"</string>
<string name="advanced_title" msgid="6259362998269627310">"其他設定"</string>
<string name="more_settings_title" msgid="9033454654010697185">"其他安全性和隱私權設定"</string>
diff --git a/SafetyCenter/Resources/res/values/config.xml b/SafetyCenter/Resources/res/values/config.xml
index 6b20b69b4..2c92afb21 100644
--- a/SafetyCenter/Resources/res/values/config.xml
+++ b/SafetyCenter/Resources/res/values/config.xml
@@ -22,4 +22,6 @@
<string name="config_NotificationListenerServicePregrants" translatable="false"></string>
<!-- Comma separated list of safety source IDs to add an Intent Extra confirming they should be displayed as if opened by a settings UI page. -->
<string name="config_useSettingsHomepageIntentExtra" translatable="false">AndroidAdvancedPrivacy,AndroidAdvancedSecurity,TestSource</string>
+ <!-- Comma separated list of safety source IDs that should be refreshed on page open by default. -->
+ <string name="config_defaultRefreshOnPageOpenSources" translatable="false"></string>
</resources>
diff --git a/SafetyCenter/Resources/shared_res/values-af/strings.xml b/SafetyCenter/Resources/shared_res/values-af/strings.xml
index 9ab860ecf..3c20c6428 100644
--- a/SafetyCenter/Resources/shared_res/values-af/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-af/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Sien waarskuwing}other{Sien waarskuwings}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Kon nie bladsy oopmaak nie"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Kon nie opletberig afhandel nie"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Kon nie instellings herlaai nie"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Kon nie instelling nagaan nie}other{Kon nie instellings nagaan nie}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Werkprofiel is onderbreek"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Nog geen inligting nie"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-am/strings.xml b/SafetyCenter/Resources/shared_res/values-am/strings.xml
index 093dda2f2..d1080ebf7 100644
--- a/SafetyCenter/Resources/shared_res/values-am/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-am/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{ማንቂያ ይመልከቱ}one{ማንቂያ ይመልከቱ}other{ማንቂያዎች ይመልከቱ}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"ገጹን መከፈት አልተቻለም"</string>
<string name="resolving_action_error" msgid="371968886143262375">"ማንቂያን መፍታት አልተቻለም"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"ቅንብሮችን ማደስ አልተቻለም"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ቅንብርን መፈተሽ አልተቻለም}one{ቅንብርን መፈተሽ አልተቻለም}other{ቅንብሮችን መፈተሽ አልተቻለም}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"የስራ መገለጫ ባለበት ቆሟል"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ገና ምንም መረጃ የለም"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ar/strings.xml b/SafetyCenter/Resources/shared_res/values-ar/strings.xml
index ce0b1c10d..f20febafd 100644
--- a/SafetyCenter/Resources/shared_res/values-ar/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ar/strings.xml
@@ -20,7 +20,7 @@
<string name="scanning_title" msgid="5424849039854311398">"جارٍ الفحص"</string>
<string name="loading_summary" msgid="3740846439782713910">"جارٍ التحقّق من إعدادات الجهاز…"</string>
<string name="overall_severity_level_ok_title" msgid="2041250138727564565">"كل شيء على ما يرام"</string>
- <string name="overall_severity_level_ok_summary" msgid="383626536912856690">"لم يتم رصد أي مشاكل."</string>
+ <string name="overall_severity_level_ok_summary" msgid="383626536912856690">"لم يتم رصد أي مشاكل"</string>
<string name="overall_severity_level_tip_summary" msgid="1935765582243024999">"{count,plural, =1{الاطّلاع على اقتراح}zero{الاطّلاع على اقتراحات}two{الاطّلاع على اقتراحَين}few{الاطّلاع على اقتراحات}many{الاطّلاع على اقتراحات}other{الاطّلاع على اقتراحات}}"</string>
<string name="overall_severity_level_action_taken_summary" msgid="8064091657855656545">"{count,plural, =1{الإجراء الذي تم اتخاذه}zero{الإجراءات التي تم اتخاذها}two{الإجراءان اللذان تم اتخاذهما}few{الإجراءات التي تم اتخاذها}many{الإجراءات التي تم اتخاذها}other{الإجراءات التي تم اتخاذها}}"</string>
<string name="overall_severity_level_ok_review_title" msgid="1494321117696765360">"مراجعة الإعدادات"</string>
@@ -40,9 +40,8 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{الاطّلاع على التنبيه}zero{الاطّلاع على التنبيهات}two{الاطّلاع على التنبيهَين}few{الاطّلاع على التنبيهات}many{الاطّلاع على التنبيهات}other{الاطّلاع على التنبيهات}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"تعذَّر فتح الصفحة"</string>
<string name="resolving_action_error" msgid="371968886143262375">"تعذَّر التعامل بشكل نهائي مع التنبيه"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"تعذّر تحديث الإعدادات"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{تعذّر التحقّق من الإعداد.}zero{تعذّر التحقّق من الإعدادات.}two{تعذّر التحقّق من الإعدادَين.}few{تعذّر التحقّق من الإعدادات.}many{تعذّر التحقّق من الإعدادات.}other{تعذّر التحقّق من الإعدادات.}}"</string>
- <string name="work_profile_paused" msgid="7037400224040869079">"تم إيقاف الملف الشخصي للعمل مؤقتًا"</string>
+ <string name="work_profile_paused" msgid="7037400224040869079">"تم إيقاف ملف العمل مؤقتًا"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ما مِن معلومات بعد."</string>
<string name="notification_channel_group_name" msgid="7155072032524876859">"الأمان والخصوصية"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"الاقتراحات"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-as/strings.xml b/SafetyCenter/Resources/shared_res/values-as/strings.xml
index 473003f59..4105c26d0 100644
--- a/SafetyCenter/Resources/shared_res/values-as/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-as/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{সতৰ্কবাৰ্তা চাওক}one{সতৰ্কবাৰ্তাসমূহ চাওক}other{সতৰ্কবাৰ্তাসমূহ চাওক}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"পৃষ্ঠাখন খুলিব পৰা নগ’ল"</string>
<string name="resolving_action_error" msgid="371968886143262375">"সতৰ্কবাৰ্তা সমাধান কৰিব পৰা নগ’ল"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"ছেটিং ৰিফ্ৰেশ্ব কৰিব পৰা নগ’ল"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ছেটিং পৰীক্ষা কৰিব পৰা নগ’ল}one{ছেটিং পৰীক্ষা কৰিব পৰা নগ’ল}other{ছেটিং পৰীক্ষা কৰিব পৰা নগ’ল}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"কৰ্মস্থানৰ প্ৰ’ফাইলটো পজ কৰা আছে"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"এতিয়ালৈকে কোনো তথ্য নাই"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-az/strings.xml b/SafetyCenter/Resources/shared_res/values-az/strings.xml
index e3c31a178..7e2e7c140 100644
--- a/SafetyCenter/Resources/shared_res/values-az/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-az/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Xəbərdarlığa baxın}other{Xəbərdarlıqlara baxın}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Səhifəni açmaq mümkün olmadı"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Siqnalı həll etmək mümkün olmadı"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Ayarları yeniləmək mümkün olmadı"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Ayarı yoxlamaq alınmadı}other{Ayarları yoxlamaq alınmadı}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"İş profili durdurulub"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Hələ ki, məlumat yoxdur"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml b/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml
index 5233edc91..19660cb06 100644
--- a/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Prikaži obaveštenje}one{Prikaži obaveštenja}few{Prikaži obaveštenja}other{Prikaži obaveštenja}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Otvaranje stranice nije uspelo"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Rešavanje obaveštenja nije uspelo"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Osvežavanje podešavanja nije uspelo"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Provera podešavanja nije uspela}one{Provera podešavanja nije uspela}few{Provera podešavanja nije uspela}other{Provera podešavanja nije uspela}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Poslovni profil je pauziran"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Još nema informacija"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-be/strings.xml b/SafetyCenter/Resources/shared_res/values-be/strings.xml
index 2b386f3e6..2c27670e4 100644
--- a/SafetyCenter/Resources/shared_res/values-be/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-be/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Паглядзець абвестку}one{Паглядзець абвесткі}few{Паглядзець абвесткі}many{Паглядзець абвесткі}other{Паглядзець абвесткі}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Не ўдалося адкрыць старонку"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Не ўдалося вырашыць праблему"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Не ўдалося абнавіць налады"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Не ўдалося праверыць наладу}one{Не ўдалося праверыць налады}few{Не ўдалося праверыць налады}many{Не ўдалося праверыць налады}other{Не ўдалося праверыць налады}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Працоўны профіль прыпынены"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Пакуль няма інфармацыі"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-bg/strings.xml b/SafetyCenter/Resources/shared_res/values-bg/strings.xml
index 85833f145..e3495d4d5 100644
--- a/SafetyCenter/Resources/shared_res/values-bg/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-bg/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Преглед на сигнала}other{Преглед на сигналите}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Страницата не се отвори"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Сигналът не се отстрани"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Настройките не бяха опреснени"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Настройката не бе проверена}other{Настройките не бяха проверени}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Служебният потребителски профил е поставен на пауза"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Още няма информация"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-bn/strings.xml b/SafetyCenter/Resources/shared_res/values-bn/strings.xml
index 30904c97e..3f24f05a4 100644
--- a/SafetyCenter/Resources/shared_res/values-bn/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-bn/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{বিজ্ঞপ্তি দেখুন}one{বিজ্ঞপ্তি দেখুন}other{বিজ্ঞপ্তি দেখুন}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"পৃষ্ঠা খোলা যায়নি"</string>
<string name="resolving_action_error" msgid="371968886143262375">"সতর্কতার সমাধান করা যায়নি"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"সেটিংস রিফ্রেশ করা যায়নি"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{সেটিং চেক করা যায়নি}one{সেটিংস চেক করা যায়নি}other{সেটিংস চেক করা যায়নি}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"অফিস প্রোফাইল পজ করা আছে"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"এখনও কোনও তথ্য নেই"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-bs/strings.xml b/SafetyCenter/Resources/shared_res/values-bs/strings.xml
index 93cfcf8fa..a877f8b36 100644
--- a/SafetyCenter/Resources/shared_res/values-bs/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-bs/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Pogledajte upozorenje}one{Pogledajte upozorenja}few{Pogledajte upozorenja}other{Pogledajte upozorenja}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Otvaranje stranice nije uspjelo"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Rješavanje upozorenja nije uspjelo"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Osvježavanje postavki nije uspjelo"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Provjera postavke nije uspjela}one{Provjera postavki nije uspjela}few{Provjera postavki nije uspjela}other{Provjera postavki nije uspjela}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Radni profil je pauziran"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Još uvijek nema informacija"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ca/strings.xml b/SafetyCenter/Resources/shared_res/values-ca/strings.xml
index ae998b815..a2d8b0f35 100644
--- a/SafetyCenter/Resources/shared_res/values-ca/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ca/strings.xml
@@ -26,7 +26,7 @@
<string name="overall_severity_level_ok_review_title" msgid="1494321117696765360">"Revisa la configuració"</string>
<string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Comprova la llista d\'opcions de configuració"</string>
<string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"El dispositiu pot estar en perill"</string>
- <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Dispositiu en perill"</string>
+ <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"El dispositiu està en perill"</string>
<string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Les dades poden estar en perill"</string>
<string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Les dades estan en perill"</string>
<string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Contrasenyes en perill (antigues)"</string>
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Mostra l\'alerta}many{Mostra les alertes}other{Mostra les alertes}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"No s\'ha pogut obrir la pàgina"</string>
<string name="resolving_action_error" msgid="371968886143262375">"No s\'ha pogut resoldre l\'alerta"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"No s\'ha pogut actualitzar la configuració"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{No s\'ha pogut comprovar la configuració}many{No s\'ha pogut comprovar la configuració}other{No s\'ha pogut comprovar la configuració}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"El perfil de treball s\'ha posat en pausa"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Encara no hi ha informació"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-cs/strings.xml b/SafetyCenter/Resources/shared_res/values-cs/strings.xml
index 9d90b05aa..28aabd4e8 100644
--- a/SafetyCenter/Resources/shared_res/values-cs/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-cs/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Zobrazit upozornění}few{Zobrazit upozornění}many{Zobrazit upozornění}other{Zobrazit upozornění}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Stránku nelze otevřít"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Upozornění se nepodařilo vyřešit"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Nastavení se nepodařilo obnovit"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Nastavení nelze zkontrolovat}few{Nastavení nelze zkontrolovat}many{Nastavení nelze zkontrolovat}other{Nastavení nelze zkontrolovat}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Pracovní profil je pozastaven"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Zatím žádné údaje"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-da/strings.xml b/SafetyCenter/Resources/shared_res/values-da/strings.xml
index bce5898fa..e4d28aaf1 100644
--- a/SafetyCenter/Resources/shared_res/values-da/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-da/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Se underretning}one{Se underretning}other{Se underretninger}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Siden kunne ikke åbnes"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Underretningen kunne ikke behandles"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Indstillingerne kunne ikke opdateres"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Indstillingen kunne ikke tjekkes}one{Indstillingen kunne ikke tjekkes}other{Indstillingerne kunne ikke tjekkes}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Arbejdsprofilen er sat på pause"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Der er ingen oplysninger endnu"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-de/strings.xml b/SafetyCenter/Resources/shared_res/values-de/strings.xml
index 0a1bf2a45..0ff9ebfb8 100644
--- a/SafetyCenter/Resources/shared_res/values-de/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-de/strings.xml
@@ -40,11 +40,10 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Benachrichtigung ansehen}other{Benachrichtigungen ansehen}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Seite konnte nicht geöffnet werden"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Ursache konnte nicht behoben werden"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Einstellungen konnten nicht aktualisiert werden"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Einstellung konnte nicht überprüft werden}other{Einstellungen konnten nicht überprüft werden}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Arbeitsprofil pausiert"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Noch keine Angaben vorhanden"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"Sicherheit &amp; Datenschutz"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"Datenschutz &amp; Sicherheit"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"Empfehlungen"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"Warnungen"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"Wichtige Warnungen"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-el/strings.xml b/SafetyCenter/Resources/shared_res/values-el/strings.xml
index ac93dedd8..c6f005d5f 100644
--- a/SafetyCenter/Resources/shared_res/values-el/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-el/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Εμφάνιση ειδοποίησης}other{Εμφάνιση ειδοποιήσεων}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Δεν ήταν δυνατό το άνοιγμα της σελίδας"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Δεν ήταν δυνατή η επίλυση της ειδοποίησης"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Δεν ήταν δυνατή η ανανέωση των ρυθμίσεων"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Δεν ήταν δυνατός ο έλεγχος της ρύθμισης}other{Δεν ήταν δυνατός ο έλεγχος των ρυθμίσεων}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Το προφίλ εργασίας έχει τεθεί σε παύση"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Δεν υπάρχουν ακόμα πληροφορίες"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml
index 7e0312660..f389be966 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{See alert}other{See alerts}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Couldn\'t open page"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Couldn\'t resolve alert"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Couldn\'t refresh settings"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Couldn\'t check setting}other{Couldn\'t check settings}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Work profile is paused"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"No info yet"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml
index f63228247..00234beca 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{See alert}other{See alerts}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Couldnt open page"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Couldnt resolve alert"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Couldnt refresh settings"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Couldnt check setting}other{Couldnt check settings}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Work profile is paused"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"No info yet"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml
index 7e0312660..f389be966 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{See alert}other{See alerts}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Couldn\'t open page"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Couldn\'t resolve alert"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Couldn\'t refresh settings"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Couldn\'t check setting}other{Couldn\'t check settings}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Work profile is paused"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"No info yet"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml
index 7e0312660..f389be966 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{See alert}other{See alerts}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Couldn\'t open page"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Couldn\'t resolve alert"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Couldn\'t refresh settings"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Couldn\'t check setting}other{Couldn\'t check settings}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Work profile is paused"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"No info yet"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml
index d1cd45d43..811e6e7fd 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎See alert‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎See alerts‎‏‎‎‏‎}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎Couldnt open page‎‏‎‎‏‎"</string>
<string name="resolving_action_error" msgid="371968886143262375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎Couldnt resolve alert‎‏‎‎‏‎"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎Couldnt refresh settings‎‏‎‎‏‎"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎Couldnt check setting‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎Couldnt check settings‎‏‎‎‏‎}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‏‎Work profile is paused‎‏‎‎‏‎"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎No info yet‎‏‎‎‏‎"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml b/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml
index b94198674..29613fc69 100644
--- a/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Ver alerta}many{Ver alertas}other{Ver alertas}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"No se pudo abrir la página"</string>
<string name="resolving_action_error" msgid="371968886143262375">"No se pudo resolver la alerta"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"No se pudo actualizar la configuración"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{No se pudo revisar el parámetro de configuración}many{No se pudieron revisar los parámetros de configuración}other{No se pudieron revisar los parámetros de configuración}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"El perfil de trabajo está en pausa"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Aún no hay información"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-es/strings.xml b/SafetyCenter/Resources/shared_res/values-es/strings.xml
index 245d3394c..a13a68d8f 100644
--- a/SafetyCenter/Resources/shared_res/values-es/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-es/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Ver alerta}many{Ver alertas}other{Ver alertas}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"No se ha podido abrir la página"</string>
<string name="resolving_action_error" msgid="371968886143262375">"No se ha podido resolver la alerta"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"No se han podido actualizar los ajustes"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{No se ha podido comprobar el ajuste}many{No se han podido comprobar los ajustes}other{No se han podido comprobar los ajustes}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"El perfil de trabajo está en pausa"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Aún no hay información"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-et/strings.xml b/SafetyCenter/Resources/shared_res/values-et/strings.xml
index 0aab37815..cfe0541d2 100644
--- a/SafetyCenter/Resources/shared_res/values-et/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-et/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Vaadake hoiatust}other{Vaadake hoiatusi}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Lehte ei saanud avada"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Hoiatusega seotud probleemi ei saanud lahendada"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Seadeid ei saanud värskendada"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Seadet ei õnnestunud kontrollida}other{Seadeid ei õnnestunud kontrollida}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Tööprofiil on peatatud"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Teavet ei ole veel"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-eu/strings.xml b/SafetyCenter/Resources/shared_res/values-eu/strings.xml
index 88942a47b..c00cb5827 100644
--- a/SafetyCenter/Resources/shared_res/values-eu/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-eu/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Ikusi alerta}other{Ikusi alertak}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Ezin da ireki orria"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Ezin izan da ebatzi alerta"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Ezin izan dira freskatu ezarpenak"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Ezin izan da egiaztatu ezarpena}other{Ezin izan dira egiaztatu ezarpenak}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Laneko profila pausatuta dago"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Ez dago informaziorik oraindik"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-fa/strings.xml b/SafetyCenter/Resources/shared_res/values-fa/strings.xml
index df11d470f..744ac950c 100644
--- a/SafetyCenter/Resources/shared_res/values-fa/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-fa/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{دیدن هشدار}one{دیدن هشدار}other{دیدن هشدارها}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"صفحه باز نشد"</string>
<string name="resolving_action_error" msgid="371968886143262375">"هشدار رفع نشد"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"تنظیمات بازآوری نشد"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{تنظیم بررسی نشد}one{تنظیم بررسی نشد}other{تنظیمات بررسی نشدند}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"نمایه کاری موقتاً متوقف شده است"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"هنوز اطلاعاتی دردسترس نیست"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-fi/strings.xml b/SafetyCenter/Resources/shared_res/values-fi/strings.xml
index bad542e6d..801b40081 100644
--- a/SafetyCenter/Resources/shared_res/values-fi/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-fi/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Näytä ilmoitus}other{Näytä ilmoitukset}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Sivun avaaminen epäonnistui"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Hälytyksen ratkaiseminen epäonnistui"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Asetuksia ei voitu päivittää"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Asetuksen tarkistaminen ei onnistunut}other{Asetusten tarkistaminen ei onnistunut}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Työprofiilin käyttö on keskeytetty"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Ei vielä tietoa"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml b/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml
index c1530b2cb..3956c61f5 100644
--- a/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Afficher l\'alerte}one{Afficher l\'alerte}many{Afficher les alertes}other{Afficher les alertes}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Impossible d\'ouvrir la page"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Impossible de résoudre l\'alerte"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Impossible d\'actualiser les paramètres"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Impossible de vérifier le paramètre}one{Impossible de vérifier le paramètre}many{Impossible de vérifier les paramètres}other{Impossible de vérifier les paramètres}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Le profil professionnel est interrompu"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Aucune donnée pour le moment"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-fr/strings.xml b/SafetyCenter/Resources/shared_res/values-fr/strings.xml
index d7bf215a0..b05f99a15 100644
--- a/SafetyCenter/Resources/shared_res/values-fr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-fr/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Voir l\'alerte}one{Voir l\'alerte}many{Voir les alertes}other{Voir les alertes}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Impossible d\'accéder à la page"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Impossible de résoudre l\'alerte"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Impossible d\'actualiser les paramètres"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Impossible de vérifier le paramètre}one{Impossible de vérifier le paramètre}many{Impossible de vérifier les paramètres}other{Impossible de vérifier les paramètres}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Profil professionnel en pause"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Aucune info pour l\'instant"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-gl/strings.xml b/SafetyCenter/Resources/shared_res/values-gl/strings.xml
index ea934a37b..3a0251dd2 100644
--- a/SafetyCenter/Resources/shared_res/values-gl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-gl/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Consulta a alerta}other{Consulta as alertas}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Non se puido abrir a páxina"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Non se puido resolver a alerta"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Non se puido actualizar a configuración"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Non se puido comprobar a opción de configuración}other{Non se puideron comprobar as opcións de configuración}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"O perfil de traballo está en pausa"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Aínda non hai información"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-gu/strings.xml b/SafetyCenter/Resources/shared_res/values-gu/strings.xml
index 0894b304b..ded82b3e5 100644
--- a/SafetyCenter/Resources/shared_res/values-gu/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-gu/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{અલર્ટ જુઓ}one{અલર્ટ જુઓ}other{અલર્ટ જુઓ}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"પેજ ખોલી શક્યા નથી"</string>
<string name="resolving_action_error" msgid="371968886143262375">"અલર્ટનું નિરાકરણ લાવી શક્યા નથી"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"સેટિંગ રિફ્રેશ કરી શકાયા નથી"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{સેટિંગ ચેક કરી શકાયું નથી}one{સેટિંગ ચેક કરી શકાયું નથી}other{સેટિંગ ચેક કરી શકાયા નથી}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"ઑફિસની પ્રોફાઇલ થોભાવી છે"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"હજી સુધી કોઈ માહિતી નથી"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-hi/strings.xml b/SafetyCenter/Resources/shared_res/values-hi/strings.xml
index c9a80e7f0..f9d5e34af 100644
--- a/SafetyCenter/Resources/shared_res/values-hi/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-hi/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="scanning_title" msgid="5424849039854311398">"स्कैन किया जा रहा है"</string>
- <string name="loading_summary" msgid="3740846439782713910">"डिवाइस सेटिंग की जांच हो रही है…"</string>
+ <string name="loading_summary" msgid="3740846439782713910">"डिवाइस की सेटिंग की जांच की जा रही है…"</string>
<string name="overall_severity_level_ok_title" msgid="2041250138727564565">"डिवाइस सुरक्षित लग रहा है"</string>
<string name="overall_severity_level_ok_summary" msgid="383626536912856690">"कोई समस्या नहीं मिली"</string>
<string name="overall_severity_level_tip_summary" msgid="1935765582243024999">"{count,plural, =1{सुझाव देखें}one{सुझाव देखें}other{सुझावों को देखें}}"</string>
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{चेतावनी देखें}one{चेतावनी देखें}other{चेतावनियां देखें}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"पेज को खोला नहीं जा सका"</string>
<string name="resolving_action_error" msgid="371968886143262375">"चेतावनी में बताई गई समस्या को ठीक नहीं किया जा सका"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"सेटिंग को रीफ़्रेश नहीं किया जा सका"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{सेटिंग की जांच नहीं की जा सकी}one{सेटिंग की जांच नहीं की जा सकी}other{सेटिंग की जांच नहीं की जा सकी}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"वर्क प्रोफ़ाइल रोक दी गई है"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"कोई जानकारी मौजूद नहीं है"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-hr/strings.xml b/SafetyCenter/Resources/shared_res/values-hr/strings.xml
index ed02bc991..fe11e931b 100644
--- a/SafetyCenter/Resources/shared_res/values-hr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-hr/strings.xml
@@ -24,7 +24,7 @@
<string name="overall_severity_level_tip_summary" msgid="1935765582243024999">"{count,plural, =1{Pogledajte preporuku}one{Pogledajte preporuke}few{Pogledajte preporuke}other{Pogledajte preporuke}}"</string>
<string name="overall_severity_level_action_taken_summary" msgid="8064091657855656545">"{count,plural, =1{Poduzeta je radnja}one{Poduzete su radnje}few{Poduzete su radnje}other{Poduzete su radnje}}"</string>
<string name="overall_severity_level_ok_review_title" msgid="1494321117696765360">"Pregledajte postavke"</string>
- <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Provjera popisa postavki"</string>
+ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Popis postavki za provjeru"</string>
<string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Uređaj je možda ugrožen"</string>
<string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Uređaj je ugrožen"</string>
<string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Podaci mogu biti ugroženi"</string>
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Pogledajte upozorenje}one{Pogledajte upozorenja}few{Pogledajte upozorenja}other{Pogledajte upozorenja}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Otvaranje stranice nije uspjelo"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Razrješavanje upozorenja nije uspjelo"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Nije bilo moglo osvježiti postavke"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Nije moguće provjeriti postavku}one{Nije moguće provjeriti postavke}few{Nije moguće provjeriti postavke}other{Nije moguće provjeriti postavke}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Poslovni profil je pauziran"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Još nema podataka"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-hu/strings.xml b/SafetyCenter/Resources/shared_res/values-hu/strings.xml
index 5f97845bc..b8a8540ef 100644
--- a/SafetyCenter/Resources/shared_res/values-hu/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-hu/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Értesítés megtekintése}other{Értesítések megtekintése}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Nem sikerült megnyitni az oldalt"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Nem sikerült feloldani a figyelmeztetést"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Nem sikerült a beállítások frissítése"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Nem sikerült a beállítás ellenőrzése}other{Nem sikerült a beállítások ellenőrzése}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"A munkaprofil használata szünetel"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Még nincsenek adatok"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-hy/strings.xml b/SafetyCenter/Resources/shared_res/values-hy/strings.xml
index befaeb8e5..ff38a14eb 100644
--- a/SafetyCenter/Resources/shared_res/values-hy/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-hy/strings.xml
@@ -40,11 +40,10 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Տեսնել ծանուցումը}one{Տեսնել ծանուցումները}other{Տեսնել ծանուցումները}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Չհաջողվեց բացել էջը"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Չհաջողվեց լուծել ծանուցումը"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Չհաջողվեց թարմացնել կարգավորումները"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Չհաջողվեց ստուգել կարգավորումը}one{Չհաջողվեց ստուգել կարգավորումը}other{Չհաջողվեց ստուգել կարգավորումները}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Աշխատանքային պրոֆիլը դադարեցված է"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Տեղեկություններ դեռ չկան"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"Անվտանգություն և գաղտնիություն"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"Ապահովություն և գաղտնիություն"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"Առաջարկներ"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"Նախազգուշացումներ"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"Կարևոր նախազգուշացումներ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-in/strings.xml b/SafetyCenter/Resources/shared_res/values-in/strings.xml
index cd584dbab..8d25d7340 100644
--- a/SafetyCenter/Resources/shared_res/values-in/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-in/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Lihat peringatan}other{Lihat peringatan}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Tidak dapat membuka halaman"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Tidak dapat menyelesaikan peringatan"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Tidak dapat merefresh setelan"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Tidak dapat memeriksa setelan}other{Tidak dapat memeriksa setelan}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Profil kerja dijeda"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Belum ada info"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-is/strings.xml b/SafetyCenter/Resources/shared_res/values-is/strings.xml
index 3e36f81f1..faa4c2e1f 100644
--- a/SafetyCenter/Resources/shared_res/values-is/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-is/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Sjá viðvörun}one{Sjá viðvaranir}other{Sjá viðvaranir}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Ekki tókst að opna síðuna"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Ekki tókst að leysa úr viðvöruninni"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Ekki tókst að endurnýja stillingar"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Ekki tókst að athuga stillingu}one{Ekki tókst að athuga stillingar}other{Ekki tókst að athuga stillingar}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Hlé gert á vinnusniði"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Engar upplýsingar ennþá"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-it/strings.xml b/SafetyCenter/Resources/shared_res/values-it/strings.xml
index 68aa4eab3..b2661e84a 100644
--- a/SafetyCenter/Resources/shared_res/values-it/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-it/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Visualizza l\'avviso}many{Visualizza gli avvisi}other{Visualizza gli avvisi}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Impossibile aprire la pagina"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Impossibile risolvere l\'avviso"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Impossibile aggiornare le impostazioni"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Impossibile controllare l\'impostazione}many{Impossibile controllare le impostazioni}other{Impossibile controllare le impostazioni}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Profilo di lavoro in pausa"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Ancora nessuna informazione"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-iw/strings.xml b/SafetyCenter/Resources/shared_res/values-iw/strings.xml
index 05cb0cd18..8a0794632 100644
--- a/SafetyCenter/Resources/shared_res/values-iw/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-iw/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{הצגת ההתראה}one{הצגת ההתראות}two{הצגת ההתראות}other{הצגת ההתראות}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"לא ניתן היה לפתוח את הדף"</string>
<string name="resolving_action_error" msgid="371968886143262375">"לא ניתן היה לפתור את הבעיה בהתראה"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"לא ניתן היה לרענן את ההגדרות"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{לא ניתן היה לבדוק את ההגדרה}one{לא ניתן היה לבדוק את ההגדרות}two{לא ניתן היה לבדוק את ההגדרות}other{לא ניתן היה לבדוק את ההגדרות}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"פרופיל העבודה מושהה"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"אין עדיין פרטים"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ja/strings.xml b/SafetyCenter/Resources/shared_res/values-ja/strings.xml
index 9e962fa55..c6d76eb2c 100644
--- a/SafetyCenter/Resources/shared_res/values-ja/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ja/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{アラートを確認してください}other{アラートを確認してください}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"ページを開けませんでした"</string>
<string name="resolving_action_error" msgid="371968886143262375">"アラートを解決できませんでした"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"設定を更新できませんでした"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{設定を確認できませんでした}other{設定を確認できませんでした}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"仕事用プロファイルが一時停止しています"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"まだ情報がありません"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ka/strings.xml b/SafetyCenter/Resources/shared_res/values-ka/strings.xml
index 4dda15927..de8890dec 100644
--- a/SafetyCenter/Resources/shared_res/values-ka/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ka/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{გაფრთხილების ნახვა}other{გაფრთხილებების ნახვა}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"გვერდის გახსნა ვერ მოხერხდა"</string>
<string name="resolving_action_error" msgid="371968886143262375">"გაფრთხილება ვერ გადაიჭრა"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"პარამეტრები ვერ განახლდა"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{პარამეტრის შემოწმება ვერ მოხერხდა}other{პარამეტრების Შემოწმება ვერ მოხერხდა}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"სამსახურის პროფილი დაპაუზებულია"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ინფო ჯერ არ არის"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-kk/strings.xml b/SafetyCenter/Resources/shared_res/values-kk/strings.xml
index 5ea3fa3e1..386a0d465 100644
--- a/SafetyCenter/Resources/shared_res/values-kk/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-kk/strings.xml
@@ -25,7 +25,7 @@
<string name="overall_severity_level_action_taken_summary" msgid="8064091657855656545">"{count,plural, =1{Әрекет жасалды.}other{Әрекеттер жасалды.}}"</string>
<string name="overall_severity_level_ok_review_title" msgid="1494321117696765360">"Параметрлерді тексерy"</string>
<string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Параметрлер тізімін тексеріңіз."</string>
- <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Құрылғыға қауіп төнген болуы мүмкін"</string>
+ <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Құрылғыға қауіп төнген сияқты"</string>
<string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Құрылғыға қауіп төніп тұр"</string>
<string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Деректерге қауіп төнген сияқты"</string>
<string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Деректерге қауіп төніп тұр"</string>
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Хабарландыруды көру}other{Хабарландыруларды көру}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Бет ашылмады."</string>
<string name="resolving_action_error" msgid="371968886143262375">"Хабарландыруда көрсетілген мәселе шешілмеді."</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Параметрлер жаңартылмады."</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Параметрді тексеру мүмкін болмады.}other{Параметрлерді тексеру мүмкін болмады.}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Жұмыс профилі кідіртілді."</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Әзірге мәлімет жоқ."</string>
diff --git a/SafetyCenter/Resources/shared_res/values-km/strings.xml b/SafetyCenter/Resources/shared_res/values-km/strings.xml
index d2bf8fc48..a145762cd 100644
--- a/SafetyCenter/Resources/shared_res/values-km/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-km/strings.xml
@@ -40,11 +40,10 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{មើលការជូន​ដំណឹង}other{មើលការជូនដំណឹង}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"មិនអាច​បើកទំព័រ​បានទេ"</string>
<string name="resolving_action_error" msgid="371968886143262375">"មិនអាច​ដោះស្រាយការជូនដំណឹងនេះ​បានទេ"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"មិនអាចផ្ទុកការកំណត់ឡើងវិញបានទេ"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{មិន​អាច​ពិនិត្យ​មើល​ការកំណត់​បាន​ទេ}other{មិន​អាច​ពិនិត្យ​មើល​ការកំណត់​បាន​ទេ}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"កម្រងព័ត៌មានការងារត្រូវបាន​ផ្អាក"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"មិន​ទាន់​មាន​ព័ត៌មាន​នៅ​ឡើយ​ទេ"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"សុវត្ថិភាព និងឯកជនភាព"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"សន្តិសុខ និងឯកជនភាព"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"ការណែនាំ"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"សារប្រុងប្រយ័ត្ន"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"សារប្រុងប្រយ័ត្នសំខាន់ៗ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-kn/strings.xml b/SafetyCenter/Resources/shared_res/values-kn/strings.xml
index 9c3cb9dca..46eedaac5 100644
--- a/SafetyCenter/Resources/shared_res/values-kn/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-kn/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{ಎಚ್ಚರಿಕೆಯನ್ನು ನೋಡಿ}one{ಎಚ್ಚರಿಕೆಗಳನ್ನು ನೋಡಿ}other{ಎಚ್ಚರಿಕೆಗಳನ್ನು ನೋಡಿ}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"ಪುಟವನ್ನು ತೆರೆಯಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="resolving_action_error" msgid="371968886143262375">"ಅಲರ್ಟ್ ಅನ್ನು ಬಗೆಹರಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ರಿಫ್ರೆಶ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ಸೆಟ್ಟಿಂಗ್ ಅನ್ನು ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ}one{ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ}other{ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ಇನ್ನೂ ಯಾವುದೇ ಮಾಹಿತಿ ಲಭ್ಯವಿಲ್ಲ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ko/strings.xml b/SafetyCenter/Resources/shared_res/values-ko/strings.xml
index 295fe4992..1a5938c9a 100644
--- a/SafetyCenter/Resources/shared_res/values-ko/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ko/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{알림 보기}other{알림 보기}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"페이지를 열 수 없습니다."</string>
<string name="resolving_action_error" msgid="371968886143262375">"알림을 해결할 수 없습니다."</string>
- <string name="refresh_timeout" msgid="251734999692581852">"설정을 새로고침할 수 없습니다."</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{설정을 확인할 수 없습니다.}other{설정을 확인할 수 없습니다.}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"직장 프로필이 일시중지됨"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"아직 정보 없음"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ky/strings.xml b/SafetyCenter/Resources/shared_res/values-ky/strings.xml
index 2da0e82e4..474377b87 100644
--- a/SafetyCenter/Resources/shared_res/values-ky/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ky/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Эскетүүнү көрүү}other{Эскертүүлөрдү көрүү}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Барак ачылган жок"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Эскертүү чечилген жок"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Параметрлер жаңырган жок"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Параметр текшерилген жок}other{Параметрлер текшерилген жок}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Жумуш профили тындырылды"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Азырынча маалымат жок"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-lo/strings.xml b/SafetyCenter/Resources/shared_res/values-lo/strings.xml
index 724411879..0abf76b30 100644
--- a/SafetyCenter/Resources/shared_res/values-lo/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-lo/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{ເບິ່ງແຈ້ງເຕືອນ}other{ເບິ່ງແຈ້ງເຕືອນ}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"ບໍ່ສາມາດເປີດໜ້າໄດ້"</string>
<string name="resolving_action_error" msgid="371968886143262375">"ບໍ່ສາມາດແກ້ໄຂແຈ້ງເຕືອນໄດ້"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"ບໍ່ສາມາດໂຫຼດການຕັ້ງຄ່າຄືນໃໝ່"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ກວດສອບການຕັ້ງຄ່າບໍ່ໄດ້}other{ກວດສອບການຕັ້ງຄ່າບໍ່ໄດ້}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"ຢຸດໂປຣໄຟລ໌ວຽກໄວ້ຊົ່ວຄາວແລ້ວ"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ບໍ່ມີຂໍ້ມູນເທື່ອ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-lt/strings.xml b/SafetyCenter/Resources/shared_res/values-lt/strings.xml
index fc1704c13..832ef32f1 100644
--- a/SafetyCenter/Resources/shared_res/values-lt/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-lt/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Žr. įspėjimą}one{Žr. įspėjimus}few{Žr. įspėjimus}many{Žr. įspėjimus}other{Žr. įspėjimus}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Nepavyko atidaryti puslapio"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Nepavyko pašalinti įspėjimo"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Nepavyko atnaujinti nustatymų"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Nepavyko patikrinti nustatymo}one{Nepavyko patikrinti nustatymų}few{Nepavyko patikrinti nustatymų}many{Nepavyko patikrinti nustatymų}other{Nepavyko patikrinti nustatymų}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Darbo profilis pristabdytas"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Kol kas informacijos nėra"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-lv/strings.xml b/SafetyCenter/Resources/shared_res/values-lv/strings.xml
index c27624603..eb544b15f 100644
--- a/SafetyCenter/Resources/shared_res/values-lv/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-lv/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Skatiet brīdinājumu}zero{Skatiet brīdinājumus}one{Skatiet brīdinājumus}other{Skatiet brīdinājumus}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Nevarēja atvērt lapu"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Nevarēja atrisināt ieteikumu vai brīdinājumu"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Nevarēja atsvaidzināt iestatījumus"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Nevarēja pārbaudīt iestatījumu.}zero{Nevarēja pārbaudīt iestatījumus.}one{Nevarēja pārbaudīt iestatījumus.}other{Nevarēja pārbaudīt iestatījumus.}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Darba profila darbība ir apturēta"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Vēl nav informācijas"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-mk/strings.xml b/SafetyCenter/Resources/shared_res/values-mk/strings.xml
index 2ba7a1976..30b405c67 100644
--- a/SafetyCenter/Resources/shared_res/values-mk/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-mk/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Видете го предупредувањето}one{Видете ги предупредувањата}other{Видете ги предупредувањата}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Не можеше да се отвори страницата"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Не можеше да се реши предупредувањето"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Не можеше да се освежат поставките"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Не може да се провери поставката}one{Не може да се проверат поставките}other{Не може да се проверат поставките}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Работниот профил е паузиран"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Сѐ уште нема податоци"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ml/strings.xml b/SafetyCenter/Resources/shared_res/values-ml/strings.xml
index a05a7eed1..4a77469da 100644
--- a/SafetyCenter/Resources/shared_res/values-ml/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ml/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{മുന്നറിയിപ്പ് കാണുക}other{മുന്നറിയിപ്പുകൾ കാണുക}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"പേജ് തുറക്കാനായില്ല"</string>
<string name="resolving_action_error" msgid="371968886143262375">"മുന്നറിയിപ്പ് പരിഹരിക്കാനായില്ല"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"ക്രമീകരണം റീഫ്രഷ് ചെയ്യാനായില്ല"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ക്രമീകരണം പരിശോധിക്കാനായില്ല}other{ക്രമീകരണം പരിശോധിക്കാനായില്ല}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"ഔദ്യോഗിക പ്രൊഫൈൽ തൽക്കാലം നിർത്തിയിരിക്കുന്നു"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ഇതുവരെ വിവരങ്ങളൊന്നുമില്ല"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-mn/strings.xml b/SafetyCenter/Resources/shared_res/values-mn/strings.xml
index 8b2f17f37..90ddca44a 100644
--- a/SafetyCenter/Resources/shared_res/values-mn/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-mn/strings.xml
@@ -40,11 +40,10 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Сэрэмжлүүлгийг харах}other{Сэрэмжлүүлгүүдийг харах}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Хуудсыг нээж чадсангүй"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Сэрэмжлүүлгийг шийдвэрлэж чадсангүй"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Тохиргоог сэргээж чадсангүй"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Тохиргоог шалгаж чадсангүй}other{Тохиргоог шалгаж чадсангүй}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Ажлын профайлыг түр зогсоосон"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Мэдээлэл хараахан алга"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"Аюулгүй байдал &amp; нууцлал"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"Аюулгүй байдал ба нууцлал"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"Зөвлөмж"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"Сануулга"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"Ноцтой сануулга"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-mr/strings.xml b/SafetyCenter/Resources/shared_res/values-mr/strings.xml
index f406a2931..0b47053cd 100644
--- a/SafetyCenter/Resources/shared_res/values-mr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-mr/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{सूचना पहा}other{सूचना पहा}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"पेज उघडता आले नाही"</string>
<string name="resolving_action_error" msgid="371968886143262375">"इशाऱ्याचे निराकरण करता आले नाही"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"सेटिंग्ज रिफ्रेश करता आली नाही"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{सेटिंग तपासता आले नाही}other{सेटिंग्ज तपासता आली नाहीत}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"कार्य प्रोफाइल थांबवली आहे"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"अद्याप कोणतीही माहिती नाही"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ms/strings.xml b/SafetyCenter/Resources/shared_res/values-ms/strings.xml
index bb98c69d0..f8c3e2c32 100644
--- a/SafetyCenter/Resources/shared_res/values-ms/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ms/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Lihat makluman}other{Lihat makluman}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Tidak dapat membuka halaman"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Tidak dapat menyelesaikan amaran"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Tidak dapat menyegar semula tetapan"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Tidak dapat menyemak tetapan}other{Tidak dapat menyemak tetapan}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Profil kerja dijeda"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Belum ada maklumat lagi"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-my/strings.xml b/SafetyCenter/Resources/shared_res/values-my/strings.xml
index af7f71a34..cf5108a13 100644
--- a/SafetyCenter/Resources/shared_res/values-my/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-my/strings.xml
@@ -40,11 +40,10 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{သတိပေးချက် ကြည့်ရန်}other{သတိပေးချက်များ ကြည့်ရန်}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"စာမျက်နှာကို ဖွင့်၍မရပါ"</string>
<string name="resolving_action_error" msgid="371968886143262375">"သတိပေးချက်ကို ဖြေရှင်း၍မရပါ"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"ဆက်တင်များကို ပြန်လည် စတင်၍မရပါ"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ဆက်တင်ကြည့်၍မရပါ}other{ဆက်တင်များ ကြည့်၍မရပါ}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"အလုပ်ပရိုဖိုင် ခဏရပ်ထားသည်"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"အချက်အလက် မရှိသေးပါ"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"လုံခြုံရေးနှင့် ကိုယ်ရေးဒေတာ"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"စက်နှင့်အချက်အလက်လုံခြုံရေး"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"အကြံပြုချက်များ"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"သတိပေးချက်များ"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"အရေးကြီးသော သတိပေးချက်များ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-nb/strings.xml b/SafetyCenter/Resources/shared_res/values-nb/strings.xml
index 8365aaea8..61338a390 100644
--- a/SafetyCenter/Resources/shared_res/values-nb/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-nb/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Se varselet}other{Se varslene}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Kunne ikke åpne siden"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Kunne ikke løse varselet"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Kunne ikke laste inn innstillingene på nytt"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Kunne ikke sjekke innstillingen}other{Kunne ikke sjekke innstillingene}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Jobbprofilen er satt på pause"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Ingen informasjon ennå"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ne/strings.xml b/SafetyCenter/Resources/shared_res/values-ne/strings.xml
index 4e55d23f1..e7c392a3b 100644
--- a/SafetyCenter/Resources/shared_res/values-ne/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ne/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{अलर्ट हेर्नुहोस्}other{अलर्टहरू हेर्नुहोस्}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"पेज खोल्न सकिएन"</string>
<string name="resolving_action_error" msgid="371968886143262375">"अलर्ट समाधान गर्न सकिएन"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"सेटिङ रिफ्रेस गर्न सकिएन"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{सेटिङ जाँच गर्न सकिएन}other{सेटिङहरू जाँच गर्न सकिएन}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"कार्य प्रोफाइल पज गरिएको छ"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"कुनै जानकारी उपलब्ध छैन"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-nl/strings.xml b/SafetyCenter/Resources/shared_res/values-nl/strings.xml
index b916f37bc..4b1000112 100644
--- a/SafetyCenter/Resources/shared_res/values-nl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-nl/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="scanning_title" msgid="5424849039854311398">"Scannen"</string>
- <string name="loading_summary" msgid="3740846439782713910">"Apparaat­instellingen­ checken…"</string>
+ <string name="loading_summary" msgid="3740846439782713910">"Apparaat­instellingen­ controleren…"</string>
<string name="overall_severity_level_ok_title" msgid="2041250138727564565">"Dat ziet er goed uit"</string>
<string name="overall_severity_level_ok_summary" msgid="383626536912856690">"Geen problemen gevonden"</string>
<string name="overall_severity_level_tip_summary" msgid="1935765582243024999">"{count,plural, =1{Aanbeveling bekijken}other{Aanbevelingen bekijken}}"</string>
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Melding bekijken}other{Meldingen bekijken}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Kan de pagina niet openen"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Kan melding niet oplossen"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Kan instellingen niet vernieuwen"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Kan instelling niet checken}other{Kan instellingen niet checken}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Werkprofiel is onderbroken"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Nog geen informatie"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-or/strings.xml b/SafetyCenter/Resources/shared_res/values-or/strings.xml
index cf550456f..341149120 100644
--- a/SafetyCenter/Resources/shared_res/values-or/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-or/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{ଆଲର୍ଟ ଦେଖନ୍ତୁ}other{ଆଲର୍ଟଗୁଡ଼ିକ ଦେଖନ୍ତୁ}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"ପୃଷ୍ଠାକୁ ଖୋଲା ଯାଇପାରିଲା ନାହିଁ"</string>
<string name="resolving_action_error" msgid="371968886143262375">"ଆଲର୍ଟର ସମାଧାନ କରାଯାଇପାରିଲା ନାହିଁ"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"ସେଟିଂସ ରିଫ୍ରେସ କରାଯାଇପାରିଲା ନାହିଁ"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ସେଟିଂ ଯାଞ୍ଚ କରାଯାଇପାରିଲା ନାହିଁ}other{ସେଟିଂସ ଯାଞ୍ଚ କରାଯାଇପାରିଲା ନାହିଁ}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"ୱାର୍କ ପ୍ରୋଫାଇଲକୁ ବିରତ କରାଯାଇଛି"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ଏପର୍ଯ୍ୟନ୍ତ କୌଣସି ସୂଚନା ନାହିଁ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pa/strings.xml b/SafetyCenter/Resources/shared_res/values-pa/strings.xml
index e13d6327f..4ad0a331c 100644
--- a/SafetyCenter/Resources/shared_res/values-pa/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pa/strings.xml
@@ -39,12 +39,11 @@
<string name="overall_severity_level_critical_account_warning_title" msgid="1913235490583842004">"ਖਾਤਾ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string>
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{ਅਲਰਟ ਦੇਖੋ}one{ਅਲਰਟ ਦੇਖੋ}other{ਅਲਰਟ ਦੇਖੋ}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"ਪੰਨਾ ਖੋਲ੍ਹਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ"</string>
- <string name="resolving_action_error" msgid="371968886143262375">"ਸੁਚੇਤਨਾ ਦਾ ਹੱਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"ਸੈਟਿੰਗਾਂ ਨੂੰ ਰਿਫ੍ਰੈਸ਼ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
+ <string name="resolving_action_error" msgid="371968886143262375">"ਅਲਰਟ ਦਾ ਹੱਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ਸੈਟਿੰਗ ਦੀ ਜਾਂਚ ਨਹੀਂ ਕਰ ਸਕੇ}one{ਸੈਟਿੰਗ ਦੀ ਜਾਂਚ ਨਹੀਂ ਕਰ ਸਕੇ}other{ਸੈਟਿੰਗਾਂ ਦੀ ਜਾਂਚ ਨਹੀਂ ਕਰ ਸਕੇ}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ਅਜੇ ਕੋਈ ਜਾਣਕਾਰੀ ਨਹੀਂ ਹੈ"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"ਸਿਕਿਊਰਟੀ ਅਤੇ ਪਰਦੇਦਾਰੀ"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"ਸਿਫ਼ਾਰਸ਼ਾਂ"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"ਚਿਤਾਵਨੀਆਂ"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"ਗੰਭੀਰ ਚਿਤਾਵਨੀਆਂ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pl/strings.xml b/SafetyCenter/Resources/shared_res/values-pl/strings.xml
index 23a331b7f..89a242e7f 100644
--- a/SafetyCenter/Resources/shared_res/values-pl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pl/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Zobacz alert}few{Zobacz alerty}many{Zobacz alerty}other{Zobacz alerty}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Nie udało się otworzyć strony"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Nie udało się rozwiązać problemu z alertu"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Nie udało się odświeżyć ustawień"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Nie udało się sprawdzić ustawienia}few{Nie udało się sprawdzić ustawień}many{Nie udało się sprawdzić ustawień}other{Nie udało się sprawdzić ustawień}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Wstrzymano profil służbowy"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Nie ma jeszcze informacji"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml b/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml
index b9d887f78..0599f1c24 100644
--- a/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Mostrar alerta}one{Mostrar alerta}many{Mostrar alertas}other{Mostrar alertas}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Não foi possível abrir a página"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Não foi possível resolver o alerta"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Não foi possível atualizar as configurações"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Não foi possível verificar a configuração}one{Não foi possível verificar a configuração}many{Não foi possível verificar as configurações}other{Não foi possível verificar as configurações}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"O perfil de trabalho está pausado"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Ainda não há informações"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml b/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml
index 592e1b60a..835b8a14f 100644
--- a/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Veja o alerta}many{Veja os alertas}other{Veja os alertas}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Não foi possível abrir a página"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Não foi possível resolver o alerta"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Não foi possível atualizar as definições"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Não foi possível verificar a definição}many{Não foi possível verificar as definições}other{Não foi possível verificar as definições}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Perfil de trabalho em pausa"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Ainda sem informações"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pt/strings.xml b/SafetyCenter/Resources/shared_res/values-pt/strings.xml
index b9d887f78..0599f1c24 100644
--- a/SafetyCenter/Resources/shared_res/values-pt/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pt/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Mostrar alerta}one{Mostrar alerta}many{Mostrar alertas}other{Mostrar alertas}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Não foi possível abrir a página"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Não foi possível resolver o alerta"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Não foi possível atualizar as configurações"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Não foi possível verificar a configuração}one{Não foi possível verificar a configuração}many{Não foi possível verificar as configurações}other{Não foi possível verificar as configurações}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"O perfil de trabalho está pausado"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Ainda não há informações"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ro/strings.xml b/SafetyCenter/Resources/shared_res/values-ro/strings.xml
index e64b8b497..8fa57dcb4 100644
--- a/SafetyCenter/Resources/shared_res/values-ro/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ro/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Vezi alerta}few{Vezi alertele}other{Vezi alertele}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Pagina nu s-a putut deschide"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Nu s-a putut rezolva alerta"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Nu s-au putut actualiza setările"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Nu s-a putut verifica setarea}few{Nu s-au putut verifica setările}other{Nu s-au putut verifica setările}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Profilul de serviciu este întrerupt"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Nu există informații încă"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ru/strings.xml b/SafetyCenter/Resources/shared_res/values-ru/strings.xml
index 832dd396c..d35a937d4 100644
--- a/SafetyCenter/Resources/shared_res/values-ru/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ru/strings.xml
@@ -40,11 +40,10 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Посмотрите оповещение}one{Посмотрите оповещения}few{Посмотрите оповещения}many{Посмотрите оповещения}other{Посмотрите оповещения}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Не удалось открыть страницу."</string>
<string name="resolving_action_error" msgid="371968886143262375">"Не удалось устранить проблему."</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Не удалось обновить настройки"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Не удалось проверить параметр}one{Не удалось проверить параметры}few{Не удалось проверить параметры}many{Не удалось проверить параметры}other{Не удалось проверить параметры}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Действие рабочего профиля приостановлено."</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Данных пока нет"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"Защита и конфиденциальность"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"Защита и кон­фи­ден­циаль­ность"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"Рекомендации"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"Предупреждения"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"Важные предупреждения"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-si/strings.xml b/SafetyCenter/Resources/shared_res/values-si/strings.xml
index 08e6787cc..1ee63199f 100644
--- a/SafetyCenter/Resources/shared_res/values-si/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-si/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{ඇඟවීම බලන්න}one{ඇඟවීම් බලන්න}other{ඇඟවීම් බලන්න}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"පිටුව විවෘත කළ නොහැකි විය"</string>
<string name="resolving_action_error" msgid="371968886143262375">"ඇඟවීම විසඳිය නොහැකි විය"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"සැකසීම් නැවුම් කිරීමට නොහැකි විය"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{සැකසීම පරීක්ෂා කිරීමට නොහැකි විය}one{සැකසීම් පරීක්ෂා කිරීමට නොහැකි විය}other{සැකසීම් පරීක්ෂා කිරීමට නොහැකි විය}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"කාර්යාල පැතිකඩ විරාම කර ඇත"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"තවම තතු නැත"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sk/strings.xml b/SafetyCenter/Resources/shared_res/values-sk/strings.xml
index 0c6da9c5b..905c3ffe5 100644
--- a/SafetyCenter/Resources/shared_res/values-sk/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sk/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Zobraziť upozornenie}few{Zobraziť upozornenia}many{See alerts}other{Zobraziť upozornenia}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Stránku sa nepodarilo otvoriť"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Upozornenie sa nepodarilo vyriešiť"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Nastavenia sa nepodarilo obnoviť"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Nastavenie sa nepodarilo skontrolovať}few{Nastavenia sa nepodarilo skontrolovať}many{Nastavenia sa nepodarilo skontrolovať}other{Nastavenia sa nepodarilo skontrolovať}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Pracovný profil je pozastavený"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Zatiaľ žiadne informácie"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sl/strings.xml b/SafetyCenter/Resources/shared_res/values-sl/strings.xml
index c8b0d7d00..4502b63fd 100644
--- a/SafetyCenter/Resources/shared_res/values-sl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sl/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Ogled opozorila}one{Ogled opozoril}two{Ogled opozoril}few{Ogled opozoril}other{Ogled opozoril}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Strani ni bilo mogoče odpreti."</string>
<string name="resolving_action_error" msgid="371968886143262375">"Opozorila ni bilo mogoče odpraviti."</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Nastavitev ni bilo mogoče osvežiti."</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Nastavitve ni bilo mogoče preveriti.}one{Nastavitve ni bilo mogoče preveriti.}two{Nastavitev ni bilo mogoče preveriti.}few{Nastavitev ni bilo mogoče preveriti.}other{Nastavitev ni bilo mogoče preveriti.}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Delovni profil je začasno zaustavljen."</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Ni še nobenega podatka."</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sq/strings.xml b/SafetyCenter/Resources/shared_res/values-sq/strings.xml
index 68a9d3e1b..0e4981d2f 100644
--- a/SafetyCenter/Resources/shared_res/values-sq/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sq/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Shiko sinjalizimin}other{Shiko sinjalizimet}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Faqja nuk mund të hapej"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Sinjalizimi nuk mund të zgjidhej"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Cilësimet nuk mund të rifreskoheshin"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Cilësimi nuk mund të kontrollohej}other{Cilësimet nuk mund të kontrolloheshin}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Profili i punës është në pauzë"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Nuk ka ende informacione"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sr/strings.xml b/SafetyCenter/Resources/shared_res/values-sr/strings.xml
index e258d138b..e7a5db675 100644
--- a/SafetyCenter/Resources/shared_res/values-sr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sr/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Прикажи обавештење}one{Прикажи обавештења}few{Прикажи обавештења}other{Прикажи обавештења}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Отварање странице није успело"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Решавање обавештења није успело"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Освежавање подешавања није успело"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Провера подешавања није успела}one{Провера подешавања није успела}few{Провера подешавања није успела}other{Провера подешавања није успела}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Пословни профил је паузиран"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Још нема информација"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sv/strings.xml b/SafetyCenter/Resources/shared_res/values-sv/strings.xml
index a14fd7c8b..e7773a565 100644
--- a/SafetyCenter/Resources/shared_res/values-sv/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sv/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Se varning}other{Se varningar}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Det gick inte att öppna sidan"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Det gick inte att åtgärda varningen"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Det gick inte att uppdatera inställningarna"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Det gick inte att kontrollera inställningen}other{Det gick inte att kontrollera inställningarna}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Jobbprofilen är pausad"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Ingen information än"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sw/strings.xml b/SafetyCenter/Resources/shared_res/values-sw/strings.xml
index aaea33e79..7e98b1d75 100644
--- a/SafetyCenter/Resources/shared_res/values-sw/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sw/strings.xml
@@ -40,11 +40,10 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Angalia tahadhari}other{Angalia tahadhari}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Imeshindwa kufungua ukurasa"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Imeshindwa kutia alama kuwa arifa imeshughulikiwa"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Tumeshindwa kuonyesha upya mipangilio"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Imeshindwa kukagua mipangilio}other{Imeshindwa kukagua mipangilio}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Wasifu wa kazini umesimamishwa"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Bado hakuna maelezo"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"Usalama na faragha"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"Ulinzi na faragha"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"Mapendekezo"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"Maonyo"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"Maonyo muhimu"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ta/strings.xml b/SafetyCenter/Resources/shared_res/values-ta/strings.xml
index 05eb793e9..3acc13e92 100644
--- a/SafetyCenter/Resources/shared_res/values-ta/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ta/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{விழிப்பூட்டலைப் பாருங்கள்}other{விழிப்பூட்டல்களைப் பாருங்கள்}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"பக்கத்தைத் திறக்க முடியவில்லை"</string>
<string name="resolving_action_error" msgid="371968886143262375">"எச்சரிக்கையைத் தீர்க்க முடியவில்லை"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"அமைப்புகளைப் புதுப்பிக்க முடியவில்லை"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{அமைப்பைச் சரிபார்க்க முடியவில்லை}other{அமைப்புகளைச் சரிபார்க்க முடியவில்லை}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"பணிக் கணக்கு இடைநிறுத்தப்பட்டது"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"தகவல்கள் எதுவுமில்லை"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-te/strings.xml b/SafetyCenter/Resources/shared_res/values-te/strings.xml
index 1fc82228d..c9ecc0f23 100644
--- a/SafetyCenter/Resources/shared_res/values-te/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-te/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{అలర్ట్‌ను చూడండి}other{అలర్ట్‌లను చూడండి}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"పేజీని తెరవడం సాధ్యపడలేదు"</string>
<string name="resolving_action_error" msgid="371968886143262375">"అలర్ట్‌ను పరిష్కరించడం సాధ్యపడలేదు"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"సెట్టింగ్‌లను రిఫ్రెష్ చేయడం సాధ్యపడలేదు"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{సెట్టింగ్‌ను చెక్ చేయడం సాధ్యపడలేదు}other{సెట్టింగ్‌లను చెక్ చేయడం సాధ్యపడలేదు}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"వర్క్ ప్రొఫైల్ పాజ్ చేయబడింది"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ఇంకా ఏ సమాచారం లేదు"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-th/strings.xml b/SafetyCenter/Resources/shared_res/values-th/strings.xml
index 37f345214..717a8c08e 100644
--- a/SafetyCenter/Resources/shared_res/values-th/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-th/strings.xml
@@ -40,11 +40,10 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{ดูการแจ้งเตือน}other{ดูการแจ้งเตือน}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"เปิดหน้าไม่ได้"</string>
<string name="resolving_action_error" msgid="371968886143262375">"แก้ไขการแจ้งเตือนไม่ได้"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"รีเฟรชการตั้งค่าไม่ได้"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ไม่สามารถตรวจสอบการตั้งค่า}other{ไม่สามารถตรวจสอบการตั้งค่า}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"โปรไฟล์งานหยุดชั่วคราว"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ยังไม่มีข้อมูล"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"ความปลอดภัยและความเป็นส่วนตัว"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"การรักษาความปลอดภัยและความเป็นส่วนตัว"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"คำแนะนำ"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"คำเตือน"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"คำเตือนที่สำคัญ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-tl/strings.xml b/SafetyCenter/Resources/shared_res/values-tl/strings.xml
index ee7210dc2..d2449116f 100644
--- a/SafetyCenter/Resources/shared_res/values-tl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-tl/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Tingnan ang alerto}one{Tingnan ang mga alerto}other{Tingnan ang mga alerto}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Hindi mabuksan ang page"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Hindi ma-resolve ang alerto"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Hindi ma-refresh ang mga setting"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Hindi masuri ang setting}one{Hindi masuri ang mga setting}other{Hindi masuri ang mga setting}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Naka-pause ang profile sa trabaho"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Wala pang impormasyon"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-tr/strings.xml b/SafetyCenter/Resources/shared_res/values-tr/strings.xml
index 5a32bcfac..69bf874dd 100644
--- a/SafetyCenter/Resources/shared_res/values-tr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-tr/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Uyarıya göz atın}other{Uyarılara göz atın}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Sayfa açılamadı"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Uyarı sonlandırılamadı"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Ayarlar yenilenemedi"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Ayar kontrol edilemedi}other{Ayarlar kontrol edilemedi}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"İş profili duraklatıldı"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Henüz bilgi yok"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-uk/strings.xml b/SafetyCenter/Resources/shared_res/values-uk/strings.xml
index f103d5a4f..44b304f80 100644
--- a/SafetyCenter/Resources/shared_res/values-uk/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-uk/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Перегляньте сповіщення}one{Перегляньте сповіщення}few{Перегляньте сповіщення}many{Перегляньте сповіщення}other{Перегляньте сповіщення}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Не вдалося відкрити сторінку"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Не вдалося закрити сповіщення"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Не вдалось оновити налаштування"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Не вдалося перевірити налаштування}one{Не вдалося перевірити налаштування}few{Не вдалося перевірити налаштування}many{Не вдалося перевірити налаштування}other{Не вдалося перевірити налаштування}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Робочий профіль призупинено"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Поки немає інформації"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ur/strings.xml b/SafetyCenter/Resources/shared_res/values-ur/strings.xml
index df25d9ac2..f0d791f1e 100644
--- a/SafetyCenter/Resources/shared_res/values-ur/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ur/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{الرٹ دیکھیں}other{الرٹس دیکھیں}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"صفحہ نہیں کھل سکا"</string>
<string name="resolving_action_error" msgid="371968886143262375">"الرٹ حل نہیں ہو سکا"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"ترتیبات ریفریش نہیں کی جا سکی"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{ترتیب کی جانچ نہیں کی جا سکی}other{ترتیبات کی جانچ نہیں کی جا سکی}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"دفتری پروفائل روک دی گئی ہے"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"ابھی تک کوئی معلومات نہیں ہے"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-uz/strings.xml b/SafetyCenter/Resources/shared_res/values-uz/strings.xml
index 5965fb760..a4b933eb9 100644
--- a/SafetyCenter/Resources/shared_res/values-uz/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-uz/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Ogohlantirish}other{Ogohlantirishlar}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Sahifa ochilmadi"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Ogohlantirish hal qilinmadi"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Sozlamalar yangilanmadi"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Sozlama tekshirilmadi}other{Sozlamalar tekshirilmadi}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Ish profili pauzada"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Hali axborot olinmadi"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-vi/strings.xml b/SafetyCenter/Resources/shared_res/values-vi/strings.xml
index b697186e0..ba9183e54 100644
--- a/SafetyCenter/Resources/shared_res/values-vi/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-vi/strings.xml
@@ -20,7 +20,7 @@
<string name="scanning_title" msgid="5424849039854311398">"Quét"</string>
<string name="loading_summary" msgid="3740846439782713910">"Đang kiểm tra chế độ cài đặt của thiết bị…"</string>
<string name="overall_severity_level_ok_title" msgid="2041250138727564565">"Có vẻ ổn"</string>
- <string name="overall_severity_level_ok_summary" msgid="383626536912856690">"Không phát hiện thấy sự cố nào"</string>
+ <string name="overall_severity_level_ok_summary" msgid="383626536912856690">"Không phát hiện thấy vấn đề nào"</string>
<string name="overall_severity_level_tip_summary" msgid="1935765582243024999">"{count,plural, =1{Xem đề xuất}other{Xem các đề xuất}}"</string>
<string name="overall_severity_level_action_taken_summary" msgid="8064091657855656545">"{count,plural, =1{Hành động đã thực hiện}other{Hành động đã thực hiện}}"</string>
<string name="overall_severity_level_ok_review_title" msgid="1494321117696765360">"Xem lại các chế độ cài đặt"</string>
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Xem cảnh báo}other{Xem cảnh báo}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Không thể mở trang"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Không thể giải quyết vấn đề cảnh báo"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Không thể làm mới cài đặt"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Không kiểm tra được chế độ cài đặt}other{Không kiểm tra được các chế độ cài đặt}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Hồ sơ công việc của bạn đã bị tạm dừng"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Chưa có thông tin"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml b/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml
index d705612ea..bc59150b4 100644
--- a/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{查看提醒}other{查看提醒}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"无法打开页面"</string>
<string name="resolving_action_error" msgid="371968886143262375">"无法解决提醒事项"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"无法刷新设置"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{无法检查设置}other{无法检查设置}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"工作资料已被暂停"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"尚无任何信息"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml b/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml
index 0222a0acc..071d4bad1 100644
--- a/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml
@@ -40,11 +40,10 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{查看警示}other{查看警示}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"無法開啟頁面"</string>
<string name="resolving_action_error" msgid="371968886143262375">"無法解除警示"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"無法重新整理設定"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{無法檢查設定}other{無法檢查設定}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"工作設定檔已暫停"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"暫時沒有資料"</string>
- <string name="notification_channel_group_name" msgid="7155072032524876859">"安全性和私隱權"</string>
+ <string name="notification_channel_group_name" msgid="7155072032524876859">"安全性和私隱"</string>
<string name="notification_channel_name_information" msgid="2966444432152990166">"建議"</string>
<string name="notification_channel_name_recommendation" msgid="7847408286580217922">"警告"</string>
<string name="notification_channel_name_critical_warning" msgid="5994320322108351769">"嚴重警告"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml b/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml
index 1be6fa621..beb5af4a2 100644
--- a/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{查看警示}other{查看警示}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"無法開啟網頁"</string>
<string name="resolving_action_error" msgid="371968886143262375">"無法解決警示"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"無法重新整理設定"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{無法檢查設定}other{無法檢查設定}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"工作資料夾已暫停"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"目前還沒有任何資訊"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-zu/strings.xml b/SafetyCenter/Resources/shared_res/values-zu/strings.xml
index 61096c234..21c358f15 100644
--- a/SafetyCenter/Resources/shared_res/values-zu/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-zu/strings.xml
@@ -40,7 +40,6 @@
<string name="overall_severity_n_alerts_summary" msgid="3262010942295408403">"{count,plural, =1{Bona isixwayiso}one{Bona izixwayiso}other{Bona izixwayiso}}"</string>
<string name="redirecting_error" msgid="8146983632878233202">"Ayikwazanga ukuvula ikhasi"</string>
<string name="resolving_action_error" msgid="371968886143262375">"Ayikwazanga ukuxazulula isexwayiso"</string>
- <string name="refresh_timeout" msgid="251734999692581852">"Ayikwazanga ukuvuselela amasethingi"</string>
<string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Ayikwazanga ukuhlola isethingi}one{Ayikwazanga ukuhlola amasethingi}other{Ayikwazanga ukuhlola amasethingi}}"</string>
<string name="work_profile_paused" msgid="7037400224040869079">"Iphrofayela yomsebenzi iphunyuziwe"</string>
<string name="group_unknown_summary" msgid="6951386960814105641">"Alukho ulwazi okwamanje"</string>
diff --git a/SafetyCenter/Resources/shared_res/values/strings.xml b/SafetyCenter/Resources/shared_res/values/strings.xml
index f4f1b0201..2b231b55f 100644
--- a/SafetyCenter/Resources/shared_res/values/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values/strings.xml
@@ -94,9 +94,6 @@
<!-- An error shown to the user when we failed to resolve an alert that they attempted to resolve directly from the Safety Center screen [CHAR LIMIT=50] -->
<string name="resolving_action_error">Couldn\’t resolve alert</string>
- <!-- An error shown to the user when we failed to refresh the overall Safety Center status. This happens when at least one safety signal did not get back to Safety Center within an arbitrary timeout [CHAR LIMIT=50] -->
- <string name="refresh_timeout">Couldn\’t refresh settings</string>
-
<!-- An error shown to the user when we failed to refresh the status of one or more Safety Center entries. The number of entries is used as a parameter to format the error [CHAR LIMIT=60] -->
<string name="refresh_error">{count, plural,
=1 {Couldn\’t check setting}
diff --git a/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesApk.java b/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesApk.java
new file mode 100644
index 000000000..fe0908ea0
--- /dev/null
+++ b/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesApk.java
@@ -0,0 +1,353 @@
+/*
+ * 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.
+ */
+
+package com.android.safetycenter.resources;
+
+import static java.util.Objects.requireNonNull;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.util.Log;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.annotation.VisibleForTesting;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * A class to access Safety Center resources that need to be fetched from a dedicated APK.
+ *
+ * <p>You must check whether Safety Center is enabled or the value returned by {@link #init()} prior
+ * to interacting with this class. Failure to do so may cause an {@link IllegalStateException} if
+ * the resources APK cannot be accessed.
+ *
+ * <p>This class isn't thread safe. Thread safety must be handled by the caller, or this may cause
+ * the resources APK {@link Context} to be initialized multiple times.
+ */
+public final class SafetyCenterResourcesApk {
+
+ private static final String TAG = "SafetyCenterResApk";
+
+ /** Intent action that is used to identify the Safety Center resources APK */
+ private static final String RESOURCES_APK_ACTION =
+ "com.android.safetycenter.intent.action.SAFETY_CENTER_RESOURCES_APK";
+
+ /** Permission APEX name */
+ private static final String APEX_MODULE_NAME = "com.android.permission";
+
+ /**
+ * The path where the Permission apex is mounted. Current value = "/apex/com.android.permission"
+ */
+ private static final String APEX_MODULE_PATH =
+ new File("/apex", APEX_MODULE_NAME).getAbsolutePath();
+
+ /** Raw XML config resource name */
+ private static final String CONFIG_NAME = "safety_center_config";
+
+ private final Context mContext;
+
+ /** Intent action that is used to identify the Safety Center resources APK */
+ private final String mResourcesApkAction;
+
+ /** The path where the Safety Center resources APK is expected to be installed */
+ private final String mResourcesApkPath;
+
+ /** Specific flags used for retrieving resolve info. */
+ private final int mFlags;
+
+ /**
+ * Whether we should fallback with an empty string / null values when calling the methods of
+ * this class for a resource that does not exist.
+ */
+ private final boolean mShouldFallbackIfNamedResourceNotFound;
+
+ // Cached context from the resources APK.
+ @Nullable private Context mResourcesApkContext;
+
+ public SafetyCenterResourcesApk(Context context) {
+ this(context, /* shouldFallbackIfNamedResourceNotFound */ true);
+ }
+
+ private SafetyCenterResourcesApk(
+ Context context, boolean shouldFallbackIfNamedResourceNotFound) {
+ this(
+ context,
+ RESOURCES_APK_ACTION,
+ APEX_MODULE_PATH,
+ PackageManager.MATCH_SYSTEM_ONLY,
+ shouldFallbackIfNamedResourceNotFound);
+ }
+
+ @VisibleForTesting
+ SafetyCenterResourcesApk(
+ Context context,
+ String resourcesApkAction,
+ String resourcesApkPath,
+ int flags,
+ boolean shouldFallbackIfNamedResourceNotFound) {
+ mContext = requireNonNull(context);
+ mResourcesApkAction = requireNonNull(resourcesApkAction);
+ mResourcesApkPath = requireNonNull(resourcesApkPath);
+ mFlags = flags;
+ mShouldFallbackIfNamedResourceNotFound = shouldFallbackIfNamedResourceNotFound;
+ }
+
+ /** Creates a new {@link SafetyCenterResourcesApk} for testing. */
+ @VisibleForTesting
+ public static SafetyCenterResourcesApk forTests(Context context) {
+ return new SafetyCenterResourcesApk(
+ context, /* shouldFallbackIfNamedResourceNotFound */ false);
+ }
+
+ /**
+ * Initializes the resources APK {@link Context}, and returns whether this was successful.
+ *
+ * <p>This call is optional as this can also be lazily instantiated. It can be used to ensure
+ * that the resources APK context is loaded prior to interacting with this class. This
+ * initialization code needs to run in the same user as the provided base {@link Context}. This
+ * may not be the case with a binder call, which is why it can be more appropriate to do this
+ * explicitly.
+ */
+ public boolean init() {
+ mResourcesApkContext = loadResourcesApkContext();
+ return mResourcesApkContext != null;
+ }
+
+ /**
+ * Returns the {@link Context} of the Safety Center resources APK.
+ *
+ * <p>Throws an {@link IllegalStateException} if the resources APK is not available
+ */
+ public Context getContext() {
+ if (mResourcesApkContext != null) {
+ return mResourcesApkContext;
+ }
+
+ mResourcesApkContext = loadResourcesApkContext();
+ if (mResourcesApkContext == null) {
+ throw new IllegalStateException("Resources APK context not found");
+ }
+
+ return mResourcesApkContext;
+ }
+
+ @Nullable
+ private Context loadResourcesApkContext() {
+ List<ResolveInfo> resolveInfos =
+ mContext.getPackageManager()
+ .queryIntentActivities(new Intent(mResourcesApkAction), mFlags);
+
+ if (resolveInfos.size() > 1) {
+ // multiple apps found, log a warning, but continue
+ Log.w(TAG, "Found > 1 APK that can resolve Safety Center resources APK intent:");
+ final int resolveInfosSize = resolveInfos.size();
+ for (int i = 0; i < resolveInfosSize; i++) {
+ ResolveInfo resolveInfo = resolveInfos.get(i);
+ Log.w(
+ TAG,
+ String.format(
+ "- pkg:%s at:%s",
+ resolveInfo.activityInfo.applicationInfo.packageName,
+ resolveInfo.activityInfo.applicationInfo.sourceDir));
+ }
+ }
+
+ ResolveInfo info = null;
+ // Assume the first good ResolveInfo is the one we're looking for
+ final int resolveInfosSize = resolveInfos.size();
+ for (int i = 0; i < resolveInfosSize; i++) {
+ ResolveInfo resolveInfo = resolveInfos.get(i);
+ if (!resolveInfo.activityInfo.applicationInfo.sourceDir.startsWith(mResourcesApkPath)) {
+ // skip apps that don't live in the Permission apex
+ continue;
+ }
+ info = resolveInfo;
+ break;
+ }
+
+ if (info == null) {
+ // Resource APK not loaded yet, print a stack trace to see where this is called from
+ Log.e(TAG, "Could not find Safety Center resources APK", new IllegalStateException());
+ return null;
+ }
+
+ String resourcesApkPkgName = info.activityInfo.applicationInfo.packageName;
+ Log.i(TAG, "Found Safety Center resources APK at: " + resourcesApkPkgName);
+ return getPackageContext(resourcesApkPkgName);
+ }
+
+ @Nullable
+ private Context getPackageContext(String packageName) {
+ try {
+ return mContext.createPackageContext(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to load package context for: " + packageName, e);
+ }
+ return null;
+ }
+
+ /** Calls {@link Context#getResources()} for the resources APK {@link Context}. */
+ public Resources getResources() {
+ return getContext().getResources();
+ }
+
+ /**
+ * Returns the raw XML resource representing the Safety Center configuration file from the
+ * Safety Center resources APK.
+ */
+ @Nullable
+ public InputStream getSafetyCenterConfig() {
+ return getSafetyCenterConfig(CONFIG_NAME);
+ }
+
+ @VisibleForTesting
+ @Nullable
+ InputStream getSafetyCenterConfig(String configName) {
+ int resId = getResIdAndMaybeThrowIfNull(configName, "raw");
+ if (resId == Resources.ID_NULL) {
+ return null;
+ }
+ return getResources().openRawResource(resId);
+ }
+
+ /** Calls {@link Context#getString(int)} for the resources APK {@link Context}. */
+ public String getString(@StringRes int stringId) {
+ return getContext().getString(stringId);
+ }
+
+ /** Same as {@link #getString(int)} but with the given {@code formatArgs}. */
+ public String getString(@StringRes int stringId, Object... formatArgs) {
+ return getContext().getString(stringId, formatArgs);
+ }
+
+ /**
+ * Returns the {@link String} with the given resource name.
+ *
+ * <p>If the {@link String} cannot be accessed, returns {@code ""} or throws {@link
+ * Resources.NotFoundException} depending on {@link #mShouldFallbackIfNamedResourceNotFound}.
+ */
+ public String getStringByName(String name) {
+ int resId = getResIdAndMaybeThrowIfNull(name, "string");
+ if (resId == Resources.ID_NULL) {
+ return "";
+ }
+ return getString(resId);
+ }
+
+ /** Same as {@link #getStringByName(String)} but with the given {@code formatArgs}. */
+ public String getStringByName(String name, Object... formatArgs) {
+ int resId = getResIdAndMaybeThrowIfNull(name, "string");
+ if (resId == Resources.ID_NULL) {
+ return "";
+ }
+ return getString(resId, formatArgs);
+ }
+
+ /**
+ * Returns an optional {@link String} resource with the given {@code stringId}.
+ *
+ * <p>Returns {@code null} if {@code stringId} is equal to {@link Resources#ID_NULL}. Otherwise,
+ * throws a {@link Resources.NotFoundException}.
+ */
+ @Nullable
+ public String getOptionalString(@StringRes int stringId) {
+ if (stringId == Resources.ID_NULL) {
+ return null;
+ }
+ return getString(stringId);
+ }
+
+ /** Same as {@link #getOptionalString(int)} but with the given resource name rather than ID. */
+ @Nullable
+ public String getOptionalStringByName(String name) {
+ return getOptionalString(getResId(name, "string"));
+ }
+
+ /**
+ * Returns the {@link Drawable} with the given resource name.
+ *
+ * <p>If the {@link Drawable} cannot be accessed, returns {@code null} or throws {@link
+ * Resources.NotFoundException} depending on {@link #mShouldFallbackIfNamedResourceNotFound}.
+ *
+ * @param theme the theme used to style the drawable attributes, may be {@code null}
+ */
+ @Nullable
+ public Drawable getDrawableByName(String name, @Nullable Resources.Theme theme) {
+ int resId = getResIdAndMaybeThrowIfNull(name, "drawable");
+ if (resId == Resources.ID_NULL) {
+ return null;
+ }
+ return getResources().getDrawable(resId, theme);
+ }
+
+ /**
+ * Returns an {@link Icon} containing the {@link Drawable} with the given resource name.
+ *
+ * <p>If the {@link Drawable} cannot be accessed, returns {@code null} or throws {@link
+ * Resources.NotFoundException} depending on {@link #mShouldFallbackIfNamedResourceNotFound}.
+ */
+ @Nullable
+ public Icon getIconByDrawableName(String name) {
+ int resId = getResIdAndMaybeThrowIfNull(name, "drawable");
+ if (resId == Resources.ID_NULL) {
+ return null;
+ }
+ return Icon.createWithResource(getContext().getPackageName(), resId);
+ }
+
+ /**
+ * Returns the {@link ColorInt} with the given resource name.
+ *
+ * <p>If the {@link ColorInt} cannot be accessed, returns {@code null} or throws {@link
+ * Resources.NotFoundException} depending on {@link #mShouldFallbackIfNamedResourceNotFound}.
+ */
+ @ColorInt
+ @Nullable
+ public Integer getColorByName(String name) {
+ int resId = getResIdAndMaybeThrowIfNull(name, "color");
+ if (resId == Resources.ID_NULL) {
+ return null;
+ }
+ return getResources().getColor(resId, getContext().getTheme());
+ }
+
+ private int getResIdAndMaybeThrowIfNull(String name, String type) {
+ int resId = getResId(name, type);
+ if (resId != Resources.ID_NULL) {
+ return resId;
+ }
+ if (!mShouldFallbackIfNamedResourceNotFound) {
+ throw new Resources.NotFoundException();
+ }
+ Log.w(TAG, "Named " + type + " resource: " + name + " not found");
+ return resId;
+ }
+
+ private int getResId(String name, String type) {
+ // TODO(b/227738283): profile the performance of this operation and consider adding caching
+ // or finding some alternative solution.
+ return getResources().getIdentifier(name, type, getContext().getPackageName());
+ }
+}
diff --git a/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesContext.java b/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesContext.java
deleted file mode 100644
index 9a77296e2..000000000
--- a/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesContext.java
+++ /dev/null
@@ -1,403 +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.
- */
-
-package com.android.safetycenter.resources;
-
-import static java.util.Objects.requireNonNull;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.AssetManager;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.util.Log;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.annotation.VisibleForTesting;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.List;
-
-/**
- * Wrapper for context to override getResources method. Resources for the Safety Center that need to
- * be fetched from the dedicated resources APK.
- */
-public class SafetyCenterResourcesContext extends ContextWrapper {
- private static final String TAG = "SafetyCenterResContext";
-
- /** Intent action that is used to identify the Safety Center resources APK */
- private static final String RESOURCES_APK_ACTION =
- "com.android.safetycenter.intent.action.SAFETY_CENTER_RESOURCES_APK";
-
- /** Permission APEX name */
- private static final String APEX_MODULE_NAME = "com.android.permission";
-
- /**
- * The path where the Permission apex is mounted. Current value = "/apex/com.android.permission"
- */
- private static final String APEX_MODULE_PATH =
- new File("/apex", APEX_MODULE_NAME).getAbsolutePath();
-
- /** Raw XML config resource name */
- private static final String CONFIG_NAME = "safety_center_config";
-
- /** Intent action that is used to identify the Safety Center resources APK */
- private final String mResourcesApkAction;
-
- /** The path where the Safety Center resources APK is expected to be installed */
- @Nullable private final String mResourcesApkPath;
-
- /** Raw XML config resource name */
- private final String mConfigName;
-
- /** Specific flags used for retrieving resolve info */
- private final int mFlags;
-
- /**
- * Whether we should fallback with an empty string when calling {@link #getStringByName} for a
- * string resource that does not exist.
- */
- private final boolean mShouldFallbackIfNamedResourceNotFound;
-
- // Cached package name and resources from the resources APK
- @Nullable private String mResourcesApkPkgName;
- @Nullable private AssetManager mAssetsFromApk;
- @Nullable private Resources mResourcesFromApk;
- @Nullable private Resources.Theme mThemeFromApk;
-
- public SafetyCenterResourcesContext(Context contextBase) {
- this(contextBase, /* shouldFallbackIfNamedResourceNotFound */ true);
- }
-
- private SafetyCenterResourcesContext(
- Context contextBase, boolean shouldFallbackIfNamedResourceNotFound) {
- this(
- contextBase,
- RESOURCES_APK_ACTION,
- APEX_MODULE_PATH,
- CONFIG_NAME,
- PackageManager.MATCH_SYSTEM_ONLY,
- shouldFallbackIfNamedResourceNotFound);
- }
-
- @VisibleForTesting
- SafetyCenterResourcesContext(
- Context contextBase,
- String resourcesApkAction,
- @Nullable String resourcesApkPath,
- String configName,
- int flags,
- boolean shouldFallbackIfNamedResourceNotFound) {
- super(contextBase);
- mResourcesApkAction = requireNonNull(resourcesApkAction);
- mResourcesApkPath = resourcesApkPath;
- mConfigName = requireNonNull(configName);
- mFlags = flags;
- mShouldFallbackIfNamedResourceNotFound = shouldFallbackIfNamedResourceNotFound;
- }
-
- /** Creates a new {@link SafetyCenterResourcesContext} for testing. */
- @VisibleForTesting
- public static SafetyCenterResourcesContext forTests(Context contextBase) {
- return new SafetyCenterResourcesContext(
- contextBase, /* shouldFallbackIfNamedResourceNotFound */ false);
- }
-
- /**
- * Initializes the {@link Context}'s {@link AssetManager}, {@link Resources} and {@link
- * Resources.Theme}.
- *
- * <p>This call is optional as this can also be lazily instantiated. This is useful to ensure
- * that resources are loaded prior to interacting with the {@link SafetyCenterResourcesContext},
- * as this code needs to run for the same user as the provided base {@link Context}; which may
- * not be the case with a binder call.
- */
- public void init() {
- mAssetsFromApk = getAssets();
- mResourcesFromApk = getResources();
- mThemeFromApk = getTheme();
- }
-
- /** Get the package name of the Safety Center resources APK. */
- @VisibleForTesting
- @Nullable
- String getResourcesApkPkgName() {
- if (mResourcesApkPkgName != null) {
- return mResourcesApkPkgName;
- }
-
- List<ResolveInfo> resolveInfos =
- getPackageManager().queryIntentActivities(new Intent(mResourcesApkAction), mFlags);
-
- if (resolveInfos.size() > 1) {
- // multiple apps found, log a warning, but continue
- Log.w(TAG, "Found > 1 APK that can resolve Safety Center resources APK intent:");
- final int resolveInfosSize = resolveInfos.size();
- for (int i = 0; i < resolveInfosSize; i++) {
- ResolveInfo resolveInfo = resolveInfos.get(i);
- Log.w(
- TAG,
- String.format(
- "- pkg:%s at:%s",
- resolveInfo.activityInfo.applicationInfo.packageName,
- resolveInfo.activityInfo.applicationInfo.sourceDir));
- }
- }
-
- ResolveInfo info = null;
- // Assume the first good ResolveInfo is the one we're looking for
- final int resolveInfosSize = resolveInfos.size();
- for (int i = 0; i < resolveInfosSize; i++) {
- ResolveInfo resolveInfo = resolveInfos.get(i);
- if (mResourcesApkPath != null
- && !resolveInfo.activityInfo.applicationInfo.sourceDir.startsWith(
- mResourcesApkPath)) {
- // skip apps that don't live in the Permission apex
- continue;
- }
- info = resolveInfo;
- break;
- }
-
- if (info == null) {
- // Resource APK not loaded yet, print a stack trace to see where this is called from
- Log.e(
- TAG,
- "Attempted to fetch resources before Safety Center resources APK is loaded!",
- new IllegalStateException());
- return null;
- }
-
- mResourcesApkPkgName = info.activityInfo.applicationInfo.packageName;
- Log.i(TAG, "Found Safety Center resources APK at: " + mResourcesApkPkgName);
- return mResourcesApkPkgName;
- }
-
- /**
- * Gets the raw XML resource representing the Safety Center configuration from the Safety Center
- * resources APK.
- */
- @Nullable
- public InputStream getSafetyCenterConfig() {
- String resourcePkgName = getResourcesApkPkgName();
- if (resourcePkgName == null) {
- return null;
- }
- Resources resources = getResources();
- if (resources == null) {
- return null;
- }
- int id = resources.getIdentifier(mConfigName, "raw", resourcePkgName);
- if (id == Resources.ID_NULL) {
- return null;
- }
- return resources.openRawResource(id);
- }
-
- /**
- * Returns an optional {@link String} resource from the given {@code stringId}.
- *
- * <p>Returns {@code null} if {@code stringId} is equal to {@link Resources#ID_NULL}. Otherwise,
- * throws a {@link Resources.NotFoundException} if the resource cannot be accessed.
- */
- @Nullable
- public String getOptionalString(@StringRes int stringId) {
- if (stringId == Resources.ID_NULL) {
- return null;
- }
- return getString(stringId);
- }
-
- /** Same as {@link #getOptionalString(int)} but with the given {@code formatArgs}. */
- @Nullable
- public String getOptionalString(@StringRes int stringId, Object... formatArgs) {
- if (stringId == Resources.ID_NULL) {
- return null;
- }
- return getString(stringId, formatArgs);
- }
-
- /** Same as {@link #getOptionalString(int)} but using the string name rather than ID. */
- @Nullable
- public String getOptionalStringByName(String name) {
- return getOptionalString(getStringRes(name));
- }
-
- /**
- * Gets a string resource by name from the Safety Center resources APK, and returns an empty
- * string if the resource does not exist (or throws a {@link Resources.NotFoundException} if
- * {@link #mShouldFallbackIfNamedResourceNotFound} is {@code false}).
- */
- public String getStringByName(String name) {
- int id = getStringRes(name);
- return maybeFallbackIfNamedResourceIsNull(name, getOptionalString(id));
- }
-
- /** Same as {@link #getStringByName(String)} but with the given {@code formatArgs}. */
- public String getStringByName(String name, Object... formatArgs) {
- int id = getStringRes(name);
- return maybeFallbackIfNamedResourceIsNull(name, getOptionalString(id, formatArgs));
- }
-
- private String maybeFallbackIfNamedResourceIsNull(String name, @Nullable String value) {
- if (value != null) {
- return value;
- }
- if (!mShouldFallbackIfNamedResourceNotFound) {
- throw new Resources.NotFoundException();
- }
- Log.w(TAG, "String resource " + name + " not found");
- return "";
- }
-
- @StringRes
- private int getStringRes(String name) {
- return getResId(name, "string");
- }
-
- private int getResId(String name, String type) {
- String resourcePkgName = getResourcesApkPkgName();
- if (resourcePkgName == null) {
- return Resources.ID_NULL;
- }
- Resources resources = getResources();
- if (resources == null) {
- return Resources.ID_NULL;
- }
- // TODO(b/227738283): profile the performance of this operation and consider adding caching
- // or finding some alternative solution.
- return resources.getIdentifier(name, type, resourcePkgName);
- }
-
- @Nullable
- private Context getResourcesApkContext() {
- String name = getResourcesApkPkgName();
- if (name == null) {
- return null;
- }
- try {
- return createPackageContext(name, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Log.wtf(TAG, "Failed to load resources", e);
- }
- return null;
- }
-
- /** Retrieve assets held in the Safety Center resources APK. */
- @Override
- @Nullable
- public AssetManager getAssets() {
- if (mAssetsFromApk == null) {
- Context resourcesApkContext = getResourcesApkContext();
- if (resourcesApkContext != null) {
- mAssetsFromApk = resourcesApkContext.getAssets();
- }
- }
- return mAssetsFromApk;
- }
-
- /** Retrieve resources held in the Safety Center resources APK. */
- @Override
- @Nullable
- public Resources getResources() {
- if (mResourcesFromApk == null) {
- Context resourcesApkContext = getResourcesApkContext();
- if (resourcesApkContext != null) {
- mResourcesFromApk = resourcesApkContext.getResources();
- }
- }
- return mResourcesFromApk;
- }
-
- /** Retrieve theme held in the Safety Center resources APK. */
- @Override
- @Nullable
- public Resources.Theme getTheme() {
- if (mThemeFromApk == null) {
- Context resourcesApkContext = getResourcesApkContext();
- if (resourcesApkContext != null) {
- mThemeFromApk = resourcesApkContext.getTheme();
- }
- }
- return mThemeFromApk;
- }
-
- /**
- * Gets a drawable resource by name from the Safety Center resources APK. Returns a null
- * drawable if the resource does not exist (or throws a {@link Resources.NotFoundException} if
- * {@link #mShouldFallbackIfNamedResourceNotFound} is {@code false}).
- *
- * @param name the identifier for this drawable resource
- * @param theme the theme used to style the drawable attributes, may be {@code null}
- */
- @Nullable
- public Drawable getDrawableByName(String name, @Nullable Resources.Theme theme) {
- int resId = getResId(name, "drawable");
- if (resId != Resources.ID_NULL) {
- return getResources().getDrawable(resId, theme);
- }
-
- if (!mShouldFallbackIfNamedResourceNotFound) {
- throw new Resources.NotFoundException();
- }
-
- Log.w(TAG, "Drawable resource " + name + " not found");
- return null;
- }
-
- /**
- * Returns an {@link Icon} instance containing a drawable with the given name. If no such
- * drawable exists, returns {@code null} or throws {@link Resources.NotFoundException}.
- */
- @Nullable
- public Icon getIconByDrawableName(String drawableResName) {
- int resId = getResId(drawableResName, "drawable");
- if (resId != Resources.ID_NULL) {
- return Icon.createWithResource(getResourcesApkPkgName(), resId);
- }
-
- if (!mShouldFallbackIfNamedResourceNotFound) {
- throw new Resources.NotFoundException();
- }
-
- Log.w(TAG, "Drawable resource " + drawableResName + " not found");
- return null;
- }
-
- /** Gets a color by resource name */
- @ColorInt
- @Nullable
- public Integer getColorByName(String name) {
- int resId = getResId(name, "color");
- if (resId != Resources.ID_NULL) {
- return getResources().getColor(resId, getTheme());
- }
-
- if (!mShouldFallbackIfNamedResourceNotFound) {
- throw new Resources.NotFoundException();
- }
-
- Log.w(TAG, "Color resource " + name + " not found");
- return null;
- }
-}
diff --git a/SafetyCenter/ResourcesLib/tests/AndroidTest.xml b/SafetyCenter/ResourcesLib/tests/AndroidTest.xml
index 6dcfb0362..e15ecf22a 100644
--- a/SafetyCenter/ResourcesLib/tests/AndroidTest.xml
+++ b/SafetyCenter/ResourcesLib/tests/AndroidTest.xml
@@ -19,7 +19,7 @@
<configuration description="Runs unit tests for the Safety Center ResourcesLib library.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
- <option name="test-tag" value="SafetyCenterConfigTests" />
+ <option name="test-tag" value="SafetyCenterResourcesLibTests" />
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController" />
<option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
diff --git a/SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/res/raw/safety_center_config.txt b/SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/res/raw/safety_center_config.txt
new file mode 100644
index 000000000..3b1246497
--- /dev/null
+++ b/SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/res/raw/safety_center_config.txt
@@ -0,0 +1 @@
+TEST \ No newline at end of file
diff --git a/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesApkTest.kt b/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesApkTest.kt
new file mode 100644
index 000000000..b52595b13
--- /dev/null
+++ b/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesApkTest.kt
@@ -0,0 +1,440 @@
+/*
+ * 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.
+ */
+
+package com.android.safetycenter.resources
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.lang.IllegalStateException
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SafetyCenterResourcesApkTest {
+ private val context: Context = getApplicationContext()
+
+ @Test
+ fun init_withValidInputs_returnsTrue() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ val initialized = resourcesApk.init()
+
+ assertThat(initialized).isTrue()
+ }
+
+ @Test
+ fun init_withWrongAction_returnsFalse() {
+ val resourcesApk = newSafetyCenterResourcesApk(resourcesApkAction = "wrong")
+
+ val initialized = resourcesApk.init()
+
+ assertThat(initialized).isFalse()
+ }
+
+ @Test
+ fun init_withWrongPath_returnsFalse() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkPath = "/apex/com.android.permission")
+
+ val initialized = resourcesApk.init()
+
+ assertThat(initialized).isFalse()
+ }
+
+ @Test
+ fun init_withWrongFlags_returnsFalse() {
+ val resourcesApk = newSafetyCenterResourcesApk(flags = PackageManager.MATCH_SYSTEM_ONLY)
+
+ val initialized = resourcesApk.init()
+
+ assertThat(initialized).isFalse()
+ }
+
+ @Test
+ fun getContext_withValidInputs_returnsResourcesApkContext() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ val resourcesApkContext = resourcesApk.context
+
+ assertThat(resourcesApkContext.packageName).isEqualTo(RESOURCES_APK_PKG_NAME)
+ }
+
+ @Test
+ fun getContext_withWrongAction_throws() {
+ val resourcesApk = newSafetyCenterResourcesApk(resourcesApkAction = "wrong")
+
+ assertFailsWith(IllegalStateException::class) { resourcesApk.context }
+ }
+
+ @Test
+ fun getContext_withWrongPath_throws() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkPath = "/apex/com.android.permission")
+
+ assertFailsWith(IllegalStateException::class) { resourcesApk.context }
+ }
+
+ @Test
+ fun getContext_withWrongFlags_throws() {
+ val resourcesApk = newSafetyCenterResourcesApk(flags = PackageManager.MATCH_SYSTEM_ONLY)
+
+ assertFailsWith(IllegalStateException::class) { resourcesApk.context }
+ }
+
+ @Test
+ fun getResources_withValidInputs_returnsResourcesApkContextResources() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ val resources = resourcesApk.resources
+
+ assertThat(resources).isEqualTo(resourcesApk.context.resources)
+ }
+
+ @Test
+ fun getResources_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) { resourcesApk.resources }
+ }
+
+ @Test
+ fun getSafetyCenterConfig_withValidInputs_returnsConfigContent() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ val config = resourcesApk.safetyCenterConfig
+ val configContent = config?.bufferedReader().use { it?.readText() }
+
+ assertThat(config).isNotNull()
+ assertThat(configContent).isEqualTo(CONFIG_CONTENT)
+ }
+
+ @Test
+ fun getSafetyCenterConfig_anotherValidConfigName_returnsConfigContent() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ val config = resourcesApk.getSafetyCenterConfig(CONFIG_NAME)
+ val configContent = config?.bufferedReader().use { it?.readText() }
+
+ assertThat(config).isNotNull()
+ assertThat(configContent).isEqualTo(CONFIG_CONTENT)
+ }
+
+ @Test
+ fun getSafetyCenterConfig_invalidConfigNameWithFallback_returnsNull() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = true)
+
+ assertThat(resourcesApk.getSafetyCenterConfig("wrong")).isNull()
+ }
+
+ @Test
+ fun getSafetyCenterConfig_invalidConfigNameWithoutFallback_throws() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = false)
+
+ assertFailsWith(Resources.NotFoundException::class) {
+ resourcesApk.getSafetyCenterConfig("wrong")
+ }
+ }
+
+ @Test
+ fun getSafetyCenterConfig_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) { resourcesApk.safetyCenterConfig }
+ }
+
+ @Test
+ fun getString_validString_returnsString() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ val ok = resourcesApk.getString(android.R.string.ok)
+
+ assertThat(ok).isEqualTo("OK")
+ }
+
+ @Test
+ fun getString_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) {
+ resourcesApk.getString(android.R.string.ok)
+ }
+ }
+
+ @Test
+ fun getStringWithFormatArgs_validString_returnsString() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ val ok = resourcesApk.getString(android.R.string.ok, "")
+
+ assertThat(ok).isEqualTo("OK")
+ }
+
+ @Test
+ fun getStringWithFormatArgs_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) {
+ resourcesApk.getString(android.R.string.ok, "")
+ }
+ }
+
+ @Test
+ fun getStringByName_validString_returnsString() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ assertThat(resourcesApk.getStringByName("valid_string")).isEqualTo("I exist!")
+ }
+
+ @Test
+ fun getStringByName_invalidStringWithFallback_returnsEmptyString() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = true)
+
+ assertThat(resourcesApk.getStringByName("invalid_string")).isEqualTo("")
+ }
+
+ @Test
+ fun getStringByName_invalidStringWithoutFallback_throws() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = false)
+
+ assertFailsWith(Resources.NotFoundException::class) {
+ resourcesApk.getStringByName("invalid_string")
+ }
+ }
+
+ @Test
+ fun getStringByName_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) {
+ resourcesApk.getStringByName("valid_string")
+ }
+ }
+
+ @Test
+ fun getStringByNameWithFormatArgs_validString_returnsString() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ assertThat(resourcesApk.getStringByName("valid_string", "")).isEqualTo("I exist!")
+ }
+
+ @Test
+ fun getStringByNameWithFormatArgs_invalidStringWithFallback_returnsEmptyString() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = true)
+
+ assertThat(resourcesApk.getStringByName("invalid_string", "")).isEqualTo("")
+ }
+
+ @Test
+ fun getStringByNameWithFormatArgs_invalidStringWithoutFallback_throws() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = false)
+
+ assertFailsWith(Resources.NotFoundException::class) {
+ resourcesApk.getStringByName("invalid_string", "")
+ }
+ }
+
+ @Test
+ fun getStringByNameWithFormatArgs_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) {
+ resourcesApk.getStringByName("valid_string", "")
+ }
+ }
+
+ @Test
+ fun getOptionalString_validString_returnsString() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ val ok = resourcesApk.getOptionalString(android.R.string.ok)
+
+ assertThat(ok).isEqualTo("OK")
+ }
+
+ @Test
+ fun getOptionalString_resourceIdNull_returnsNull() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ val string = resourcesApk.getOptionalString(Resources.ID_NULL)
+
+ assertThat(string).isNull()
+ }
+
+ @Test
+ fun getOptionalString_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) {
+ resourcesApk.getOptionalString(android.R.string.ok)
+ }
+ }
+
+ @Test
+ fun getOptionalStringByName_validString_returnsString() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ assertThat(resourcesApk.getOptionalStringByName("valid_string")).isEqualTo("I exist!")
+ }
+
+ @Test
+ fun getOptionalStringByName_invalidStringWithFallback_returnsNull() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = true)
+
+ assertThat(resourcesApk.getOptionalStringByName("invalid_string")).isNull()
+ }
+
+ @Test
+ fun getOptionalStringByName_invalidStringWithoutFallback_returnsNull() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = false)
+
+ assertThat(resourcesApk.getOptionalStringByName("invalid_string")).isNull()
+ }
+
+ @Test
+ fun getOptionalStringByName_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) {
+ resourcesApk.getOptionalStringByName("valid_string")
+ }
+ }
+
+ @Test
+ fun getDrawableByName_validDrawable_returnsDrawable() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ assertThat(resourcesApk.getDrawableByName("valid_drawable", context.theme)).isNotNull()
+ }
+
+ @Test
+ fun getDrawableByName_invalidDrawableWithFallback_returnsNull() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = true)
+
+ assertThat(resourcesApk.getDrawableByName("invalid_drawable", context.theme)).isNull()
+ }
+
+ @Test
+ fun getDrawableByName_invalidDrawableWithoutFallback_throws() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = false)
+
+ assertFailsWith(Resources.NotFoundException::class) {
+ resourcesApk.getDrawableByName("invalid_drawable", context.theme)
+ }
+ }
+
+ @Test
+ fun getDrawableByName_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) {
+ resourcesApk.getDrawableByName("valid_drawable", context.theme)
+ }
+ }
+
+ @Test
+ fun getIconByDrawableName_validDrawable_returnsIcon() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ assertThat(resourcesApk.getIconByDrawableName("valid_drawable")).isNotNull()
+ }
+
+ @Test
+ fun getIconByDrawableName_invalidDrawableWithFallback_returnsNull() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = true)
+
+ assertThat(resourcesApk.getIconByDrawableName("invalid_drawable")).isNull()
+ }
+
+ @Test
+ fun getIconByDrawableName_invalidDrawableWithoutFallback_throws() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = false)
+
+ assertFailsWith(Resources.NotFoundException::class) {
+ resourcesApk.getIconByDrawableName("invalid_drawable")
+ }
+ }
+
+ @Test
+ fun getIconByDrawableByName_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) {
+ resourcesApk.getIconByDrawableName("valid_drawable")
+ }
+ }
+
+ @Test
+ fun getColorByName_validColor_returnsColor() {
+ val resourcesApk = newSafetyCenterResourcesApk()
+
+ assertThat(resourcesApk.getColorByName("valid_color")).isNotNull()
+ }
+
+ @Test
+ fun getColorByName_invalidColorWithFallback_returnsNull() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = true)
+
+ assertThat(resourcesApk.getColorByName("invalid_color")).isNull()
+ }
+
+ @Test
+ fun getColorByName_invalidColorWithoutFallback_throws() {
+ val resourcesApk = newSafetyCenterResourcesApk(fallback = false)
+
+ assertFailsWith(Resources.NotFoundException::class) {
+ resourcesApk.getColorByName("invalid_color")
+ }
+ }
+
+ @Test
+ fun getColorByName_nullContext_throwsRegardlessOfFallback() {
+ val resourcesApk =
+ newSafetyCenterResourcesApk(resourcesApkAction = "wrong", fallback = true)
+
+ assertFailsWith(IllegalStateException::class) { resourcesApk.getColorByName("valid_color") }
+ }
+
+ private fun newSafetyCenterResourcesApk(
+ resourcesApkAction: String = RESOURCES_APK_ACTION,
+ resourcesApkPath: String = "",
+ flags: Int = 0,
+ fallback: Boolean = false
+ ) = SafetyCenterResourcesApk(context, resourcesApkAction, resourcesApkPath, flags, fallback)
+
+ companion object {
+ const val RESOURCES_APK_ACTION =
+ "com.android.safetycenter.tests.intent.action.SAFETY_CENTER_TEST_RESOURCES_APK"
+ const val RESOURCES_APK_PKG_NAME =
+ "com.android.safetycenter.tests.config.safetycenterresourceslibtestresources"
+ const val CONFIG_NAME = "test"
+ const val CONFIG_CONTENT = "TEST"
+ }
+}
diff --git a/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesContextTest.kt b/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesContextTest.kt
deleted file mode 100644
index 1a82460d2..000000000
--- a/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesContextTest.kt
+++ /dev/null
@@ -1,226 +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.
- */
-
-package com.android.safetycenter.resources
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.content.res.Resources
-import androidx.test.core.app.ApplicationProvider.getApplicationContext
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.google.common.truth.Truth.assertThat
-import kotlin.test.assertFailsWith
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class SafetyCenterResourcesContextTest {
- private val context: Context = getApplicationContext()
- private val theme: Resources.Theme? = context.theme
-
- @Test
- fun validDataWithValidInputs() {
- val resourcesContext =
- SafetyCenterResourcesContext(context, RESOURCES_APK_ACTION, null, CONFIG_NAME, 0, false)
-
- assertThat(resourcesContext.resourcesApkPkgName).isEqualTo(RESOURCES_APK_PKG_NAME)
-
- val configContent =
- resourcesContext.safetyCenterConfig?.bufferedReader().use { it?.readText() }
-
- assertThat(configContent).isEqualTo(CONFIG_CONTENT)
- assertThat(resourcesContext.assets).isNotNull()
- assertThat(resourcesContext.resources).isNotNull()
- assertThat(resourcesContext.theme).isNotNull()
- }
-
- @Test
- fun nullDataWithWrongAction() {
- val resourcesContext = createNewResourcesContext(resourcesApkAction = "wrong")
-
- assertThat(resourcesContext.resourcesApkPkgName).isNull()
- assertThat(resourcesContext.safetyCenterConfig).isNull()
- assertThat(resourcesContext.assets).isNull()
- assertThat(resourcesContext.resources).isNull()
- assertThat(resourcesContext.theme).isNull()
- }
-
- @Test
- fun nullDataWithWrongPath() {
- val resourcesContext =
- createNewResourcesContext(resourcesApkPath = "/apex/com.android.permission")
-
- assertThat(resourcesContext.resourcesApkPkgName).isNull()
- assertThat(resourcesContext.safetyCenterConfig).isNull()
- assertThat(resourcesContext.assets).isNull()
- assertThat(resourcesContext.resources).isNull()
- assertThat(resourcesContext.theme).isNull()
- }
-
- @Test
- fun nullDataWithWrongFlag() {
- val resourcesContext = createNewResourcesContext(flags = PackageManager.MATCH_SYSTEM_ONLY)
-
- assertThat(resourcesContext.resourcesApkPkgName).isNull()
- assertThat(resourcesContext.safetyCenterConfig).isNull()
- assertThat(resourcesContext.assets).isNull()
- assertThat(resourcesContext.resources).isNull()
- assertThat(resourcesContext.theme).isNull()
- }
-
- @Test
- fun nullConfigWithWrongConfigName() {
- val resourcesContext = createNewResourcesContext(configName = "wrong")
-
- assertThat(resourcesContext.resourcesApkPkgName).isNotNull()
- assertThat(resourcesContext.safetyCenterConfig).isNull()
- assertThat(resourcesContext.assets).isNotNull()
- assertThat(resourcesContext.resources).isNotNull()
- assertThat(resourcesContext.theme).isNotNull()
- }
-
- @Test
- fun getStringByName_validString_returnsString() {
- val resourcesContext = createNewResourcesContext()
-
- assertThat(resourcesContext.getStringByName("valid_string")).isEqualTo("I exist!")
- }
-
- @Test
- fun getStringByName_invalidStringWithFallback_returnsEmptyString() {
- val resourcesContext = createNewResourcesContext(fallback = true)
-
- assertThat(resourcesContext.getStringByName("invalid_string")).isEqualTo("")
- }
-
- @Test
- fun getStringByName_invalidStringWithoutFallback_throws() {
- val resourcesContext = createNewResourcesContext(fallback = false)
-
- assertFailsWith(Resources.NotFoundException::class) {
- resourcesContext.getStringByName("invalid_string")
- }
- }
-
- @Test
- fun getOptionalStringByName_validString_returnsString() {
- val resourcesContext = createNewResourcesContext()
-
- assertThat(resourcesContext.getOptionalStringByName("valid_string")).isEqualTo("I exist!")
- }
-
- @Test
- fun getOptionalStringByName_invalidStringWithFallback_returnsNull() {
- val resourcesContext = createNewResourcesContext(fallback = true)
-
- assertThat(resourcesContext.getOptionalStringByName("invalid_string")).isNull()
- }
-
- @Test
- fun getOptionalStringByName_invalidStringWithoutFallback_returnsNull() {
- val resourcesContext = createNewResourcesContext(fallback = false)
-
- assertThat(resourcesContext.getOptionalStringByName("invalid_string")).isNull()
- }
-
- @Test
- fun getDrawableByName_validDrawable_returnsDrawable() {
- val resourcesContext = createNewResourcesContext()
-
- assertThat(resourcesContext.getDrawableByName("valid_drawable", theme)).isNotNull()
- }
-
- @Test
- fun getDrawableByName_invalidDrawableWithFallback_returnsNull() {
- val resourcesContext = createNewResourcesContext(fallback = true)
-
- assertThat(resourcesContext.getDrawableByName("invalid_drawable", theme)).isNull()
- }
-
- @Test
- fun getDrawableByName_invalidDrawableWithoutFallback_throws() {
- val resourcesContext = createNewResourcesContext(fallback = false)
-
- assertFailsWith(Resources.NotFoundException::class) {
- resourcesContext.getDrawableByName("invalid_drawable", theme)
- }
- }
-
- @Test
- fun getIconByDrawableName_validDrawable_returnsIcon() {
- val resourcesContext = createNewResourcesContext()
-
- assertThat(resourcesContext.getIconByDrawableName("valid_drawable")).isNotNull()
- }
-
- @Test
- fun getIconByDrawableName_invalidDrawableWithFallback_returnsNull() {
- val resourcesContext = createNewResourcesContext(fallback = true)
-
- assertThat(resourcesContext.getIconByDrawableName("invalid_drawable")).isNull()
- }
-
- @Test
- fun getIconByDrawableName_invalidDrawableWithoutFallback_throws() {
- val resourcesContext = createNewResourcesContext(fallback = false)
-
- assertFailsWith(Resources.NotFoundException::class) {
- resourcesContext.getIconByDrawableName("invalid_drawable")
- }
- }
-
- @Test
- fun getColorByName_validColor_returnsColor() {
- val resourcesContext = createNewResourcesContext()
-
- assertThat(resourcesContext.getColorByName("valid_color")).isNotNull()
- }
-
- @Test
- fun getColorByName_invalidColorWithFallback_returnsNull() {
- val resourcesContext = createNewResourcesContext(fallback = true)
-
- assertThat(resourcesContext.getColorByName("invalid_color")).isNull()
- }
-
- @Test
- fun getColorByName_invalidColorWithoutFallback_throws() {
- val resourcesContext = createNewResourcesContext(fallback = false)
-
- assertFailsWith(Resources.NotFoundException::class) {
- resourcesContext.getColorByName("invalid_color")
- }
- }
-
- private fun createNewResourcesContext(
- resourcesApkAction: String = RESOURCES_APK_ACTION,
- resourcesApkPath: String? = null,
- configName: String = CONFIG_NAME,
- flags: Int = 0,
- fallback: Boolean = false
- ) =
- SafetyCenterResourcesContext(
- context, resourcesApkAction, resourcesApkPath, configName, flags, fallback)
-
- companion object {
- const val RESOURCES_APK_ACTION =
- "com.android.safetycenter.tests.intent.action.SAFETY_CENTER_TEST_RESOURCES_APK"
- const val RESOURCES_APK_PKG_NAME =
- "com.android.safetycenter.tests.config.safetycenterresourceslibtestresources"
- const val CONFIG_NAME = "test"
- const val CONFIG_CONTENT = "TEST"
- }
-}
diff --git a/SafetyCenter/TEST_MAPPING b/SafetyCenter/TEST_MAPPING
index 5bc6abbc7..92397fe71 100644
--- a/SafetyCenter/TEST_MAPPING
+++ b/SafetyCenter/TEST_MAPPING
@@ -10,7 +10,13 @@
"path": "packages/modules/Permission/tests/functional/safetycenter/safetycenteractivity/"
},
{
+ "path": "packages/modules/Permission/tests/functional/safetycenter/subpages/"
+ },
+ {
"path": "packages/modules/Permission/tests/functional/safetycenter/singleuser/"
+ },
+ {
+ "path": "packages/modules/Permission/tests/hostside/safetycenter/"
}
]
}
diff --git a/framework-s/Android.bp b/framework-s/Android.bp
index e017a7ea5..9cf1e97e0 100644
--- a/framework-s/Android.bp
+++ b/framework-s/Android.bp
@@ -44,6 +44,7 @@ java_library {
name: "framework-permission-s-shared",
srcs: [":framework-permission-s-shared-srcs"],
libs: [
+ "error_prone_annotations",
"framework-annotations-lib",
"unsupportedappusage",
],
diff --git a/framework-s/java/android/app/role/RoleManager.java b/framework-s/java/android/app/role/RoleManager.java
index de697f801..5b927084d 100644
--- a/framework-s/java/android/app/role/RoleManager.java
+++ b/framework-s/java/android/app/role/RoleManager.java
@@ -527,8 +527,8 @@ public final class RoleManager {
public void setDefaultApplication(@NonNull String roleName, @Nullable String packageName,
@ManageHoldersFlags int flags, @CallbackExecutor @NonNull Executor executor,
@NonNull Consumer<Boolean> callback) {
+ // Prior to Android V some devices might require the "packageName" to be non-null.
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
Objects.requireNonNull(executor, "executor cannot be null");
Objects.requireNonNull(callback, "callback cannot be null");
try {
diff --git a/framework-s/java/android/safetycenter/SafetyCenter.md b/framework-s/java/android/safetycenter/SafetyCenter.md
index 9d3eb811f..ded7809a5 100644
--- a/framework-s/java/android/safetycenter/SafetyCenter.md
+++ b/framework-s/java/android/safetycenter/SafetyCenter.md
@@ -16,4 +16,5 @@
# Android safety for system developers
-TODO(b/203400159): Documentation
+See the [Android Preview docs](https://preview.source.android.com/docs/security/safety-center) for
+more information about Safety Center.
diff --git a/framework-s/java/android/safetycenter/SafetyCenterData.java b/framework-s/java/android/safetycenter/SafetyCenterData.java
index 720fb4997..7cae061a6 100644
--- a/framework-s/java/android/safetycenter/SafetyCenterData.java
+++ b/framework-s/java/android/safetycenter/SafetyCenterData.java
@@ -176,7 +176,8 @@ public final class SafetyCenterData implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public List<SafetyCenterIssue> getDismissedIssues() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mDismissedIssues;
}
@@ -191,7 +192,8 @@ public final class SafetyCenterData implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Bundle getExtras() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mExtras;
}
@@ -371,7 +373,8 @@ public final class SafetyCenterData implements Parcelable {
public Builder(@NonNull SafetyCenterStatus status) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mStatus = requireNonNull(status);
}
@@ -379,7 +382,8 @@ public final class SafetyCenterData implements Parcelable {
/** Creates a {@link Builder} with the values from the given {@link SafetyCenterData}. */
public Builder(@NonNull SafetyCenterData safetyCenterData) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(safetyCenterData);
mStatus = safetyCenterData.mStatus;
diff --git a/framework-s/java/android/safetycenter/SafetyCenterIssue.java b/framework-s/java/android/safetycenter/SafetyCenterIssue.java
index cee872a3a..f10b56b83 100644
--- a/framework-s/java/android/safetycenter/SafetyCenterIssue.java
+++ b/framework-s/java/android/safetycenter/SafetyCenterIssue.java
@@ -190,7 +190,7 @@ public final class SafetyCenterIssue implements Parcelable {
public CharSequence getAttributionTitle() {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
- "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mAttributionTitle;
}
@@ -237,7 +237,7 @@ public final class SafetyCenterIssue implements Parcelable {
public String getGroupId() {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
- "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mGroupId;
}
@@ -404,7 +404,7 @@ public final class SafetyCenterIssue implements Parcelable {
public Builder setAttributionTitle(@Nullable CharSequence attributionTitle) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
- "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mAttributionTitle = attributionTitle;
return this;
@@ -461,7 +461,7 @@ public final class SafetyCenterIssue implements Parcelable {
public Builder setGroupId(@Nullable String groupId) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
- "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mGroupId = groupId;
return this;
@@ -599,7 +599,8 @@ public final class SafetyCenterIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public ConfirmationDialogDetails getConfirmationDialogDetails() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mConfirmationDialogDetails;
}
@@ -811,7 +812,8 @@ public final class SafetyCenterIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull Action action) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(action);
mId = action.mId;
@@ -890,7 +892,8 @@ public final class SafetyCenterIssue implements Parcelable {
public Builder setConfirmationDialogDetails(
@Nullable ConfirmationDialogDetails confirmationDialogDetails) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mConfirmationDialogDetails = confirmationDialogDetails;
return this;
diff --git a/framework-s/java/android/safetycenter/SafetyCenterManager.java b/framework-s/java/android/safetycenter/SafetyCenterManager.java
index bb67f578f..35d09cce3 100644
--- a/framework-s/java/android/safetycenter/SafetyCenterManager.java
+++ b/framework-s/java/android/safetycenter/SafetyCenterManager.java
@@ -471,7 +471,7 @@ public final class SafetyCenterManager {
@RefreshReason int refreshReason, @NonNull List<String> safetySourceIds) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
- "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(safetySourceIds, "safetySourceIds cannot be null");
diff --git a/framework-s/java/android/safetycenter/SafetyEvent.java b/framework-s/java/android/safetycenter/SafetyEvent.java
index 72e8defaa..2a0d07032 100644
--- a/framework-s/java/android/safetycenter/SafetyEvent.java
+++ b/framework-s/java/android/safetycenter/SafetyEvent.java
@@ -240,7 +240,8 @@ public final class SafetyEvent implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull SafetyEvent safetyEvent) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(safetyEvent);
mType = safetyEvent.mType;
diff --git a/framework-s/java/android/safetycenter/SafetySourceData.java b/framework-s/java/android/safetycenter/SafetySourceData.java
index 2e80621a2..04846dd75 100644
--- a/framework-s/java/android/safetycenter/SafetySourceData.java
+++ b/framework-s/java/android/safetycenter/SafetySourceData.java
@@ -216,7 +216,8 @@ public final class SafetySourceData implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Bundle getExtras() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mExtras;
}
@@ -274,7 +275,8 @@ public final class SafetySourceData implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull SafetySourceData safetySourceData) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(safetySourceData);
mIssues.addAll(safetySourceData.mIssues);
@@ -299,13 +301,14 @@ public final class SafetySourceData implements Parcelable {
/**
* Sets additional information for the {@link SafetySourceData}.
*
- * If not set, the default value is {@link Bundle#EMPTY}.
+ * <p>If not set, the default value is {@link Bundle#EMPTY}.
*/
@NonNull
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setExtras(@NonNull Bundle extras) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mExtras = requireNonNull(extras);
return this;
@@ -319,7 +322,8 @@ public final class SafetySourceData implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder clearExtras() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mExtras = Bundle.EMPTY;
return this;
diff --git a/framework-s/java/android/safetycenter/SafetySourceIssue.java b/framework-s/java/android/safetycenter/SafetySourceIssue.java
index 985131764..b6e291fe3 100644
--- a/framework-s/java/android/safetycenter/SafetySourceIssue.java
+++ b/framework-s/java/android/safetycenter/SafetySourceIssue.java
@@ -337,7 +337,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public CharSequence getAttributionTitle() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mAttributionTitle;
}
@@ -418,7 +419,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Notification getCustomNotification() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mCustomNotification;
}
@@ -449,7 +451,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public int getNotificationBehavior() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mNotificationBehavior;
}
@@ -482,7 +485,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public String getDeduplicationId() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mDeduplicationId;
}
@@ -505,7 +509,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public int getIssueActionability() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mIssueActionability;
}
@@ -739,7 +744,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public ConfirmationDialogDetails getConfirmationDialogDetails() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mConfirmationDialogDetails;
}
@@ -938,7 +944,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull Action action) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(action);
mId = action.mId;
@@ -949,6 +956,8 @@ public final class SafetySourceIssue implements Parcelable {
mConfirmationDialogDetails = action.mConfirmationDialogDetails;
}
+ // TODO(b/303443020): Add setters for id, label, and pendingIntent
+
/**
* Sets whether the action will resolve the safety issue. Defaults to {@code false}.
*
@@ -984,7 +993,8 @@ public final class SafetySourceIssue implements Parcelable {
public Builder setConfirmationDialogDetails(
@Nullable ConfirmationDialogDetails confirmationDialogDetails) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mConfirmationDialogDetails = confirmationDialogDetails;
return this;
@@ -1229,7 +1239,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull SafetySourceIssue safetySourceIssue) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(safetySourceIssue);
mId = safetySourceIssue.mId;
@@ -1266,7 +1277,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public Builder setAttributionTitle(@Nullable CharSequence attributionTitle) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mAttributionTitle = attributionTitle;
return this;
@@ -1335,7 +1347,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setCustomNotification(@Nullable Notification customNotification) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mCustomNotification = customNotification;
return this;
@@ -1355,7 +1368,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setNotificationBehavior(@NotificationBehavior int notificationBehavior) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mNotificationBehavior = validateNotificationBehavior(notificationBehavior);
return this;
@@ -1370,7 +1384,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setDeduplicationId(@Nullable String deduplicationId) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mDeduplicationId = deduplicationId;
return this;
@@ -1388,7 +1403,8 @@ public final class SafetySourceIssue implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setIssueActionability(@IssueActionability int issueActionability) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mIssueActionability = validateIssueActionability(issueActionability);
return this;
diff --git a/framework-s/java/android/safetycenter/SafetySourceStatus.java b/framework-s/java/android/safetycenter/SafetySourceStatus.java
index 37095eb59..d8900f8d9 100644
--- a/framework-s/java/android/safetycenter/SafetySourceStatus.java
+++ b/framework-s/java/android/safetycenter/SafetySourceStatus.java
@@ -348,7 +348,8 @@ public final class SafetySourceStatus implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull SafetySourceStatus safetySourceStatus) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(safetySourceStatus);
mTitle = safetySourceStatus.mTitle;
diff --git a/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java
index c94cedbd6..f6c6c2156 100644
--- a/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java
+++ b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java
@@ -122,7 +122,8 @@ public final class SafetyCenterConfig implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull SafetyCenterConfig safetyCenterConfig) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(safetyCenterConfig);
mSafetySourcesGroups.addAll(safetyCenterConfig.mSafetySourcesGroups);
diff --git a/framework-s/java/android/safetycenter/config/SafetySource.java b/framework-s/java/android/safetycenter/config/SafetySource.java
index fddb8b622..8aa897850 100644
--- a/framework-s/java/android/safetycenter/config/SafetySource.java
+++ b/framework-s/java/android/safetycenter/config/SafetySource.java
@@ -297,7 +297,8 @@ public final class SafetySource implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public String getOptionalPackageName() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mPackageName;
}
@@ -488,7 +489,8 @@ public final class SafetySource implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public boolean areNotificationsAllowed() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mNotificationsAllowed;
}
@@ -503,7 +505,8 @@ public final class SafetySource implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public String getDeduplicationGroup() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mDeduplicationGroup;
}
@@ -525,7 +528,8 @@ public final class SafetySource implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Set<String> getPackageCertificateHashes() {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mPackageCertificateHashes;
}
@@ -668,7 +672,8 @@ public final class SafetySource implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull SafetySource safetySource) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(safetySource);
mType = safetySource.mType;
@@ -884,7 +889,8 @@ public final class SafetySource implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setNotificationsAllowed(boolean notificationsAllowed) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mNotificationsAllowed = notificationsAllowed;
return this;
@@ -903,7 +909,8 @@ public final class SafetySource implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setDeduplicationGroup(@Nullable String deduplicationGroup) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mDeduplicationGroup = deduplicationGroup;
return this;
@@ -919,7 +926,8 @@ public final class SafetySource implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder addPackageCertificateHash(@NonNull String packageCertificateHash) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mPackageCertificateHashes.add(packageCertificateHash);
return this;
diff --git a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java
index 1bbe25bbb..9f9ff96cd 100644
--- a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java
+++ b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java
@@ -314,7 +314,8 @@ public final class SafetySourcesGroup implements Parcelable {
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull SafetySourcesGroup original) {
if (!SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException(
+ "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
requireNonNull(original);
mSafetySources.addAll(original.mSafetySources);
diff --git a/ktfmt_includes.txt b/ktfmt_includes.txt
index 40d8f07d9..5917fc714 100644
--- a/ktfmt_includes.txt
+++ b/ktfmt_includes.txt
@@ -1,2 +1,3 @@
+SafetyCenter
++PermissionController
+tests
diff --git a/permissions/Android.bp b/permissions/Android.bp
index 8c7dab40b..6c1fdb8f8 100644
--- a/permissions/Android.bp
+++ b/permissions/Android.bp
@@ -19,9 +19,7 @@ package {
default_visibility: ["//packages/modules/Permission:__subpackages__"],
}
-prebuilt_etc {
+filegroup {
name: "privapp_allowlist_com.android.permissioncontroller.xml",
- sub_dir: "permissions",
- src: "com.android.permissioncontroller.xml",
- installable: false,
+ srcs: ["com.android.permissioncontroller.xml"],
}
diff --git a/service/Android.bp b/service/Android.bp
index 96b8fbb96..6d2cf9b8c 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -154,6 +154,7 @@ java_library {
":statslog-service-permission-java-gen",
],
libs: [
+ "androidx.annotation_annotation",
"framework-statsd.stubs.module_lib",
],
apex_available: [
diff --git a/service/java/com/android/safetycenter/ApiLock.java b/service/java/com/android/safetycenter/ApiLock.java
index 91466d3d5..f80e2ea32 100644
--- a/service/java/com/android/safetycenter/ApiLock.java
+++ b/service/java/com/android/safetycenter/ApiLock.java
@@ -16,10 +16,6 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
-import androidx.annotation.RequiresApi;
-
/**
* A class that is used to strongly type the {@link Object} used to synchronize the Safety Center
* APIs.
@@ -29,7 +25,6 @@ import androidx.annotation.RequiresApi;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class ApiLock {
ApiLock() {}
}
diff --git a/service/java/com/android/safetycenter/DevicePolicyResources.java b/service/java/com/android/safetycenter/DevicePolicyResources.java
index 25cab343f..8d31c254b 100644
--- a/service/java/com/android/safetycenter/DevicePolicyResources.java
+++ b/service/java/com/android/safetycenter/DevicePolicyResources.java
@@ -16,8 +16,6 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static java.util.Objects.requireNonNull;
import android.annotation.StringRes;
@@ -26,14 +24,11 @@ import android.app.admin.DevicePolicyResourcesManager;
import android.content.Context;
import android.os.Binder;
-import androidx.annotation.RequiresApi;
-
-import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import java.util.function.Supplier;
/** A class that handles dynamically updating enterprise-related resources. */
-@RequiresApi(TIRAMISU)
final class DevicePolicyResources {
private static final String SAFETY_CENTER_PREFIX = "SafetyCenter.";
@@ -46,31 +41,30 @@ final class DevicePolicyResources {
* DevicePolicyResourcesManager#getString}.
*/
static String getSafetySourceWorkString(
- SafetyCenterResourcesContext safetyCenterResourcesContext,
+ SafetyCenterResourcesApk safetyCenterResourcesApk,
String safetySourceId,
@StringRes int workResId) {
return getEnterpriseString(
- safetyCenterResourcesContext,
+ safetyCenterResourcesApk.getContext(),
safetySourceId,
- () -> safetyCenterResourcesContext.getString(workResId));
+ () -> safetyCenterResourcesApk.getString(workResId));
}
/**
* Returns the updated string for the {@code work_profile_paused} string by calling {@link
* DevicePolicyResourcesManager#getString}.
*/
- static String getWorkProfilePausedString(
- SafetyCenterResourcesContext safetyCenterResourcesContext) {
+ static String getWorkProfilePausedString(SafetyCenterResourcesApk safetyCenterResourcesApk) {
return getEnterpriseString(
- safetyCenterResourcesContext,
+ safetyCenterResourcesApk.getContext(),
WORK_PROFILE_PAUSED_TITLE,
- () -> safetyCenterResourcesContext.getStringByName("work_profile_paused"));
+ () -> safetyCenterResourcesApk.getStringByName("work_profile_paused"));
}
private static String getEnterpriseString(
Context context, String devicePolicyIdentifier, Supplier<String> defaultValueLoader) {
// This call requires the caller’s identity to match the package name of the given context.
- // However, the SafetyCenterResourcesContext’s has package name "android", which does not
+ // However, the SafetyCenterResourceApk Context's has package name "android", which does not
// necessarily match the caller’s package when making Binder calls, so the calling identity
// has to be cleared.
final long callingId = Binder.clearCallingIdentity();
diff --git a/service/java/com/android/safetycenter/PendingIntentFactory.java b/service/java/com/android/safetycenter/PendingIntentFactory.java
index 8c447c477..c4e9decd2 100644
--- a/service/java/com/android/safetycenter/PendingIntentFactory.java
+++ b/service/java/com/android/safetycenter/PendingIntentFactory.java
@@ -16,15 +16,13 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static java.util.Objects.requireNonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.content.pm.ResolveInfo;
@@ -32,9 +30,9 @@ import android.os.Binder;
import android.os.UserHandle;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
-import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import java.util.Arrays;
@@ -43,7 +41,6 @@ import java.util.Arrays;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class PendingIntentFactory {
private static final String TAG = "PendingIntentFactory";
@@ -53,21 +50,20 @@ public final class PendingIntentFactory {
private static final String IS_SETTINGS_HOMEPAGE = "is_from_settings_homepage";
private final Context mContext;
- private final SafetyCenterResourcesContext mSafetyCenterResourcesContext;
+ private final SafetyCenterResourcesApk mSafetyCenterResourcesApk;
- PendingIntentFactory(
- Context context, SafetyCenterResourcesContext safetyCenterResourcesContext) {
+ PendingIntentFactory(Context context, SafetyCenterResourcesApk safetyCenterResourcesApk) {
mContext = context;
- mSafetyCenterResourcesContext = safetyCenterResourcesContext;
+ mSafetyCenterResourcesApk = safetyCenterResourcesApk;
}
/**
* Creates or retrieves a {@link PendingIntent} that will start a new {@code Activity} matching
* the given {@code intentAction}.
*
- * <p>If the given {@code intentAction} resolves for the given {@code packageName}, the {@link
- * PendingIntent} will explicitly target the {@code packageName}. If the {@code intentAction}
- * resolves elsewhere, the {@link PendingIntent} will be implicit.
+ * <p>If the given {@code intentAction} resolves, the {@link PendingIntent} will use an implicit
+ * {@link Intent}. Otherwise, the {@link PendingIntent} will explicitly target the {@code
+ * packageName} if it resolves.
*
* <p>The {@code PendingIntent} is associated with a specific source given by {@code sourceId}.
*
@@ -75,7 +71,7 @@ public final class PendingIntentFactory {
* is no valid target for the given {@code intentAction}.
*/
@Nullable
- PendingIntent getPendingIntent(
+ public PendingIntent getPendingIntent(
String sourceId,
@Nullable String intentAction,
String packageName,
@@ -118,26 +114,25 @@ public final class PendingIntentFactory {
intent.setIdentifier("with_settings_homepage_extra");
}
+ if (intentResolvesToActivity(packageContext, intent)) {
+ return intent;
+ }
+
// If the intent resolves for the package provided, then we make the assumption that it is
// the desired app and make the intent explicit. This is to workaround implicit internal
// intents that may not be exported which will stop working on Android U+.
- // This assumes that the source or the caller has the highest priority to resolve the intent
- // action.
Intent explicitIntent = new Intent(intent).setPackage(packageContext.getPackageName());
- if (intentResolves(packageContext, explicitIntent)) {
+ if (intentResolvesToActivity(packageContext, explicitIntent)) {
return explicitIntent;
}
- if (intentResolves(packageContext, intent)) {
- return intent;
- }
-
// resolveActivity does not return any activity when the work profile is in quiet mode, even
// though it opens the quiet mode dialog and/or the original intent would otherwise resolve
// when quiet mode is turned off. So, we assume that the explicit intent will always resolve
// to this dialog. This heuristic is preferable on U+ as it has a higher chance of resolving
// once the work profile is enabled considering the implicit internal intent restriction.
if (isQuietModeEnabled) {
+ // TODO(b/266538628): Find a way to fix this, this heuristic isn't ideal.
return explicitIntent;
}
@@ -146,14 +141,26 @@ public final class PendingIntentFactory {
private boolean shouldAddSettingsHomepageExtra(String sourceId) {
return Arrays.asList(
- mSafetyCenterResourcesContext
+ mSafetyCenterResourcesApk
.getStringByName("config_useSettingsHomepageIntentExtra")
.split(","))
.contains(sourceId);
}
- private static boolean intentResolves(Context packageContext, Intent intent) {
- return resolveActivity(packageContext, intent) != null;
+ private static boolean intentResolvesToActivity(Context packageContext, Intent intent) {
+ ResolveInfo resolveInfo = resolveActivity(packageContext, intent);
+ if (resolveInfo == null) {
+ return false;
+ }
+ ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ return false;
+ }
+ boolean intentIsImplicit = intent.getPackage() == null && intent.getComponent() == null;
+ if (intentIsImplicit) {
+ return activityInfo.exported;
+ }
+ return true;
}
@Nullable
@@ -235,7 +242,8 @@ public final class PendingIntentFactory {
// This call requires the INTERACT_ACROSS_USERS permission.
final long callingId = Binder.clearCallingIdentity();
try {
- return context.createPackageContextAsUser(packageName, 0, UserHandle.of(userId));
+ return context.createPackageContextAsUser(
+ packageName, /* flags= */ 0, UserHandle.of(userId));
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Package name " + packageName + " not found", e);
return null;
diff --git a/service/java/com/android/safetycenter/RefreshReasons.java b/service/java/com/android/safetycenter/RefreshReasons.java
index ee318c7fd..f62d4cddb 100644
--- a/service/java/com/android/safetycenter/RefreshReasons.java
+++ b/service/java/com/android/safetycenter/RefreshReasons.java
@@ -16,7 +16,6 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA;
import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA;
@@ -33,12 +32,7 @@ import android.safetycenter.SafetyCenterManager.RefreshReason;
import android.safetycenter.SafetyCenterManager.RefreshRequestType;
import android.util.Log;
-import androidx.annotation.RequiresApi;
-
-import com.android.modules.utils.build.SdkLevel;
-
/** Helpers to do with {@link RefreshReason}. */
-@RequiresApi(TIRAMISU)
final class RefreshReasons {
private static final String TAG = "RefreshReasons";
@@ -49,6 +43,7 @@ final class RefreshReasons {
* Validates the given {@link RefreshReason}, and throws an {@link IllegalArgumentException} in
* case of unexpected value.
*/
+ @TargetApi(UPSIDE_DOWN_CAKE)
static void validate(@RefreshReason int refreshReason) {
switch (refreshReason) {
case REFRESH_REASON_RESCAN_BUTTON_CLICK:
@@ -57,11 +52,9 @@ final class RefreshReasons {
case REFRESH_REASON_DEVICE_LOCALE_CHANGE:
case REFRESH_REASON_SAFETY_CENTER_ENABLED:
case REFRESH_REASON_OTHER:
+ case REFRESH_REASON_PERIODIC:
return;
}
- if (SdkLevel.isAtLeastU() && refreshReason == REFRESH_REASON_PERIODIC) {
- return;
- }
throw new IllegalArgumentException("Unexpected refresh reason: " + refreshReason);
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
index e8e6befe5..a36beb2d3 100644
--- a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
+++ b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
@@ -18,8 +18,8 @@ package com.android.safetycenter;
import static android.Manifest.permission.READ_SAFETY_CENTER_STATUS;
import static android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE;
+import static android.content.Intent.FLAG_INCLUDE_STOPPED_PACKAGES;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.os.PowerExemptionManager.REASON_REFRESH_SAFETY_SOURCES;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
@@ -32,8 +32,6 @@ import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_SAFETY_CEN
import static java.util.Collections.unmodifiableList;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.BroadcastOptions;
import android.content.Context;
@@ -42,13 +40,12 @@ import android.os.Binder;
import android.os.UserHandle;
import android.safetycenter.SafetyCenterManager;
import android.safetycenter.SafetyCenterManager.RefreshReason;
-import android.safetycenter.SafetyCenterManager.RefreshRequestType;
import android.safetycenter.SafetySourceData;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.permission.util.PackageUtils;
import com.android.safetycenter.SafetyCenterConfigReader.Broadcast;
@@ -67,7 +64,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* <p>This class isn't thread safe. Thread safety must be handled by the caller.
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetyCenterBroadcastDispatcher {
private static final String TAG = "SafetyCenterBroadcastDi";
@@ -139,7 +135,6 @@ final class SafetyCenterBroadcastDispatcher {
String broadcastId,
@Nullable List<String> requiredSourceIds) {
boolean hasSentAtLeastOneBroadcast = false;
- int requestType = RefreshReasons.toRefreshRequestType(refreshReason);
String packageName = broadcast.getPackageName();
Set<String> deniedSourceIds = getRefreshDeniedSourceIds(refreshReason);
SparseArray<List<String>> userIdsToSourceIds =
@@ -163,7 +158,7 @@ final class SafetyCenterBroadcastDispatcher {
continue;
}
- Intent intent = createRefreshIntent(requestType, packageName, sourceIds, broadcastId);
+ Intent intent = createRefreshIntent(refreshReason, packageName, sourceIds, broadcastId);
boolean broadcastWasSent =
sendBroadcastIfResolves(intent, UserHandle.of(userId), broadcastOptions);
if (broadcastWasSent) {
@@ -198,7 +193,11 @@ final class SafetyCenterBroadcastDispatcher {
}
Intent implicitIntent = createImplicitEnabledChangedIntent();
- sendBroadcast(implicitIntent, UserHandle.SYSTEM, READ_SAFETY_CENTER_STATUS, null);
+ sendBroadcast(
+ implicitIntent,
+ UserHandle.SYSTEM,
+ READ_SAFETY_CENTER_STATUS,
+ /* broadcastOptions= */ null);
}
private void sendEnabledChangedBroadcast(
@@ -206,15 +205,17 @@ final class SafetyCenterBroadcastDispatcher {
BroadcastOptions broadcastOptions,
List<UserProfileGroup> userProfileGroups) {
Intent intent = createExplicitEnabledChangedIntent(broadcast.getPackageName());
- // The same ENABLED reason is used here for both enable and disable events. It is not sent
- // externally and is only used internally to filter safety sources in the methods of the
- // Broadcast class.
- int refreshReason = REFRESH_REASON_SAFETY_CENTER_ENABLED;
for (int i = 0; i < userProfileGroups.size(); i++) {
UserProfileGroup userProfileGroup = userProfileGroups.get(i);
SparseArray<List<String>> userIdsToSourceIds =
- getUserIdsToSourceIds(broadcast, userProfileGroup, refreshReason);
+ getUserIdsToSourceIds(
+ broadcast,
+ userProfileGroup,
+ // The same ENABLED reason is used here for both enable and disable
+ // events. It is not sent externally and is only used internally to
+ // filter safety sources in the methods of the Broadcast class.
+ REFRESH_REASON_SAFETY_CENTER_ENABLED);
for (int j = 0; j < userIdsToSourceIds.size(); j++) {
int userId = userIdsToSourceIds.keyAt(j);
@@ -229,24 +230,22 @@ final class SafetyCenterBroadcastDispatcher {
if (!doesBroadcastResolve(intent, userHandle)) {
Log.w(
TAG,
- "No receiver for intent targeting "
+ "No receiver for intent targeting: "
+ intent.getPackage()
- + " and user "
- + userHandle);
+ + ", and user id: "
+ + userHandle.getIdentifier());
return false;
}
Log.v(
TAG,
- "Found receiver for intent targeting "
+ "Found receiver for intent targeting: "
+ intent.getPackage()
- + " and user "
- + userHandle);
+ + ", and user id: "
+ + userHandle.getIdentifier());
sendBroadcast(intent, userHandle, SEND_SAFETY_CENTER_UPDATE, broadcastOptions);
return true;
}
- // TODO(b/193460475): Remove when tooling supports SystemApi to public API.
- @SuppressLint("NewApi")
private void sendBroadcast(
Intent intent,
UserHandle userHandle,
@@ -267,7 +266,7 @@ final class SafetyCenterBroadcastDispatcher {
private boolean doesBroadcastResolve(Intent broadcastIntent, UserHandle userHandle) {
return !PackageUtils.queryUnfilteredBroadcastReceiversAsUser(
- broadcastIntent, 0, userHandle.getIdentifier(), mContext)
+ broadcastIntent, /* flags= */ 0, userHandle.getIdentifier(), mContext)
.isEmpty();
}
@@ -280,24 +279,29 @@ final class SafetyCenterBroadcastDispatcher {
}
private static Intent createRefreshIntent(
- @RefreshRequestType int requestType,
+ @RefreshReason int refreshReason,
String packageName,
List<String> sourceIdsToRefresh,
String broadcastId) {
String[] sourceIdsArray = sourceIdsToRefresh.toArray(new String[0]);
- return createBroadcastIntent(ACTION_REFRESH_SAFETY_SOURCES)
- .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE, requestType)
- .putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, sourceIdsArray)
- .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, broadcastId)
- .setPackage(packageName);
+ int requestType = RefreshReasons.toRefreshRequestType(refreshReason);
+ Intent refreshIntent =
+ createBroadcastIntent(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE, requestType)
+ .putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, sourceIdsArray)
+ .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, broadcastId)
+ .setPackage(packageName);
+ boolean isUserInitiated = !RefreshReasons.isBackgroundRefresh(refreshReason);
+ if (isUserInitiated) {
+ return refreshIntent.addFlags(FLAG_INCLUDE_STOPPED_PACKAGES);
+ }
+ return refreshIntent;
}
private static Intent createBroadcastIntent(String intentAction) {
- return new Intent(intentAction).setFlags(FLAG_RECEIVER_FOREGROUND);
+ return new Intent(intentAction).addFlags(FLAG_RECEIVER_FOREGROUND);
}
- // TODO(b/193460475): Remove when tooling supports SystemApi to public API.
- @SuppressLint("NewApi")
private static BroadcastOptions createBroadcastOptions() {
BroadcastOptions broadcastOptions = BroadcastOptions.makeBasic();
Duration allowListDuration = SafetyCenterFlags.getFgsAllowlistDuration();
diff --git a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
index 92959a47d..e3e39cc9a 100644
--- a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
+++ b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
@@ -16,12 +16,9 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static java.util.Collections.unmodifiableList;
import static java.util.Objects.requireNonNull;
-import android.annotation.Nullable;
import android.content.res.Resources;
import android.safetycenter.config.SafetyCenterConfig;
import android.safetycenter.config.SafetySource;
@@ -29,11 +26,11 @@ import android.safetycenter.config.SafetySourcesGroup;
import android.util.ArrayMap;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.safetycenter.config.ParseException;
import com.android.safetycenter.config.SafetyCenterConfigParser;
-import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import java.io.InputStream;
import java.io.PrintWriter;
@@ -50,21 +47,20 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterConfigReader {
private static final String TAG = "SafetyCenterConfigReade";
- private final SafetyCenterResourcesContext mSafetyCenterResourcesContext;
+ private final SafetyCenterResourcesApk mSafetyCenterResourcesApk;
@Nullable private SafetyCenterConfigInternal mConfigInternalFromXml;
@Nullable private SafetyCenterConfigInternal mConfigInternalOverrideForTests;
- /** Creates a {@link SafetyCenterConfigReader} from a {@link SafetyCenterResourcesContext}. */
- SafetyCenterConfigReader(SafetyCenterResourcesContext safetyCenterResourcesContext) {
- mSafetyCenterResourcesContext = safetyCenterResourcesContext;
+ /** Creates a {@link SafetyCenterConfigReader} from a {@link SafetyCenterResourcesApk}. */
+ SafetyCenterConfigReader(SafetyCenterResourcesApk safetyCenterResourcesApk) {
+ mSafetyCenterResourcesApk = safetyCenterResourcesApk;
}
/**
@@ -76,7 +72,7 @@ public final class SafetyCenterConfigReader {
* this method was {@code true}.
*/
boolean loadConfig() {
- SafetyCenterConfig safetyCenterConfig = readSafetyCenterConfig();
+ SafetyCenterConfig safetyCenterConfig = loadSafetyCenterConfig();
if (safetyCenterConfig == null) {
return false;
}
@@ -114,7 +110,7 @@ public final class SafetyCenterConfigReader {
/**
* Returns the groups of {@link SafetySource}, filtering out any sources where {@link
- * SafetySources#isLoggable(SafetySource)} is false (and any resultingly empty groups).
+ * SafetySources#isLoggable(SafetySource)} is {@code false} (and any resulting empty groups).
*/
public List<SafetySourcesGroup> getLoggableSafetySourcesGroups() {
return getCurrentConfigInternal().getLoggableSourcesGroups();
@@ -137,15 +133,15 @@ public final class SafetyCenterConfigReader {
@Nullable
public ExternalSafetySource getExternalSafetySource(
String safetySourceId, String callingPackageName) {
- SafetyCenterConfigInternal currentConfig = getCurrentConfigInternal();
+ SafetyCenterConfigInternal testConfig = mConfigInternalOverrideForTests;
SafetyCenterConfigInternal xmlConfig = requireNonNull(mConfigInternalFromXml);
- if (currentConfig == xmlConfig) {
+ if (testConfig == null) {
// No override, access source directly.
- return currentConfig.getExternalSafetySources().get(safetySourceId);
+ return xmlConfig.getExternalSafetySources().get(safetySourceId);
}
ExternalSafetySource externalSafetySourceInTestConfig =
- currentConfig.getExternalSafetySources().get(safetySourceId);
+ testConfig.getExternalSafetySources().get(safetySourceId);
ExternalSafetySource externalSafetySourceInRealConfig =
xmlConfig.getExternalSafetySources().get(safetySourceId);
@@ -225,26 +221,21 @@ public final class SafetyCenterConfigReader {
}
@Nullable
- private SafetyCenterConfig readSafetyCenterConfig() {
- InputStream in = mSafetyCenterResourcesContext.getSafetyCenterConfig();
+ private SafetyCenterConfig loadSafetyCenterConfig() {
+ InputStream in = mSafetyCenterResourcesApk.getSafetyCenterConfig();
if (in == null) {
- Log.e(TAG, "Cannot get safety center config file, safety center will be disabled.");
- return null;
- }
-
- Resources resources = mSafetyCenterResourcesContext.getResources();
- if (resources == null) {
- Log.e(TAG, "Cannot get safety center resources, safety center will be disabled.");
+ Log.e(TAG, "Cannot access Safety Center config file");
return null;
}
+ Resources resources = mSafetyCenterResourcesApk.getResources();
try {
SafetyCenterConfig safetyCenterConfig =
SafetyCenterConfigParser.parseXmlResource(in, resources);
- Log.i(TAG, "SafetyCenterConfig read successfully");
+ Log.d(TAG, "SafetyCenterConfig loaded successfully");
return safetyCenterConfig;
} catch (ParseException e) {
- Log.e(TAG, "Cannot read SafetyCenterConfig, safety center will be disabled.", e);
+ Log.e(TAG, "Cannot parse SafetyCenterConfig", e);
return null;
}
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterDataChangeNotifier.java b/service/java/com/android/safetycenter/SafetyCenterDataChangeNotifier.java
index e8bf2626a..0e5617c8d 100644
--- a/service/java/com/android/safetycenter/SafetyCenterDataChangeNotifier.java
+++ b/service/java/com/android/safetycenter/SafetyCenterDataChangeNotifier.java
@@ -16,12 +16,8 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import android.annotation.UserIdInt;
-import androidx.annotation.RequiresApi;
-
import com.android.safetycenter.notifications.SafetyCenterNotificationSender;
import java.util.List;
@@ -34,7 +30,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterDataChangeNotifier {
diff --git a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
index fe4a1ee43..d74d160f4 100644
--- a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
+++ b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
@@ -16,14 +16,14 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY;
import static com.android.safetycenter.internaldata.SafetyCenterBundles.STATIC_ENTRIES_TO_IDS_BUNDLE_KEY;
import static java.util.Collections.emptyList;
-import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.Context;
@@ -50,7 +50,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
import com.android.permission.util.UserUtils;
@@ -61,7 +61,7 @@ import com.android.safetycenter.internaldata.SafetyCenterIds;
import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
import com.android.safetycenter.internaldata.SafetyCenterIssueId;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
-import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import java.util.ArrayList;
import java.util.List;
@@ -78,7 +78,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterDataFactory {
@@ -87,7 +86,7 @@ public final class SafetyCenterDataFactory {
private static final String ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID = "AndroidLockScreenSources";
private final Context mContext;
- private final SafetyCenterResourcesContext mSafetyCenterResourcesContext;
+ private final SafetyCenterResourcesApk mSafetyCenterResourcesApk;
private final SafetyCenterConfigReader mSafetyCenterConfigReader;
private final SafetyCenterRefreshTracker mSafetyCenterRefreshTracker;
private final PendingIntentFactory mPendingIntentFactory;
@@ -96,13 +95,13 @@ public final class SafetyCenterDataFactory {
SafetyCenterDataFactory(
Context context,
- SafetyCenterResourcesContext safetyCenterResourcesContext,
+ SafetyCenterResourcesApk safetyCenterResourcesApk,
SafetyCenterConfigReader safetyCenterConfigReader,
SafetyCenterRefreshTracker safetyCenterRefreshTracker,
PendingIntentFactory pendingIntentFactory,
SafetyCenterDataManager safetyCenterDataManager) {
mContext = context;
- mSafetyCenterResourcesContext = safetyCenterResourcesContext;
+ mSafetyCenterResourcesApk = safetyCenterResourcesApk;
mSafetyCenterConfigReader = safetyCenterConfigReader;
mSafetyCenterRefreshTracker = safetyCenterRefreshTracker;
mPendingIntentFactory = pendingIntentFactory;
@@ -334,7 +333,7 @@ public final class SafetyCenterDataFactory {
if (SdkLevel.isAtLeastU()) {
CharSequence issueAttributionTitle =
TextUtils.isEmpty(safetySourceIssue.getAttributionTitle())
- ? mSafetyCenterResourcesContext.getOptionalString(
+ ? mSafetyCenterResourcesApk.getOptionalString(
safetySourcesGroup.getTitleResId())
: safetySourceIssue.getAttributionTitle();
safetyCenterIssueBuilder.setAttributionTitle(issueAttributionTitle);
@@ -400,8 +399,8 @@ public final class SafetyCenterDataFactory {
safetySource,
defaultPackageName,
userProfileGroup.getProfileParentUserId(),
- false,
- false));
+ /* isUserManaged= */ false,
+ /* isManagedUserRunning= */ false));
if (!SafetySources.supportsManagedProfiles(safetySource)) {
continue;
@@ -422,7 +421,7 @@ public final class SafetyCenterDataFactory {
safetySource,
defaultPackageName,
managedProfileUserId,
- true,
+ /* isUserManaged= */ true,
isManagedUserRunning));
}
}
@@ -443,7 +442,7 @@ public final class SafetyCenterDataFactory {
new SafetyCenterEntryOrGroup(
new SafetyCenterEntryGroup.Builder(
safetySourcesGroup.getId(),
- mSafetyCenterResourcesContext.getString(
+ mSafetyCenterResourcesApk.getString(
safetySourcesGroup.getTitleResId()))
.setSeverityLevel(groupSafetyCenterEntryLevel)
.setSummary(groupSummary)
@@ -509,13 +508,10 @@ public final class SafetyCenterDataFactory {
for (int i = 0; i < entries.size(); i++) {
SafetySourceKey key = toSafetySourceKey(entries.get(i).getId());
if (mSafetyCenterDataManager.sourceHasError(key)) {
- // We always use the singular form of the error string for groups because
- // they appear as single entries in the UI and this ensures consistency,
- // especially when subpages are enabled.
- return getRefreshErrorString(1);
+ return getRefreshErrorString();
}
}
- return mSafetyCenterResourcesContext.getStringByName("group_unknown_summary");
+ return mSafetyCenterResourcesApk.getStringByName("group_unknown_summary");
}
Log.w(
@@ -529,8 +525,7 @@ public final class SafetyCenterDataFactory {
private CharSequence getDefaultGroupSummary(
SafetySourcesGroup safetySourcesGroup, List<SafetyCenterEntry> entries) {
CharSequence groupSummary =
- mSafetyCenterResourcesContext.getOptionalString(
- safetySourcesGroup.getSummaryResId());
+ mSafetyCenterResourcesApk.getOptionalString(safetySourcesGroup.getSummaryResId());
if (safetySourcesGroup.getId().equals(ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID)
&& TextUtils.isEmpty(groupSummary)) {
@@ -649,7 +644,7 @@ public final class SafetyCenterDataFactory {
.setSummary(
inQuietMode
? DevicePolicyResources.getWorkProfilePausedString(
- mSafetyCenterResourcesContext)
+ mSafetyCenterResourcesApk)
: safetySourceStatus.getSummary())
.setEnabled(enabled)
.setSeverityUnspecifiedIconType(severityUnspecifiedIconType)
@@ -710,20 +705,19 @@ public final class SafetyCenterDataFactory {
CharSequence title =
isUserManaged
? DevicePolicyResources.getSafetySourceWorkString(
- mSafetyCenterResourcesContext,
+ mSafetyCenterResourcesApk,
safetySource.getId(),
safetySource.getTitleForWorkResId())
- : mSafetyCenterResourcesContext.getString(safetySource.getTitleResId());
+ : mSafetyCenterResourcesApk.getString(safetySource.getTitleResId());
CharSequence summary =
mSafetyCenterDataManager.sourceHasError(
SafetySourceKey.of(safetySource.getId(), userId))
- ? getRefreshErrorString(1)
- : mSafetyCenterResourcesContext.getOptionalString(
+ ? getRefreshErrorString()
+ : mSafetyCenterResourcesApk.getOptionalString(
safetySource.getSummaryResId());
if (isQuietModeEnabled) {
enabled = false;
- summary =
- DevicePolicyResources.getWorkProfilePausedString(mSafetyCenterResourcesContext);
+ summary = DevicePolicyResources.getWorkProfilePausedString(mSafetyCenterResourcesApk);
}
return new SafetyCenterEntry.Builder(
SafetyCenterIds.encodeToString(safetyCenterEntryId), title)
@@ -754,8 +748,8 @@ public final class SafetyCenterDataFactory {
safetySource,
defaultPackageName,
userProfileGroup.getProfileParentUserId(),
- false,
- false);
+ /* isUserManaged= */ false,
+ /* isManagedUserRunning= */ false);
if (!SafetySources.supportsManagedProfiles(safetySource)) {
continue;
@@ -774,7 +768,7 @@ public final class SafetyCenterDataFactory {
safetySource,
defaultPackageName,
managedProfileUserId,
- true,
+ /* isUserManaged= */ true,
isManagedUserRunning);
}
}
@@ -785,7 +779,7 @@ public final class SafetyCenterDataFactory {
safetyCenterStaticEntryGroups.add(
new SafetyCenterStaticEntryGroup(
- mSafetyCenterResourcesContext.getString(safetySourcesGroup.getTitleResId()),
+ mSafetyCenterResourcesApk.getString(safetySourcesGroup.getTitleResId()),
staticEntries));
}
@@ -864,7 +858,7 @@ public final class SafetyCenterDataFactory {
.setSummary(
inQuietMode
? DevicePolicyResources.getWorkProfilePausedString(
- mSafetyCenterResourcesContext)
+ mSafetyCenterResourcesApk)
: safetySourceStatus.getSummary())
.setPendingIntent(entryPendingIntent)
.build();
@@ -914,19 +908,18 @@ public final class SafetyCenterDataFactory {
CharSequence title =
isUserManaged
? DevicePolicyResources.getSafetySourceWorkString(
- mSafetyCenterResourcesContext,
+ mSafetyCenterResourcesApk,
safetySource.getId(),
safetySource.getTitleForWorkResId())
- : mSafetyCenterResourcesContext.getString(safetySource.getTitleResId());
+ : mSafetyCenterResourcesApk.getString(safetySource.getTitleResId());
CharSequence summary =
mSafetyCenterDataManager.sourceHasError(
SafetySourceKey.of(safetySource.getId(), userId))
- ? getRefreshErrorString(1)
- : mSafetyCenterResourcesContext.getOptionalString(
+ ? getRefreshErrorString()
+ : mSafetyCenterResourcesApk.getOptionalString(
safetySource.getSummaryResId());
if (isQuietModeEnabled) {
- summary =
- DevicePolicyResources.getWorkProfilePausedString(mSafetyCenterResourcesContext);
+ summary = DevicePolicyResources.getWorkProfilePausedString(mSafetyCenterResourcesApk);
}
return new SafetyCenterStaticEntry.Builder(title)
.setSummary(summary)
@@ -1088,11 +1081,10 @@ public final class SafetyCenterDataFactory {
case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN:
case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK:
if (hasSettingsToReview) {
- return mSafetyCenterResourcesContext.getStringByName(
+ return mSafetyCenterResourcesApk.getStringByName(
"overall_severity_level_ok_review_title");
}
- return mSafetyCenterResourcesContext.getStringByName(
- "overall_severity_level_ok_title");
+ return mSafetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title");
case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION:
return getStatusTitleFromIssueCategories(
topNonDismissedIssueInfo,
@@ -1117,6 +1109,7 @@ public final class SafetyCenterDataFactory {
return "";
}
+ @TargetApi(UPSIDE_DOWN_CAKE)
private String getStatusTitleFromIssueCategories(
@Nullable SafetySourceIssueInfo topNonDismissedIssueInfo,
String deviceResourceName,
@@ -1125,7 +1118,7 @@ public final class SafetyCenterDataFactory {
String dataResourceName,
String passwordsResourceName,
String personalSafetyResourceName) {
- String generalString = mSafetyCenterResourcesContext.getStringByName(generalResourceName);
+ String generalString = mSafetyCenterResourcesApk.getStringByName(generalResourceName);
if (topNonDismissedIssueInfo == null) {
Log.w(TAG, "No safety center issues found in a non-green status");
return generalString;
@@ -1133,22 +1126,17 @@ public final class SafetyCenterDataFactory {
int issueCategory = topNonDismissedIssueInfo.getSafetySourceIssue().getIssueCategory();
switch (issueCategory) {
case SafetySourceIssue.ISSUE_CATEGORY_DEVICE:
- return mSafetyCenterResourcesContext.getStringByName(deviceResourceName);
+ return mSafetyCenterResourcesApk.getStringByName(deviceResourceName);
case SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT:
- return mSafetyCenterResourcesContext.getStringByName(accountResourceName);
+ return mSafetyCenterResourcesApk.getStringByName(accountResourceName);
case SafetySourceIssue.ISSUE_CATEGORY_GENERAL:
return generalString;
- }
- if (SdkLevel.isAtLeastU()) {
- switch (issueCategory) {
- case SafetySourceIssue.ISSUE_CATEGORY_DATA:
- return mSafetyCenterResourcesContext.getStringByName(dataResourceName);
- case SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS:
- return mSafetyCenterResourcesContext.getStringByName(passwordsResourceName);
- case SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY:
- return mSafetyCenterResourcesContext.getStringByName(
- personalSafetyResourceName);
- }
+ case SafetySourceIssue.ISSUE_CATEGORY_DATA:
+ return mSafetyCenterResourcesApk.getStringByName(dataResourceName);
+ case SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS:
+ return mSafetyCenterResourcesApk.getStringByName(passwordsResourceName);
+ case SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY:
+ return mSafetyCenterResourcesApk.getStringByName(personalSafetyResourceName);
}
Log.w(TAG, "Unexpected SafetySourceIssue.IssueCategory: " + issueCategory);
@@ -1172,17 +1160,16 @@ public final class SafetyCenterDataFactory {
case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK:
if (topNonDismissedIssue == null) {
if (safetyCenterOverallState.hasSettingsToReview()) {
- return mSafetyCenterResourcesContext.getStringByName(
+ return mSafetyCenterResourcesApk.getStringByName(
"overall_severity_level_ok_review_summary");
}
- return mSafetyCenterResourcesContext.getStringByName(
+ return mSafetyCenterResourcesApk.getStringByName(
"overall_severity_level_ok_summary");
} else if (isTip(topNonDismissedIssue.getSafetySourceIssue())) {
- return mSafetyCenterResourcesContext.getStringByName(
- "overall_severity_level_tip_summary", numTipIssues);
+ return getIcuPluralsString("overall_severity_level_tip_summary", numTipIssues);
} else if (isAutomatic(topNonDismissedIssue.getSafetySourceIssue())) {
- return mSafetyCenterResourcesContext.getStringByName(
+ return getIcuPluralsString(
"overall_severity_level_action_taken_summary", numAutomaticIssues);
}
// Fall through.
@@ -1207,14 +1194,14 @@ public final class SafetyCenterDataFactory {
== SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC;
}
- private String getRefreshErrorString(int numberOfErrorEntries) {
- return getIcuPluralsString("refresh_error", numberOfErrorEntries);
+ private String getRefreshErrorString() {
+ return getIcuPluralsString("refresh_error", /* count= */ 1);
}
private String getIcuPluralsString(String name, int count, Object... formatArgs) {
MessageFormat messageFormat =
new MessageFormat(
- mSafetyCenterResourcesContext.getStringByName(name, formatArgs),
+ mSafetyCenterResourcesApk.getStringByName(name, formatArgs),
Locale.getDefault());
ArrayMap<String, Object> arguments = new ArrayMap<>();
arguments.put("count", count);
@@ -1233,7 +1220,7 @@ public final class SafetyCenterDataFactory {
}
// Fall through.
case SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS:
- return mSafetyCenterResourcesContext.getStringByName("scanning_title");
+ return mSafetyCenterResourcesApk.getStringByName("scanning_title");
}
Log.w(TAG, "Unexpected SafetyCenterStatus.RefreshStatus: " + refreshStatus);
@@ -1248,7 +1235,7 @@ public final class SafetyCenterDataFactory {
return null;
case SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS:
case SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS:
- return mSafetyCenterResourcesContext.getStringByName("loading_summary");
+ return mSafetyCenterResourcesApk.getStringByName("loading_summary");
}
Log.w(TAG, "Unexpected SafetyCenterStatus.RefreshStatus: " + refreshStatus);
diff --git a/service/java/com/android/safetycenter/SafetyCenterFlags.java b/service/java/com/android/safetycenter/SafetyCenterFlags.java
index 1fc88d4b0..e51d3a1cf 100644
--- a/service/java/com/android/safetycenter/SafetyCenterFlags.java
+++ b/service/java/com/android/safetycenter/SafetyCenterFlags.java
@@ -16,10 +16,8 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.safetycenter.SafetyCenterManager.RefreshReason;
-import android.annotation.Nullable;
import android.os.Binder;
import android.provider.DeviceConfig;
import android.safetycenter.SafetySourceData;
@@ -27,20 +25,22 @@ import android.safetycenter.SafetySourceIssue;
import android.util.ArraySet;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
-import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import java.io.PrintWriter;
import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
/**
* A class to access the Safety Center {@link DeviceConfig} flags.
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetyCenterFlags {
private static final String TAG = "SafetyCenterFlags";
@@ -63,9 +63,6 @@ public final class SafetyCenterFlags {
private static final String PROPERTY_NOTIFICATION_RESURFACE_INTERVAL =
"safety_center_notification_resurface_interval";
- private static final String PROPERTY_SHOW_ERROR_ENTRIES_ON_TIMEOUT =
- "safety_center_show_error_entries_on_timeout";
-
private static final String PROPERTY_REPLACE_LOCK_SCREEN_ICON_ACTION =
"safety_center_replace_lock_screen_icon_action";
@@ -108,6 +105,9 @@ public final class SafetyCenterFlags {
private static final String PROPERTY_TEMP_HIDDEN_ISSUE_RESURFACE_DELAY_MILLIS =
"safety_center_temp_hidden_issue_resurface_delay_millis";
+ private static final String PROPERTY_ACTIONS_TO_OVERRIDE_WITH_DEFAULT_INTENT =
+ "safety_center_actions_to_override_with_default_intent";
+
private static final Duration RESOLVING_ACTION_TIMEOUT_DEFAULT_DURATION =
Duration.ofSeconds(10);
@@ -123,6 +123,9 @@ public final class SafetyCenterFlags {
private static final String RESURFACE_ISSUE_DELAYS_DEFAULT = "";
private static final Duration RESURFACE_ISSUE_DELAYS_DEFAULT_DURATION = Duration.ofDays(180);
+ private static final ArraySet<String> sAllowedNotificationSourcesUPlus =
+ new ArraySet<>(new String[] {"GoogleBackupAndRestore"});
+
private static volatile String sUntrackedSourcesDefault =
"AndroidAccessibility,AndroidBackgroundLocation,"
+ "AndroidNotificationListener,AndroidPermissionAutoRevoke";
@@ -131,30 +134,40 @@ public final class SafetyCenterFlags {
private static volatile String sIssueCategoryAllowlistDefault = "";
- private static volatile String sRefreshOnPageOpenSourcesDefault =
- "AndroidBiometrics,AndroidLockScreen";
+ private static volatile String sRefreshOnPageOpenSourcesDefault = "AndroidBiometrics";
+
+ private static volatile String sActionsToOverrideWithDefaultIntentDefault = "";
- static void init(SafetyCenterResourcesContext resourceContext) {
+ static void init(SafetyCenterResourcesApk safetyCenterResourcesApk) {
String untrackedSourcesDefault =
- resourceContext.getOptionalStringByName("config_defaultUntrackedSources");
+ safetyCenterResourcesApk.getOptionalStringByName("config_defaultUntrackedSources");
if (untrackedSourcesDefault != null) {
sUntrackedSourcesDefault = untrackedSourcesDefault;
}
String backgroundRefreshDenyDefault =
- resourceContext.getOptionalStringByName("config_defaultBackgroundRefreshDeny");
+ safetyCenterResourcesApk.getOptionalStringByName(
+ "config_defaultBackgroundRefreshDeny");
if (backgroundRefreshDenyDefault != null) {
sBackgroundRefreshDenyDefault = backgroundRefreshDenyDefault;
}
String issueCategoryAllowlistDefault =
- resourceContext.getOptionalStringByName("config_defaultIssueCategoryAllowlist");
+ safetyCenterResourcesApk.getOptionalStringByName(
+ "config_defaultIssueCategoryAllowlist");
if (issueCategoryAllowlistDefault != null) {
sIssueCategoryAllowlistDefault = issueCategoryAllowlistDefault;
}
String refreshOnPageOpenSourcesDefault =
- resourceContext.getOptionalStringByName("config_defaultRefreshOnPageOpenSources");
+ safetyCenterResourcesApk.getOptionalStringByName(
+ "config_defaultRefreshOnPageOpenSources");
if (refreshOnPageOpenSourcesDefault != null) {
sRefreshOnPageOpenSourcesDefault = refreshOnPageOpenSourcesDefault;
}
+ String actionsToOverrideWithDefaultIntentDefault =
+ safetyCenterResourcesApk.getOptionalStringByName(
+ "config_defaultActionsToOverrideWithDefaultIntent");
+ if (actionsToOverrideWithDefaultIntentDefault != null) {
+ sActionsToOverrideWithDefaultIntentDefault = actionsToOverrideWithDefaultIntentDefault;
+ }
}
private static final Duration TEMP_HIDDEN_ISSUE_RESURFACE_DELAY_DEFAULT_DURATION =
@@ -165,7 +178,10 @@ public final class SafetyCenterFlags {
fout.println("FLAGS");
printFlag(fout, PROPERTY_SAFETY_CENTER_ENABLED, getSafetyCenterEnabled());
printFlag(fout, PROPERTY_NOTIFICATIONS_ENABLED, getNotificationsEnabled());
- printFlag(fout, PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES, getNotificationsAllowedSourceIds());
+ printFlag(
+ fout,
+ PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES,
+ getNotificationsAllowedSourceIdsFlag());
printFlag(fout, PROPERTY_NOTIFICATIONS_MIN_DELAY, getNotificationsMinDelay());
printFlag(
fout,
@@ -173,7 +189,6 @@ public final class SafetyCenterFlags {
getImmediateNotificationBehaviorIssues());
printFlag(
fout, PROPERTY_NOTIFICATION_RESURFACE_INTERVAL, getNotificationResurfaceInterval());
- printFlag(fout, PROPERTY_SHOW_ERROR_ENTRIES_ON_TIMEOUT, getShowErrorEntriesOnTimeout());
printFlag(fout, PROPERTY_REPLACE_LOCK_SCREEN_ICON_ACTION, getReplaceLockScreenIconAction());
printFlag(fout, PROPERTY_RESOLVING_ACTION_TIMEOUT_MILLIS, getResolvingActionTimeout());
printFlag(fout, PROPERTY_FGS_ALLOWLIST_DURATION_MILLIS, getFgsAllowlistDuration());
@@ -235,6 +250,20 @@ public final class SafetyCenterFlags {
* and therefore this is the only way to enable notifications for sources on Android T.
*/
public static ArraySet<String> getNotificationsAllowedSourceIds() {
+ ArraySet<String> sources = getNotificationsAllowedSourceIdsFlag();
+ if (SdkLevel.isAtLeastU()) {
+ // This is a hack to update the flag value via mainline update. Reasons why we can't do
+ // this via:
+ // remote flag update - these are generally avoided and considered risky
+ // XML config - it would break GTS tests for OEMs that have a separate config copy
+ // default flag value - it would also require a remote flag update
+ sources.addAll(sAllowedNotificationSourcesUPlus);
+ }
+
+ return sources;
+ }
+
+ private static ArraySet<String> getNotificationsAllowedSourceIdsFlag() {
return getCommaSeparatedStrings(PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES);
}
@@ -284,13 +313,6 @@ public final class SafetyCenterFlags {
}
/**
- * Returns whether we should show error entries for sources that timeout when refreshing them.
- */
- static boolean getShowErrorEntriesOnTimeout() {
- return getBoolean(PROPERTY_SHOW_ERROR_ENTRIES_ON_TIMEOUT, true);
- }
-
- /**
* Returns whether we should replace the lock screen source's {@link
* android.safetycenter.SafetySourceStatus.IconAction}.
*/
@@ -407,6 +429,17 @@ public final class SafetyCenterFlags {
return getString(PROPERTY_RESURFACE_ISSUE_DELAYS_MILLIS, RESURFACE_ISSUE_DELAYS_DEFAULT);
}
+ /**
+ * Returns a comma-delimited list of colon-delimited pairs of SourceId:ActionId. The action IDs
+ * listed by this flag should have their {@code PendingIntent}s overridden with the source's
+ * default intent drawn from Safety Center's config file, if available.
+ */
+ private static String getActionsToOverrideWithDefaultIntent() {
+ return getString(
+ PROPERTY_ACTIONS_TO_OVERRIDE_WITH_DEFAULT_INTENT,
+ sActionsToOverrideWithDefaultIntentDefault);
+ }
+
/** Returns a duration after which a temporarily hidden issue will resurface. */
public static Duration getTemporarilyHiddenIssueResurfaceDelay() {
return getDuration(
@@ -420,19 +453,10 @@ public final class SafetyCenterFlags {
*/
public static boolean isIssueCategoryAllowedForSource(
@SafetySourceIssue.IssueCategory int issueCategory, String safetySourceId) {
- String issueCategoryAllowlists = getIssueCategoryAllowlists();
- String allowlistString =
- getStringValueFromStringMapping(issueCategoryAllowlists, issueCategory);
- if (allowlistString == null) {
- return true;
- }
- String[] allowlistArray = allowlistString.split("\\|");
- for (int i = 0; i < allowlistArray.length; i++) {
- if (allowlistArray[i].equals(safetySourceId)) {
- return true;
- }
- }
- return false;
+ List<String> allowlist =
+ getStringListValueFromStringMapping(
+ getIssueCategoryAllowlists(), Integer.toString(issueCategory));
+ return allowlist.isEmpty() || allowlist.contains(safetySourceId);
}
/** Returns a set of package certificates allowlisted for the given package name. */
@@ -442,7 +466,7 @@ public final class SafetyCenterFlags {
if (allowlistedCertString == null) {
return new ArraySet<>();
}
- return new ArraySet<String>(allowlistedCertString.split("\\|"));
+ return new ArraySet<>(allowlistedCertString.split("\\|"));
}
/**
@@ -464,6 +488,16 @@ public final class SafetyCenterFlags {
}
/**
+ * Returns a list of action IDs that should be overridden with the source's default intent drawn
+ * from the config for a given source.
+ */
+ public static List<String> getActionsToOverrideWithDefaultIntentForSource(
+ String safetySourceId) {
+ return getStringListValueFromStringMapping(
+ getActionsToOverrideWithDefaultIntent(), safetySourceId);
+ }
+
+ /**
* Returns whether to show subpages in the Safety Center UI for Android-U instead of the
* expand-and-collapse list implementation.
*/
@@ -527,15 +561,15 @@ public final class SafetyCenterFlags {
* pairs of integers and longs.
*/
@Nullable
- private static Long getLongValueFromStringMapping(String config, int key) {
- String valueString = getStringValueFromStringMapping(config, key);
+ private static Long getLongValueFromStringMapping(String mapping, int key) {
+ String valueString = getStringValueFromStringMapping(mapping, key);
if (valueString == null) {
return null;
}
try {
return Long.parseLong(valueString);
} catch (NumberFormatException e) {
- Log.w(TAG, "Badly formatted string config: " + config, e);
+ Log.w(TAG, "Badly formatted string mapping: " + mapping, e);
return null;
}
}
@@ -545,8 +579,8 @@ public final class SafetyCenterFlags {
* of integers and strings.
*/
@Nullable
- private static String getStringValueFromStringMapping(String config, int key) {
- return getStringValueFromStringMapping(config, Integer.toString(key));
+ private static String getStringValueFromStringMapping(String mapping, int key) {
+ return getStringValueFromStringMapping(mapping, Integer.toString(key));
}
/**
@@ -554,15 +588,15 @@ public final class SafetyCenterFlags {
* string pairs.
*/
@Nullable
- private static String getStringValueFromStringMapping(String config, String key) {
- if (config.isEmpty()) {
+ private static String getStringValueFromStringMapping(String mapping, String key) {
+ if (mapping.isEmpty()) {
return null;
}
- String[] pairsList = config.split(",");
+ String[] pairsList = mapping.split(",");
for (int i = 0; i < pairsList.length; i++) {
String[] pair = pairsList[i].split(":", -1 /* allow trailing empty strings */);
if (pair.length != 2) {
- Log.w(TAG, "Badly formatted string config: " + config);
+ Log.w(TAG, "Badly formatted string mapping: " + mapping);
continue;
}
if (pair[0].equals(key)) {
@@ -572,5 +606,14 @@ public final class SafetyCenterFlags {
return null;
}
+ private static List<String> getStringListValueFromStringMapping(String mapping, String key) {
+ String value = getStringValueFromStringMapping(mapping, key);
+ if (value == null) {
+ return Collections.emptyList();
+ }
+
+ return Arrays.asList(value.split("\\|"));
+ }
+
private SafetyCenterFlags() {}
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterListeners.java b/service/java/com/android/safetycenter/SafetyCenterListeners.java
index 9e07c3d17..5f89f46ff 100644
--- a/service/java/com/android/safetycenter/SafetyCenterListeners.java
+++ b/service/java/com/android/safetycenter/SafetyCenterListeners.java
@@ -16,9 +16,6 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.IBinder;
import android.os.RemoteCallbackList;
@@ -30,7 +27,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicReference;
@@ -43,7 +40,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* <p>This class isn't thread safe. Thread safety must be handled by the caller.
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetyCenterListeners {
@@ -67,7 +63,7 @@ final class SafetyCenterListeners {
try {
listener.onSafetyCenterDataChanged(safetyCenterData);
} catch (RemoteException e) {
- Log.e(TAG, "Error delivering SafetyCenterData to listener", e);
+ Log.w(TAG, "Error delivering SafetyCenterData to listener", e);
}
}
@@ -81,7 +77,7 @@ final class SafetyCenterListeners {
try {
listener.onError(safetyCenterErrorDetails);
} catch (RemoteException e) {
- Log.e(TAG, "Error delivering SafetyCenterErrorDetails to listener", e);
+ Log.w(TAG, "Error delivering SafetyCenterErrorDetails to listener", e);
}
}
@@ -94,7 +90,11 @@ final class SafetyCenterListeners {
int[] relevantUserIds = userProfileGroup.getProfileParentAndManagedRunningProfilesUserIds();
for (int i = 0; i < relevantUserIds.length; i++) {
deliverUpdateForUser(
- relevantUserIds[i], userProfileGroup, safetyCenterDataCache, true, null);
+ relevantUserIds[i],
+ userProfileGroup,
+ safetyCenterDataCache,
+ /* updateSafetyCenterData= */ true,
+ /* safetyCenterErrorDetails= */ null);
}
}
@@ -111,7 +111,7 @@ final class SafetyCenterListeners {
relevantUserIds[i],
userProfileGroup,
safetyCenterDataCache,
- false,
+ /* updateSafetyCenterData= */ false,
safetyCenterErrorDetails);
}
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java b/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java
index a28ce7d22..d98127300 100644
--- a/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java
+++ b/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java
@@ -16,14 +16,12 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT;
import static com.android.safetycenter.logging.SafetyCenterStatsdLogger.toSystemEventResult;
import android.annotation.ElapsedRealtimeLong;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.os.SystemClock;
@@ -34,7 +32,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.permission.util.UserUtils;
import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
@@ -53,7 +51,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterRefreshTracker {
private static final String TAG = "SafetyCenterRefreshTrac";
@@ -78,15 +75,15 @@ public final class SafetyCenterRefreshTracker {
String reportRefreshInProgress(
@RefreshReason int refreshReason, UserProfileGroup userProfileGroup) {
if (mRefreshInProgress != null) {
- Log.w(TAG, "Replacing an ongoing refresh");
+ Log.i(TAG, "Replacing ongoing refresh with id: " + mRefreshInProgress.getId());
}
String refreshBroadcastId = UUID.randomUUID() + "_" + mRefreshCounter++;
- Log.v(
+ Log.d(
TAG,
- "Starting a new refresh with refreshReason:"
+ "Starting a new refresh with reason: "
+ refreshReason
- + " refreshBroadcastId:"
+ + ", and id: "
+ refreshBroadcastId);
mRefreshInProgress =
@@ -158,8 +155,7 @@ public final class SafetyCenterRefreshTracker {
*/
public boolean reportSourceRefreshCompleted(
String refreshBroadcastId,
- String sourceId,
- @UserIdInt int userId,
+ SafetySourceKey safetySourceKey,
boolean successful,
boolean dataChanged) {
RefreshInProgress refreshInProgress =
@@ -168,9 +164,9 @@ public final class SafetyCenterRefreshTracker {
return false;
}
- SafetySourceKey sourceKey = SafetySourceKey.of(sourceId, userId);
Duration duration =
- refreshInProgress.markSourceRefreshComplete(sourceKey, successful, dataChanged);
+ refreshInProgress.markSourceRefreshComplete(
+ safetySourceKey, successful, dataChanged);
int refreshReason = refreshInProgress.getReason();
int requestType = RefreshReasons.toRefreshRequestType(refreshReason);
@@ -178,8 +174,8 @@ public final class SafetyCenterRefreshTracker {
int sourceResult = toSystemEventResult(successful);
SafetyCenterStatsdLogger.writeSourceRefreshSystemEvent(
requestType,
- sourceId,
- UserUtils.isManagedProfile(userId, mContext),
+ safetySourceKey.getSourceId(),
+ UserUtils.isManagedProfile(safetySourceKey.getUserId(), mContext),
duration,
sourceResult,
refreshReason,
@@ -237,7 +233,7 @@ public final class SafetyCenterRefreshTracker {
*/
void clearRefreshForUser(@UserIdInt int userId) {
if (mRefreshInProgress == null) {
- Log.v(TAG, "Clear refresh for user called but no refresh in progress");
+ Log.d(TAG, "Clear refresh for user called but no refresh in progress");
return;
}
if (mRefreshInProgress.clearForUser(userId)) {
@@ -272,6 +268,15 @@ public final class SafetyCenterRefreshTracker {
int refreshReason = clearedRefresh.getReason();
int requestType = RefreshReasons.toRefreshRequestType(refreshReason);
+ Log.w(
+ TAG,
+ "Timeout after "
+ + clearedRefresh.getDurationSinceStart()
+ + " for refresh with reason: "
+ + refreshReason
+ + ", and id: "
+ + clearedRefresh.getId());
+
for (int i = 0; i < timedOutSources.size(); i++) {
SafetySourceKey sourceKey = timedOutSources.valueAt(i);
Duration duration = clearedRefresh.getDurationSinceSourceStart(sourceKey);
@@ -285,6 +290,15 @@ public final class SafetyCenterRefreshTracker {
refreshReason,
false);
}
+
+ Log.w(
+ TAG,
+ "Refresh with id: "
+ + clearedRefresh.getId()
+ + " timed out for tracked source id: "
+ + sourceKey.getSourceId()
+ + ", and user id: "
+ + sourceKey.getUserId());
}
SafetyCenterStatsdLogger.writeWholeRefreshSystemEvent(
@@ -298,7 +312,7 @@ public final class SafetyCenterRefreshTracker {
}
/**
- * Clears the any refresh in progress and returns it for the caller to do what it needs to.
+ * Clears the refresh in progress and returns it for the caller to do what it needs to.
*
* <p>If there was no refresh in progress then {@code null} is returned.
*/
@@ -306,11 +320,11 @@ public final class SafetyCenterRefreshTracker {
private RefreshInProgress clearRefreshInternal() {
RefreshInProgress refreshToClear = mRefreshInProgress;
if (refreshToClear == null) {
- Log.v(TAG, "Clear refresh called but no refresh in progress");
+ Log.d(TAG, "Clear refresh called but no refresh in progress");
return null;
}
- Log.v(TAG, "Clearing refresh with refreshBroadcastId:" + refreshToClear.getId());
+ Log.v(TAG, "Clearing refresh with id: " + refreshToClear.getId());
mRefreshInProgress = null;
return refreshToClear;
}
@@ -324,13 +338,7 @@ public final class SafetyCenterRefreshTracker {
String methodName, String refreshBroadcastId) {
RefreshInProgress refreshInProgress = mRefreshInProgress;
if (refreshInProgress == null || !refreshInProgress.getId().equals(refreshBroadcastId)) {
- Log.i(
- TAG,
- methodName
- + " called for invalid refresh broadcast id: "
- + refreshBroadcastId
- + "; no such refresh in"
- + " progress");
+ Log.i(TAG, methodName + " called with invalid refresh id: " + refreshBroadcastId);
return null;
}
return refreshInProgress;
@@ -435,19 +443,19 @@ public final class SafetyCenterRefreshTracker {
}
Log.v(
TAG,
- "Refresh started for sourceId:"
+ "Refresh with id: "
+ + mId
+ + " started for source id: "
+ safetySourceKey.getSourceId()
- + " userId:"
+ + ", user id: "
+ safetySourceKey.getUserId()
- + " with refreshBroadcastId:"
- + mId
- + " at currentElapsedMillis:"
+ + ", elapsed millis: "
+ currentElapsedMillis
- + " & tracking:"
+ + ", tracking: "
+ tracked
+ ", now "
+ mSourceRefreshesInFlight.size()
- + " tracked sources in flight.");
+ + " tracked sources in flight");
}
@Nullable
@@ -464,23 +472,23 @@ public final class SafetyCenterRefreshTracker {
: Duration.ofMillis(SystemClock.elapsedRealtime() - startElapsedMillis);
Log.v(
TAG,
- "Refresh completed for sourceId:"
+ "Refresh with id: "
+ + mId
+ + " completed for source id: "
+ safetySourceKey.getSourceId()
- + " userId:"
+ + ", user id: "
+ safetySourceKey.getUserId()
- + " with refreshBroadcastId:"
- + mId
- + " duration:"
+ + ", duration: "
+ duration
- + " successful:"
+ + ", successful: "
+ successful
- + " dataChanged:"
+ + ", data changed: "
+ dataChanged
- + " & tracking:"
+ + ", tracking: "
+ tracked
- + ", "
+ + ", now "
+ mSourceRefreshesInFlight.size()
- + " tracked sources still in flight.");
+ + " tracked sources in flight");
return duration;
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterService.java b/service/java/com/android/safetycenter/SafetyCenterService.java
index f23f041bd..1c72e7118 100644
--- a/service/java/com/android/safetycenter/SafetyCenterService.java
+++ b/service/java/com/android/safetycenter/SafetyCenterService.java
@@ -21,8 +21,8 @@ import static android.Manifest.permission.READ_SAFETY_CENTER_STATUS;
import static android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_LOCALE_CHANGE;
import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER;
import static android.safetycenter.SafetyCenterManager.RefreshReason;
import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED;
@@ -35,11 +35,10 @@ import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriend
import static java.util.Objects.requireNonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.StatsManager;
-import android.app.StatsManager.StatsPullAtomCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -69,13 +68,17 @@ import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.Keep;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.internal.annotations.GuardedBy;
import com.android.modules.utils.BackgroundThread;
+import com.android.modules.utils.build.SdkLevel;
import com.android.permission.util.ForegroundThread;
import com.android.permission.util.UserUtils;
import com.android.safetycenter.data.SafetyCenterDataManager;
+import com.android.safetycenter.data.SafetyEventFix;
+import com.android.safetycenter.data.SafetySourceDataFix;
import com.android.safetycenter.internaldata.SafetyCenterIds;
import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
import com.android.safetycenter.internaldata.SafetyCenterIssueId;
@@ -85,16 +88,13 @@ import com.android.safetycenter.notifications.SafetyCenterNotificationChannels;
import com.android.safetycenter.notifications.SafetyCenterNotificationReceiver;
import com.android.safetycenter.notifications.SafetyCenterNotificationSender;
import com.android.safetycenter.pendingintents.PendingIntentSender;
-import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.Executor;
-
-import javax.annotation.concurrent.NotThreadSafe;
/**
* Service for the safety center.
@@ -102,7 +102,6 @@ import javax.annotation.concurrent.NotThreadSafe;
* @hide
*/
@Keep
-@RequiresApi(TIRAMISU)
public final class SafetyCenterService extends SystemService {
private static final String TAG = "SafetyCenterService";
@@ -112,9 +111,8 @@ public final class SafetyCenterService extends SystemService {
@GuardedBy("mApiLock")
private final SafetyCenterTimeouts mSafetyCenterTimeouts = new SafetyCenterTimeouts();
- private final SafetyCenterResourcesContext mSafetyCenterResourcesContext;
-
- private final SafetyCenterNotificationChannels mNotificationChannels;
+ @GuardedBy("mApiLock")
+ private final SafetyCenterResourcesApk mSafetyCenterResourcesApk;
@GuardedBy("mApiLock")
private final SafetyCenterConfigReader mSafetyCenterConfigReader;
@@ -122,6 +120,8 @@ public final class SafetyCenterService extends SystemService {
@GuardedBy("mApiLock")
private final SafetyCenterRefreshTracker mSafetyCenterRefreshTracker;
+ private final SafetySourceDataFix mSafetySourceDataFix;
+
@GuardedBy("mApiLock")
private final SafetyCenterDataManager mSafetyCenterDataManager;
@@ -132,6 +132,9 @@ public final class SafetyCenterService extends SystemService {
private final SafetyCenterListeners mSafetyCenterListeners;
@GuardedBy("mApiLock")
+ private final SafetyCenterNotificationChannels mNotificationChannels;
+
+ @GuardedBy("mApiLock")
private final SafetyCenterNotificationSender mNotificationSender;
@GuardedBy("mApiLock")
@@ -140,34 +143,37 @@ public final class SafetyCenterService extends SystemService {
@GuardedBy("mApiLock")
private final SafetyCenterDataChangeNotifier mSafetyCenterDataChangeNotifier;
- private final StatsPullAtomCallback mPullAtomCallback;
private final boolean mDeviceSupportsSafetyCenter;
/** Whether the {@link SafetyCenterConfig} was successfully loaded. */
- private volatile boolean mConfigAvailable;
+ private volatile boolean mConfigAvailable = false;
public SafetyCenterService(Context context) {
super(context);
- mSafetyCenterResourcesContext = new SafetyCenterResourcesContext(context);
- mSafetyCenterConfigReader = new SafetyCenterConfigReader(mSafetyCenterResourcesContext);
+ mSafetyCenterResourcesApk = new SafetyCenterResourcesApk(context);
+ mSafetyCenterConfigReader = new SafetyCenterConfigReader(mSafetyCenterResourcesApk);
mSafetyCenterRefreshTracker = new SafetyCenterRefreshTracker(context);
+ PendingIntentFactory pendingIntentFactory =
+ new PendingIntentFactory(context, mSafetyCenterResourcesApk);
+ mSafetySourceDataFix =
+ new SafetySourceDataFix(context, pendingIntentFactory, mSafetyCenterConfigReader);
mSafetyCenterDataManager =
new SafetyCenterDataManager(
context, mSafetyCenterConfigReader, mSafetyCenterRefreshTracker, mApiLock);
mSafetyCenterDataFactory =
new SafetyCenterDataFactory(
context,
- mSafetyCenterResourcesContext,
+ mSafetyCenterResourcesApk,
mSafetyCenterConfigReader,
mSafetyCenterRefreshTracker,
- new PendingIntentFactory(context, mSafetyCenterResourcesContext),
+ pendingIntentFactory,
mSafetyCenterDataManager);
mSafetyCenterListeners = new SafetyCenterListeners(mSafetyCenterDataFactory);
- mNotificationChannels = new SafetyCenterNotificationChannels(mSafetyCenterResourcesContext);
+ mNotificationChannels = new SafetyCenterNotificationChannels(mSafetyCenterResourcesApk);
mNotificationSender =
SafetyCenterNotificationSender.newInstance(
context,
- mSafetyCenterResourcesContext,
+ mSafetyCenterResourcesApk,
mNotificationChannels,
mSafetyCenterDataManager);
mSafetyCenterBroadcastDispatcher =
@@ -176,13 +182,6 @@ public final class SafetyCenterService extends SystemService {
mSafetyCenterConfigReader,
mSafetyCenterRefreshTracker,
mSafetyCenterDataManager);
- mPullAtomCallback =
- new SafetyCenterPullAtomCallback(
- context,
- mApiLock,
- mSafetyCenterConfigReader,
- mSafetyCenterDataFactory,
- mSafetyCenterDataManager);
mSafetyCenterDataChangeNotifier =
new SafetyCenterDataChangeNotifier(mNotificationSender, mSafetyCenterListeners);
mDeviceSupportsSafetyCenter =
@@ -191,57 +190,98 @@ public final class SafetyCenterService extends SystemService {
Resources.getSystem()
.getIdentifier(
"config_enableSafetyCenter", "bool", "android"));
- if (!mDeviceSupportsSafetyCenter) {
- Log.i(TAG, "Device does not support safety center, safety center will be disabled.");
- }
}
@Override
public void onStart() {
publishBinderService(Context.SAFETY_CENTER_SERVICE, new Stub());
- if (mDeviceSupportsSafetyCenter) {
- synchronized (mApiLock) {
- mSafetyCenterResourcesContext.init();
- SafetyCenterFlags.init(mSafetyCenterResourcesContext);
- mConfigAvailable = mSafetyCenterConfigReader.loadConfig();
- if (mConfigAvailable) {
- mSafetyCenterDataManager.loadPersistableDataStateFromFile();
- new UserBroadcastReceiver().register(getContext());
- new SafetyCenterNotificationReceiver(
- this,
- mSafetyCenterDataManager,
- mSafetyCenterDataChangeNotifier,
- mApiLock)
- .register(getContext());
- new LocaleBroadcastReceiver().register(getContext());
- }
+ if (!mDeviceSupportsSafetyCenter) {
+ Log.i(TAG, "Device does not support Safety Center, it will be disabled");
+ return;
+ }
+
+ synchronized (mApiLock) {
+ boolean safetyCenterResourcesInitialized = mSafetyCenterResourcesApk.init();
+ if (!safetyCenterResourcesInitialized) {
+ Log.e(TAG, "Cannot init Safety Center resources, Safety Center will be disabled");
+ return;
}
+
+ SafetyCenterFlags.init(mSafetyCenterResourcesApk);
+
+ if (!mSafetyCenterConfigReader.loadConfig()) {
+ Log.e(TAG, "Cannot init Safety Center config, Safety Center will be disabled");
+ return;
+ }
+
+ mConfigAvailable = true;
+ mSafetyCenterDataManager.loadPersistableDataStateFromFile();
+ new UserBroadcastReceiver().register(getContext());
+ new SafetyCenterNotificationReceiver(
+ /* service= */ this,
+ mSafetyCenterDataManager,
+ mSafetyCenterDataChangeNotifier,
+ mApiLock)
+ .register(getContext());
+ new LocaleBroadcastReceiver().register(getContext());
}
}
@Override
public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_BOOT_COMPLETED && canUseSafetyCenter()) {
- registerSafetyCenterEnabledListener();
- registerSafetyCenterPullAtomCallback();
- mNotificationChannels.createAllChannelsForAllUsers(getContext());
+ if (phase != SystemService.PHASE_BOOT_COMPLETED || !canUseSafetyCenter()) {
+ return;
}
+
+ SafetyCenterPullAtomCallback pullAtomCallback;
+ synchronized (mApiLock) {
+ registerSafetyCenterEnabledListenerLocked();
+ pullAtomCallback = newSafetyCenterPullAtomCallbackLocked();
+ }
+ registerSafetyCenterPullAtomCallback(pullAtomCallback);
}
- private void registerSafetyCenterEnabledListener() {
- Executor foregroundThreadExecutor = ForegroundThread.getExecutor();
- SafetyCenterEnabledListener listener = new SafetyCenterEnabledListener();
- // Ensure the listener is called first with the current state on the same thread.
- foregroundThreadExecutor.execute(listener::setInitialState);
+ @GuardedBy("mApiLock")
+ private void registerSafetyCenterEnabledListenerLocked() {
+ SafetyCenterEnabledListener safetyCenterEnabledListener = new SafetyCenterEnabledListener();
DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_PRIVACY, foregroundThreadExecutor, listener);
+ DeviceConfig.NAMESPACE_PRIVACY,
+ ForegroundThread.getExecutor(),
+ safetyCenterEnabledListener);
+ // Set the initial state *after* registering the listener, in the unlikely event that the
+ // flag changes between creating the listener and registering it (in which case we could
+ // miss an update and end up with an inconsistent state).
+ setInitialStateLocked(safetyCenterEnabledListener);
}
- private void registerSafetyCenterPullAtomCallback() {
+ @GuardedBy("mApiLock")
+ @SuppressWarnings("GuardedBy")
+ // @GuardedBy is unable to infer that the `SafetyCenterService.this.mApiLock` in
+ // `SafetyCenterService` is the same as the one in `SafetyCenterEnabledListener` here, so it
+ // has to be suppressed.
+ private void setInitialStateLocked(SafetyCenterEnabledListener safetyCenterEnabledListener) {
+ safetyCenterEnabledListener.setInitialStateLocked();
+ }
+
+ @GuardedBy("mApiLock")
+ private SafetyCenterPullAtomCallback newSafetyCenterPullAtomCallbackLocked() {
+ return new SafetyCenterPullAtomCallback(
+ getContext(),
+ mApiLock,
+ mSafetyCenterConfigReader,
+ mSafetyCenterDataFactory,
+ mSafetyCenterDataManager);
+ }
+
+ private void registerSafetyCenterPullAtomCallback(
+ SafetyCenterPullAtomCallback pullAtomCallback) {
StatsManager statsManager =
requireNonNull(getContext().getSystemService(StatsManager.class));
statsManager.setPullAtomCallback(
- SAFETY_STATE, null, BackgroundThread.getExecutor(), mPullAtomCallback);
+ SAFETY_STATE,
+ /* metadata= */ null,
+ BackgroundThread.getExecutor(),
+ pullAtomCallback);
}
/** Service implementation of {@link ISafetyCenterManager.Stub}. */
@@ -275,6 +315,16 @@ public final class SafetyCenterService extends SystemService {
UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId);
synchronized (mApiLock) {
+ safetySourceData =
+ mSafetySourceDataFix.maybeOverrideSafetySourceData(
+ safetySourceId, safetySourceData, packageName, userId);
+ safetyEvent =
+ SafetyEventFix.maybeOverrideSafetyEvent(
+ mSafetyCenterDataManager,
+ safetySourceId,
+ safetySourceData,
+ safetyEvent,
+ userId);
boolean hasUpdate =
mSafetyCenterDataManager.setSafetySourceData(
safetySourceData, safetySourceId, safetyEvent, packageName, userId);
@@ -342,7 +392,7 @@ public final class SafetyCenterService extends SystemService {
== SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) {
safetyCenterErrorDetails =
new SafetyCenterErrorDetails(
- mSafetyCenterResourcesContext.getStringByName(
+ mSafetyCenterResourcesApk.getStringByName(
"resolving_action_error"));
}
if (hasUpdate) {
@@ -363,7 +413,10 @@ public final class SafetyCenterService extends SystemService {
|| !checkApiEnabled("refreshSafetySources")) {
return;
}
- startRefreshingSafetySources(refreshReason, userId);
+
+ synchronized (mApiLock) {
+ startRefreshingSafetySourcesLocked(refreshReason, userId);
+ }
}
@Override
@@ -380,7 +433,10 @@ public final class SafetyCenterService extends SystemService {
|| !checkApiEnabled("refreshSpecificSafetySources")) {
return;
}
- startRefreshingSafetySources(refreshReason, userId, safetySourceIds);
+
+ synchronized (mApiLock) {
+ startRefreshingSafetySourcesLocked(refreshReason, userId, safetySourceIds);
+ }
}
@Override
@@ -392,7 +448,7 @@ public final class SafetyCenterService extends SystemService {
// search works by adding all the entries very rarely (and relies on filtering them out
// instead).
if (!canUseSafetyCenter()) {
- Log.w(TAG, "Called getSafetyCenterConfig, but Safety Center is not supported");
+ Log.i(TAG, "Called getSafetyCenterConfig, but Safety Center is not supported");
return null;
}
@@ -504,7 +560,7 @@ public final class SafetyCenterService extends SystemService {
PendingIntent onDismissPendingIntent =
safetySourceIssue.getOnDismissPendingIntent();
if (onDismissPendingIntent != null
- && !dispatchPendingIntent(onDismissPendingIntent, null)) {
+ && !dispatchPendingIntent(onDismissPendingIntent)) {
Log.w(
TAG,
"Error dispatching dismissal for issue: "
@@ -634,13 +690,14 @@ 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, false, message, getContext());
+ UserUtils.enforceCrossUserPermission(
+ userId, /* allowAll= */ false, message, getContext());
if (!UserUtils.isUserExistent(userId, getContext())) {
Log.w(
TAG,
"Called "
+ message
- + " with user id "
+ + " with user id: "
+ userId
+ ", which does not correspond to an existing user");
return false;
@@ -650,7 +707,7 @@ public final class SafetyCenterService extends SystemService {
TAG,
"Called "
+ message
- + " with user id "
+ + " with user id: "
+ userId
+ ", which is an unsupported user");
return false;
@@ -676,7 +733,7 @@ public final class SafetyCenterService extends SystemService {
packageManager.getPackageUidAsUser(
packageName, PackageInfoFlags.of(0), userId);
} catch (NameNotFoundException e) {
- Log.e(TAG, "packageName=" + packageName + ", not found for userId=" + userId, e);
+ Log.w(TAG, "Package: " + packageName + ", not found for user id: " + userId, e);
return false;
}
if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) {
@@ -684,9 +741,9 @@ public final class SafetyCenterService extends SystemService {
}
if (UserHandle.getAppId(callingUid) != UserHandle.getAppId(actualUid)) {
throw new SecurityException(
- "packageName="
+ "Package: "
+ packageName
- + ", does not belong to callingUid="
+ + ", does not belong to calling uid: "
+ callingUid);
}
return true;
@@ -719,9 +776,11 @@ public final class SafetyCenterService extends SystemService {
ParcelFileDescriptor err,
String[] args) {
return new SafetyCenterShellCommandHandler(
- getContext(), this, mDeviceSupportsSafetyCenter)
+ getContext(),
+ /* safetyCenterManager= */ this,
+ mDeviceSupportsSafetyCenter)
.exec(
- this,
+ /* target= */ this,
in.getFileDescriptor(),
out.getFileDescriptor(),
err.getFileDescriptor(),
@@ -793,12 +852,10 @@ public final class SafetyCenterService extends SystemService {
* value maps to {@link SafetyCenterManager#isSafetyCenterEnabled()}. It should only be
* registered if the device supports SafetyCenter and the {@link SafetyCenterConfig} was loaded
* successfully.
- *
- * <p>This listener is not thread-safe; it should be called on a single thread.
*/
- @NotThreadSafe
private final class SafetyCenterEnabledListener implements OnPropertiesChangedListener {
+ @GuardedBy("mApiLock")
private boolean mSafetyCenterEnabled;
@Override
@@ -807,41 +864,64 @@ public final class SafetyCenterService extends SystemService {
return;
}
boolean safetyCenterEnabled =
- properties.getBoolean(PROPERTY_SAFETY_CENTER_ENABLED, false);
- if (mSafetyCenterEnabled == safetyCenterEnabled) {
- return;
+ properties.getBoolean(PROPERTY_SAFETY_CENTER_ENABLED, SdkLevel.isAtLeastU());
+ synchronized (mApiLock) {
+ if (mSafetyCenterEnabled == safetyCenterEnabled) {
+ Log.i(
+ TAG,
+ "Safety Center is already "
+ + (mSafetyCenterEnabled ? "enabled" : "disabled")
+ + ", ignoring change");
+ return;
+ }
+ onSafetyCenterEnabledChangedLocked(safetyCenterEnabled);
}
- onSafetyCenterEnabledChanged(safetyCenterEnabled);
}
- private void setInitialState() {
+ @GuardedBy("mApiLock")
+ private void setInitialStateLocked() {
mSafetyCenterEnabled = SafetyCenterFlags.getSafetyCenterEnabled();
- Log.w(TAG, "SafetyCenter is " + (mSafetyCenterEnabled ? "enabled." : "disabled."));
+ if (mSafetyCenterEnabled) {
+ onApiInitEnabledLocked();
+ }
+ Log.i(TAG, "Safety Center is " + (mSafetyCenterEnabled ? "enabled" : "disabled"));
}
- private void onSafetyCenterEnabledChanged(boolean safetyCenterEnabled) {
- Log.w(TAG, "SafetyCenter is now " + (safetyCenterEnabled ? "enabled." : "disabled."));
-
+ @GuardedBy("mApiLock")
+ private void onSafetyCenterEnabledChangedLocked(boolean safetyCenterEnabled) {
if (safetyCenterEnabled) {
- onApiEnabled();
+ onApiEnabledLocked();
} else {
- onApiDisabled();
+ onApiDisabledLocked();
}
+
mSafetyCenterEnabled = safetyCenterEnabled;
+ Log.i(TAG, "Safety Center is now " + (mSafetyCenterEnabled ? "enabled" : "disabled"));
}
- private void onApiEnabled() {
- synchronized (mApiLock) {
- mSafetyCenterBroadcastDispatcher.sendEnabledChanged();
- }
+ @GuardedBy("mApiLock")
+ private void onApiInitEnabledLocked() {
+ mNotificationChannels.createAllChannelsForAllUsers(getContext());
}
- private void onApiDisabled() {
- synchronized (mApiLock) {
- clearDataLocked();
- mSafetyCenterListeners.clear();
- mSafetyCenterBroadcastDispatcher.sendEnabledChanged();
- }
+ @GuardedBy("mApiLock")
+ private void onApiEnabledLocked() {
+ mNotificationChannels.createAllChannelsForAllUsers(getContext());
+ mSafetyCenterBroadcastDispatcher.sendEnabledChanged();
+ }
+
+ @GuardedBy("mApiLock")
+ private void onApiDisabledLocked() {
+ // We're not clearing the Safety Center notification channels here. The reason for this
+ // is that the NotificationManager will post a runnable to cancel all associated
+ // notifications when clearing the channels. Given this happens asynchronously, this can
+ // leak between test cases and cause notifications that should be active to be cleared
+ // inadvertently. We're ok with the inconsistency because the channels are hidden
+ // somewhat deeply under Settings anyway, and we're unlikely to turn off Safety Center
+ // in production.
+ clearDataLocked();
+ mSafetyCenterListeners.clear();
+ mSafetyCenterBroadcastDispatcher.sendEnabledChanged();
}
}
@@ -870,28 +950,13 @@ public final class SafetyCenterService extends SystemService {
if (stillInFlight == null) {
return;
}
- boolean showErrorEntriesOnTimeout =
- SafetyCenterFlags.getShowErrorEntriesOnTimeout();
- boolean setError =
- showErrorEntriesOnTimeout
- && !RefreshReasons.isBackgroundRefresh(mRefreshReason);
+ boolean setError = !RefreshReasons.isBackgroundRefresh(mRefreshReason);
for (int i = 0; i < stillInFlight.size(); i++) {
mSafetyCenterDataManager.markSafetySourceRefreshTimedOut(
stillInFlight.valueAt(i), setError);
}
mSafetyCenterDataChangeNotifier.updateDataConsumers(mUserProfileGroup);
- if (!showErrorEntriesOnTimeout) {
- mSafetyCenterListeners.deliverErrorForUserProfileGroup(
- mUserProfileGroup,
- new SafetyCenterErrorDetails(
- mSafetyCenterResourcesContext.getStringByName(
- "refresh_timeout")));
- }
}
-
- Log.v(
- TAG,
- "Cleared refresh with broadcastId:" + mRefreshBroadcastId + " after a timeout");
}
@Override
@@ -938,8 +1003,12 @@ public final class SafetyCenterService extends SystemService {
mSafetyCenterListeners.deliverErrorForUserProfileGroup(
mUserProfileGroup,
new SafetyCenterErrorDetails(
- mSafetyCenterResourcesContext.getStringByName(
+ mSafetyCenterResourcesApk.getStringByName(
"resolving_action_error")));
+ Log.w(
+ TAG,
+ "Resolving action timed out for: "
+ + toUserFriendlyString(mSafetyCenterIssueActionId));
}
}
@@ -961,18 +1030,38 @@ public final class SafetyCenterService extends SystemService {
/** {@link BroadcastReceiver} which handles Locale changes. */
private final class LocaleBroadcastReceiver extends BroadcastReceiver {
- private static final String TAG = "LocaleBroadcastReceiver";
+ private static final String TAG = "SafetyCenterLocaleBroad";
void register(Context context) {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- context.registerReceiverForAllUsers(this, filter, null, null);
+ context.registerReceiverForAllUsers(
+ /* receiver= */ this,
+ filter,
+ /* broadcastPermission= */ null,
+ /* scheduler= */ null);
}
@Override
public void onReceive(Context context, Intent intent) {
+ if (!SafetyCenterFlags.getSafetyCenterEnabled()) {
+ Log.i(TAG, "Safety Center is disabled, ignoring intent: " + intent);
+ return;
+ }
+
+ String action = intent.getAction();
+ if (!TextUtils.equals(action, Intent.ACTION_LOCALE_CHANGED)) {
+ Log.w(TAG, "Received unexpected action: " + action);
+ return;
+ }
+
Log.d(TAG, "Locale changed broadcast received");
- mNotificationChannels.createAllChannelsForAllUsers(getContext());
+
+ int userId = ActivityManager.getCurrentUser();
+ synchronized (mApiLock) {
+ startRefreshingSafetySourcesLocked(REFRESH_REASON_DEVICE_LOCALE_CHANGE, userId);
+ mNotificationChannels.createAllChannelsForUser(getContext(), UserHandle.of(userId));
+ }
}
}
@@ -982,62 +1071,98 @@ public final class SafetyCenterService extends SystemService {
*/
private final class UserBroadcastReceiver extends BroadcastReceiver {
- private static final String TAG = "UserBroadcastReceiver";
+ private static final String TAG = "SafetyCenterUserBroadca";
void register(Context context) {
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- context.registerReceiverForAllUsers(this, filter, null, null);
+ context.registerReceiverForAllUsers(
+ /* receiver= */ this,
+ filter,
+ /* broadcastPermission= */ null,
+ /* scheduler= */ null);
}
@Override
public void onReceive(Context context, Intent intent) {
+ if (!SafetyCenterFlags.getSafetyCenterEnabled()) {
+ Log.i(TAG, "Safety Center is disabled, ignoring intent: " + intent);
+ return;
+ }
+
String action = intent.getAction();
if (action == null) {
- Log.w(TAG, "Received broadcast with null action!");
+ Log.w(TAG, "Received broadcast with null action");
return;
}
UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
if (userHandle == null) {
- Log.w(TAG, "Received " + action + " broadcast missing user extra!");
+ Log.w(TAG, "Received action: " + action + ", but missing user extra");
return;
}
int userId = userHandle.getIdentifier();
+ Log.d(TAG, "Received action: " + action + ", for user id: " + userId);
if (!UserProfileGroup.isSupported(userId, context)) {
Log.i(
TAG,
- "Received broadcast for user id "
+ "Received broadcast for user id: "
+ userId
+ ", which is an unsupported user");
return;
}
- Log.d(TAG, "Received " + action + " broadcast for user " + userId);
switch (action) {
case Intent.ACTION_USER_REMOVED:
case Intent.ACTION_MANAGED_PROFILE_REMOVED:
- removeUser(userId, true);
+ removeUserAndData(userId);
break;
case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
- removeUser(userId, false);
- // fall through!
- case Intent.ACTION_USER_ADDED:
+ removeUser(userId);
+ break;
+ case Intent.ACTION_USER_SWITCHED:
+ if (userId != ActivityManager.getCurrentUser()) {
+ Log.w(
+ TAG,
+ "Received broadcast for user id: "
+ + userId
+ + ", which is not the current user");
+ return;
+ }
+ // Fall through
case Intent.ACTION_MANAGED_PROFILE_ADDED:
case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
- startRefreshingSafetySources(REFRESH_REASON_OTHER, userId);
- mNotificationChannels.createAllChannelsForUser(getContext(), userHandle);
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.w(
+ TAG,
+ "Received broadcast for user id: "
+ + userId
+ + ", which does not exist");
+ return;
+ }
+ synchronized (mApiLock) {
+ startRefreshingSafetySourcesLocked(REFRESH_REASON_OTHER, userId);
+ mNotificationChannels.createAllChannelsForUser(getContext(), userHandle);
+ }
break;
}
}
}
+ private void removeUserAndData(@UserIdInt int userId) {
+ removeUser(userId, /* clearDataPermanently= */ true);
+ }
+
+ private void removeUser(@UserIdInt int userId) {
+ removeUser(userId, /* clearDataPermanently= */ false);
+ }
+
private void removeUser(@UserIdInt int userId, boolean clearDataPermanently) {
UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId);
synchronized (mApiLock) {
@@ -1053,31 +1178,44 @@ public final class SafetyCenterService extends SystemService {
}
}
- private void startRefreshingSafetySources(
+ @GuardedBy("mApiLock")
+ private void startRefreshingSafetySourcesLocked(
@RefreshReason int refreshReason, @UserIdInt int userId) {
- startRefreshingSafetySources(refreshReason, userId, null);
+ startRefreshingSafetySourcesLocked(
+ refreshReason,
+ UserProfileGroup.fromUser(getContext(), userId),
+ /* selectedSafetySourceIds= */ null);
}
- private void startRefreshingSafetySources(
+ @GuardedBy("mApiLock")
+ private void startRefreshingSafetySourcesLocked(
@RefreshReason int refreshReason,
@UserIdInt int userId,
+ List<String> selectedSafetySourceIds) {
+ startRefreshingSafetySourcesLocked(
+ refreshReason,
+ UserProfileGroup.fromUser(getContext(), userId),
+ selectedSafetySourceIds);
+ }
+
+ @GuardedBy("mApiLock")
+ private void startRefreshingSafetySourcesLocked(
+ @RefreshReason int refreshReason,
+ UserProfileGroup userProfileGroup,
@Nullable List<String> selectedSafetySourceIds) {
- UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId);
- synchronized (mApiLock) {
- String refreshBroadcastId =
- mSafetyCenterBroadcastDispatcher.sendRefreshSafetySources(
- refreshReason, userProfileGroup, selectedSafetySourceIds);
- if (refreshBroadcastId == null) {
- return;
- }
+ String refreshBroadcastId =
+ mSafetyCenterBroadcastDispatcher.sendRefreshSafetySources(
+ refreshReason, userProfileGroup, selectedSafetySourceIds);
+ if (refreshBroadcastId == null) {
+ return;
+ }
- RefreshTimeout refreshTimeout =
- new RefreshTimeout(refreshBroadcastId, refreshReason, userProfileGroup);
- mSafetyCenterTimeouts.add(
- refreshTimeout, SafetyCenterFlags.getRefreshSourcesTimeout(refreshReason));
+ RefreshTimeout refreshTimeout =
+ new RefreshTimeout(refreshBroadcastId, refreshReason, userProfileGroup);
+ mSafetyCenterTimeouts.add(
+ refreshTimeout, SafetyCenterFlags.getRefreshSourcesTimeout(refreshReason));
- mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroup);
- }
+ mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroup);
}
/**
@@ -1091,7 +1229,7 @@ public final class SafetyCenterService extends SystemService {
safetyCenterIssueActionId.getSafetyCenterIssueKey();
UserProfileGroup userProfileGroup =
UserProfileGroup.fromUser(getContext(), safetyCenterIssueKey.getUserId());
- executeIssueActionInternal(safetyCenterIssueActionId, userProfileGroup, null);
+ executeIssueActionInternal(safetyCenterIssueActionId, userProfileGroup, /* taskId= */ null);
}
private void executeIssueActionInternal(
@@ -1120,16 +1258,19 @@ public final class SafetyCenterService extends SystemService {
CharSequence errorMessage;
if (safetySourceIssueAction.willResolve()) {
errorMessage =
- mSafetyCenterResourcesContext.getStringByName("resolving_action_error");
+ mSafetyCenterResourcesApk.getStringByName("resolving_action_error");
} else {
- errorMessage =
- mSafetyCenterResourcesContext.getStringByName("redirecting_error");
+ errorMessage = mSafetyCenterResourcesApk.getStringByName("redirecting_error");
}
mSafetyCenterListeners.deliverErrorForUserProfileGroup(
userProfileGroup, new SafetyCenterErrorDetails(errorMessage));
return;
}
if (safetySourceIssueAction.willResolve()) {
+ Log.d(
+ TAG,
+ "Starting resolving action for: "
+ + toUserFriendlyString(safetyCenterIssueActionId));
mSafetyCenterDataManager.markSafetyCenterIssueActionInFlight(
safetyCenterIssueActionId);
ResolvingActionTimeout resolvingActionTimeout =
@@ -1141,6 +1282,10 @@ public final class SafetyCenterService extends SystemService {
}
}
+ private boolean dispatchPendingIntent(PendingIntent pendingIntent) {
+ return dispatchPendingIntent(pendingIntent, /* launchTaskId= */ null);
+ }
+
private boolean dispatchPendingIntent(
PendingIntent pendingIntent, @Nullable Integer launchTaskId) {
if (launchTaskId != null
diff --git a/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java b/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java
index 87e3372f7..82983f0bb 100644
--- a/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java
+++ b/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java
@@ -16,7 +16,6 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_LOCALE_CHANGE;
import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_REBOOT;
import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER;
@@ -27,14 +26,13 @@ import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_SAFETY_CEN
import static java.util.Collections.unmodifiableMap;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.os.RemoteException;
import android.safetycenter.ISafetyCenterManager;
import android.safetycenter.SafetyCenterManager.RefreshReason;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.modules.utils.BasicShellCommandHandler;
import com.android.modules.utils.build.SdkLevel;
@@ -48,7 +46,6 @@ import java.util.Map;
*
* <p>Example usage: $ adb shell cmd safety_center refresh --reason PAGE_OPEN --user 10
*/
-@RequiresApi(TIRAMISU)
final class SafetyCenterShellCommandHandler extends BasicShellCommandHandler {
private static final Map<String, Integer> REASONS = createReasonMap();
@@ -89,11 +86,18 @@ final class SafetyCenterShellCommandHandler extends BasicShellCommandHandler {
return handleDefaultCommands(cmd);
}
} catch (RemoteException | IllegalArgumentException e) {
- e.printStackTrace(getErrPrintWriter());
+ printError(e);
return 1;
}
}
+ // We want to log the stack trace on a specific PrintWriter here, this is a false positive as
+ // the warning does not consider the overload that takes a PrintWriter as an argument (yet).
+ @SuppressWarnings("CatchAndPrintStackTrace")
+ private void printError(Throwable error) {
+ error.printStackTrace(getErrPrintWriter());
+ }
+
private int onEnabled() throws RemoteException {
getOutPrintWriter().println(mSafetyCenterManager.isSafetyCenterEnabled());
return 0;
diff --git a/service/java/com/android/safetycenter/SafetyCenterTimeouts.java b/service/java/com/android/safetycenter/SafetyCenterTimeouts.java
index f8bfd691e..b37951fb1 100644
--- a/service/java/com/android/safetycenter/SafetyCenterTimeouts.java
+++ b/service/java/com/android/safetycenter/SafetyCenterTimeouts.java
@@ -16,12 +16,8 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import android.os.Handler;
-import androidx.annotation.RequiresApi;
-
import com.android.permission.util.ForegroundThread;
import java.io.PrintWriter;
@@ -36,7 +32,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* <p>This class isn't thread safe. Thread safety must be handled by the caller.
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetyCenterTimeouts {
diff --git a/service/java/com/android/safetycenter/SafetySourceIssueInfo.java b/service/java/com/android/safetycenter/SafetySourceIssueInfo.java
index 51e6567d7..0dfa7c814 100644
--- a/service/java/com/android/safetycenter/SafetySourceIssueInfo.java
+++ b/service/java/com/android/safetycenter/SafetySourceIssueInfo.java
@@ -16,8 +16,6 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriendlyString;
import android.annotation.UserIdInt;
@@ -25,8 +23,6 @@ import android.safetycenter.SafetySourceIssue;
import android.safetycenter.config.SafetySource;
import android.safetycenter.config.SafetySourcesGroup;
-import androidx.annotation.RequiresApi;
-
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import java.util.Objects;
@@ -36,7 +32,6 @@ import java.util.Objects;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetySourceIssueInfo {
private final SafetySourceIssue mSafetySourceIssue;
@@ -65,6 +60,7 @@ public final class SafetySourceIssueInfo {
public SafetyCenterIssueKey getSafetyCenterIssueKey() {
return mSafetyCenterIssueKey;
}
+
/** Returns the {@link SafetySourceIssue}. */
public SafetySourceIssue getSafetySourceIssue() {
return mSafetySourceIssue;
diff --git a/service/java/com/android/safetycenter/SafetySourceIssues.java b/service/java/com/android/safetycenter/SafetySourceIssues.java
new file mode 100644
index 000000000..dc3c2a83e
--- /dev/null
+++ b/service/java/com/android/safetycenter/SafetySourceIssues.java
@@ -0,0 +1,87 @@
+/*
+ * 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.safetycenter;
+
+import android.safetycenter.SafetySourceIssue;
+
+import androidx.annotation.Nullable;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import java.util.List;
+
+/**
+ * A helper class to facilitate working with {@link SafetySourceIssue} objects.
+ *
+ * @hide
+ */
+public final class SafetySourceIssues {
+
+ /**
+ * Returns the {@link SafetySourceIssue.Action} with the given action ID belonging to the given
+ * {@link SafetySourceIssue} or {@code null} if no such action is present.
+ *
+ * <p>The action will either belong to the issue directly from {@link
+ * SafetySourceIssue#getActions()} or via {@link SafetySourceIssue#getCustomNotification()} if
+ * the issue has a custom notification.
+ */
+ @Nullable
+ public static SafetySourceIssue.Action findAction(SafetySourceIssue issue, String actionId) {
+ SafetySourceIssue.Action action = null;
+ if (SdkLevel.isAtLeastU() && issue.getCustomNotification() != null) {
+ action = findAction(issue.getCustomNotification().getActions(), actionId);
+ }
+ if (action == null) {
+ action = findAction(issue.getActions(), actionId);
+ }
+ return action;
+ }
+
+ @Nullable
+ private static SafetySourceIssue.Action findAction(
+ List<SafetySourceIssue.Action> actions, String actionId) {
+ for (int i = 0; i < actions.size(); i++) {
+ SafetySourceIssue.Action action = actions.get(i);
+ if (action.getId().equals(actionId)) {
+ return action;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns {@code true} if {@code actionId} corresponds to a "primary" action of the given
+ * {@code issue}, or {@code false} if the action is not the primary or if no action with the
+ * given ID is found.
+ *
+ * <p>A primary action is the first action of either the issue, or its custom notification.
+ */
+ public static boolean isPrimaryAction(SafetySourceIssue issue, String actionId) {
+ boolean isPrimaryNotificationAction =
+ SdkLevel.isAtLeastU()
+ && issue.getCustomNotification() != null
+ && matchesFirst(issue.getCustomNotification().getActions(), actionId);
+ boolean isPrimaryIssueAction = matchesFirst(issue.getActions(), actionId);
+ return isPrimaryNotificationAction || isPrimaryIssueAction;
+ }
+
+ private static boolean matchesFirst(List<SafetySourceIssue.Action> actions, String actionId) {
+ return !actions.isEmpty() && actions.get(0).getId().equals(actionId);
+ }
+
+ private SafetySourceIssues() {}
+}
diff --git a/service/java/com/android/safetycenter/SafetySourceKey.java b/service/java/com/android/safetycenter/SafetySourceKey.java
index 511fbef73..9e1400e30 100644
--- a/service/java/com/android/safetycenter/SafetySourceKey.java
+++ b/service/java/com/android/safetycenter/SafetySourceKey.java
@@ -16,13 +16,9 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import android.annotation.UserIdInt;
import android.safetycenter.SafetySourceData;
-import androidx.annotation.RequiresApi;
-
import java.util.Objects;
/**
@@ -32,7 +28,7 @@ import java.util.Objects;
* @hide
*/
// TODO(b/219697341): Look into using AutoValue for this data class.
-@RequiresApi(TIRAMISU)
+
public final class SafetySourceKey {
private final String mSourceId;
diff --git a/service/java/com/android/safetycenter/SafetySources.java b/service/java/com/android/safetycenter/SafetySources.java
index c0b0bdc48..02d83d27b 100644
--- a/service/java/com/android/safetycenter/SafetySources.java
+++ b/service/java/com/android/safetycenter/SafetySources.java
@@ -16,20 +16,15 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import android.safetycenter.SafetySourceData;
import android.safetycenter.config.SafetySource;
import android.util.Log;
-import androidx.annotation.RequiresApi;
-
/**
* A helper class to facilitate working with {@link SafetySource} objects.
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetySources {
private static final String TAG = "SafetySources";
diff --git a/service/java/com/android/safetycenter/SafetySourcesGroups.java b/service/java/com/android/safetycenter/SafetySourcesGroups.java
index 5233302aa..a86eccada 100644
--- a/service/java/com/android/safetycenter/SafetySourcesGroups.java
+++ b/service/java/com/android/safetycenter/SafetySourcesGroups.java
@@ -16,16 +16,11 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import android.safetycenter.config.SafetySourcesGroup;
-import androidx.annotation.RequiresApi;
-
import com.android.modules.utils.build.SdkLevel;
/** Static utilities for working with {@link SafetySourcesGroup} objects. */
-@RequiresApi(TIRAMISU)
final class SafetySourcesGroups {
/**
diff --git a/service/java/com/android/safetycenter/UserProfileGroup.java b/service/java/com/android/safetycenter/UserProfileGroup.java
index 8d3adc573..74b9b136f 100644
--- a/service/java/com/android/safetycenter/UserProfileGroup.java
+++ b/service/java/com/android/safetycenter/UserProfileGroup.java
@@ -16,11 +16,8 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static java.util.Objects.requireNonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -30,7 +27,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.permission.util.UserUtils;
@@ -45,7 +42,6 @@ import java.util.Objects;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class UserProfileGroup {
private static final String TAG = "UserProfileGroup";
@@ -141,9 +137,9 @@ public final class UserProfileGroup {
Arrays.copyOf(
managedRunningProfilesUserIds, managedRunningProfilesUserIdsLen));
if (!userProfileGroup.contains(userId)) {
- Log.w(
+ Log.i(
TAG,
- "User id " + userId + " does not belong to " + userProfileGroup,
+ "User id: " + userId + " does not belong to: " + userProfileGroup,
new Exception());
}
return userProfileGroup;
@@ -167,7 +163,8 @@ public final class UserProfileGroup {
return context;
} else {
try {
- return context.createPackageContextAsUser(context.getPackageName(), 0, userHandle);
+ return context.createPackageContextAsUser(
+ context.getPackageName(), /* flags= */ 0, userHandle);
} catch (PackageManager.NameNotFoundException doesNotHappen) {
throw new IllegalStateException(doesNotHappen);
}
@@ -232,9 +229,9 @@ public final class UserProfileGroup {
profileParentAndManagedRunningProfilesUserIds[0] = mProfileParentUserId;
System.arraycopy(
mManagedRunningProfilesUserIds,
- 0,
+ /* srcPos= */ 0,
profileParentAndManagedRunningProfilesUserIds,
- 1,
+ /* destPos= */ 1,
mManagedRunningProfilesUserIds.length);
return profileParentAndManagedRunningProfilesUserIds;
}
diff --git a/service/java/com/android/safetycenter/data/AndroidLockScreenFix.java b/service/java/com/android/safetycenter/data/AndroidLockScreenFix.java
index 5db3cfbad..53043c0f8 100644
--- a/service/java/com/android/safetycenter/data/AndroidLockScreenFix.java
+++ b/service/java/com/android/safetycenter/data/AndroidLockScreenFix.java
@@ -16,9 +16,6 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
-import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -30,8 +27,6 @@ import android.safetycenter.SafetySourceIssue;
import android.safetycenter.SafetySourceStatus;
import android.util.Log;
-import androidx.annotation.RequiresApi;
-
import com.android.modules.utils.build.SdkLevel;
import com.android.safetycenter.PendingIntentFactory;
import com.android.safetycenter.SafetyCenterFlags;
@@ -42,7 +37,6 @@ import java.util.List;
* A class to work around an issue with the {@code AndroidLockScreen} safety source, by potentially
* overriding its {@link SafetySourceData}.
*/
-@RequiresApi(TIRAMISU)
final class AndroidLockScreenFix {
private static final String TAG = "AndroidLockScreenFix";
@@ -57,9 +51,22 @@ final class AndroidLockScreenFix {
private AndroidLockScreenFix() {}
+ static boolean shouldApplyFix(String sourceId) {
+ if (SdkLevel.isAtLeastU()) {
+ // No need to override on U+ as the issue has been fixed in a T QPR release.
+ // As such, U+ fields for the SafetySourceData are not taken into account in the methods
+ // below.
+ return false;
+ }
+ if (!ANDROID_LOCK_SCREEN_SOURCE_ID.equals(sourceId)) {
+ return false;
+ }
+ return SafetyCenterFlags.getReplaceLockScreenIconAction();
+ }
+
/**
- * Potentially overrides the {@link SafetySourceData} of the {@code AndroidLockScreen} source by
- * replacing its {@link PendingIntent}s.
+ * Overrides the {@link SafetySourceData} of the {@code AndroidLockScreen} source by replacing
+ * its {@link PendingIntent}s.
*
* <p>This is done because of a bug in the Settings app where the {@link PendingIntent}s created
* end up referencing either the {@link SafetyCenterEntry#getPendingIntent()} or the {@link
@@ -72,109 +79,76 @@ final class AndroidLockScreenFix {
* different request codes for the different {@link PendingIntent}s to ensure new instances are
* created (the key does take into account the request code).
*/
- @Nullable
- static SafetySourceData maybeOverrideSafetySourceData(
- Context context, String sourceId, @Nullable SafetySourceData safetySourceData) {
- if (safetySourceData == null) {
- return null;
- }
- if (SdkLevel.isAtLeastU()) {
- // No need to override on U+ as the issue has been fixed in a T QPR release.
- // As such, U+ fields for the SafetySourceData are not taken into account in the methods
- // below.
- return safetySourceData;
- }
- if (!ANDROID_LOCK_SCREEN_SOURCE_ID.equals(sourceId)) {
- return safetySourceData;
- }
- if (!SafetyCenterFlags.getReplaceLockScreenIconAction()) {
- return safetySourceData;
- }
- return overrideTiramisuSafetySourceData(context, safetySourceData);
- }
+ static SafetySourceData applyFix(Context context, SafetySourceData data) {
+ SafetySourceData.Builder overriddenData =
+ SafetySourceDataOverrides.copyDataToBuilderWithoutIssues(data);
- private static SafetySourceData overrideTiramisuSafetySourceData(
- Context context, SafetySourceData safetySourceData) {
- SafetySourceData.Builder overriddenSafetySourceData = new SafetySourceData.Builder();
- SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
- if (safetySourceStatus != null) {
- overriddenSafetySourceData.setStatus(
- overrideTiramisuSafetySourceStatus(context, safetySourceStatus));
+ SafetySourceStatus originalStatus = data.getStatus();
+ if (originalStatus != null) {
+ overriddenData.setStatus(overrideTiramisuSafetySourceStatus(context, originalStatus));
}
- List<SafetySourceIssue> safetySourceIssues = safetySourceData.getIssues();
- for (int i = 0; i < safetySourceIssues.size(); i++) {
- SafetySourceIssue safetySourceIssue = safetySourceIssues.get(i);
- overriddenSafetySourceData.addIssue(
- overrideTiramisuSafetySourceIssue(context, safetySourceIssue));
+
+ List<SafetySourceIssue> issues = data.getIssues();
+ for (int i = 0; i < issues.size(); i++) {
+ overriddenData.addIssue(overrideTiramisuIssue(context, issues.get(i)));
}
- return overriddenSafetySourceData.build();
+
+ return overriddenData.build();
}
private static SafetySourceStatus overrideTiramisuSafetySourceStatus(
- Context context, SafetySourceStatus safetySourceStatus) {
- SafetySourceStatus.Builder overriddenSafetySourceStatus =
- new SafetySourceStatus.Builder(
- safetySourceStatus.getTitle(),
- safetySourceStatus.getSummary(),
- safetySourceStatus.getSeverityLevel())
- .setPendingIntent(
- overridePendingIntent(
- context, safetySourceStatus.getPendingIntent(), false))
- .setEnabled(safetySourceStatus.isEnabled());
- SafetySourceStatus.IconAction iconAction = safetySourceStatus.getIconAction();
+ Context context, SafetySourceStatus status) {
+ SafetySourceStatus.Builder overriddenStatus =
+ SafetySourceDataOverrides.copyStatusToBuilder(status);
+
+ PendingIntent originalPendingIntent = status.getPendingIntent();
+ if (originalPendingIntent != null) {
+ overriddenStatus.setPendingIntent(
+ overridePendingIntent(
+ context, originalPendingIntent, /* isIconAction= */ false));
+ }
+
+ SafetySourceStatus.IconAction iconAction = status.getIconAction();
if (iconAction != null) {
- overriddenSafetySourceStatus.setIconAction(
- overrideTiramisuSafetySourceStatusIconAction(
- context, safetySourceStatus.getIconAction()));
+ overriddenStatus.setIconAction(
+ overrideTiramisuIconAction(context, status.getIconAction()));
}
- return overriddenSafetySourceStatus.build();
+
+ return overriddenStatus.build();
}
- private static SafetySourceStatus.IconAction overrideTiramisuSafetySourceStatusIconAction(
+ private static SafetySourceStatus.IconAction overrideTiramisuIconAction(
Context context, SafetySourceStatus.IconAction iconAction) {
return new SafetySourceStatus.IconAction(
iconAction.getIconType(),
- overridePendingIntent(context, iconAction.getPendingIntent(), true));
+ overridePendingIntent(
+ context, iconAction.getPendingIntent(), /* isIconAction= */ true));
}
- private static SafetySourceIssue overrideTiramisuSafetySourceIssue(
- Context context, SafetySourceIssue safetySourceIssue) {
- SafetySourceIssue.Builder overriddenSafetySourceIssue =
- new SafetySourceIssue.Builder(
- safetySourceIssue.getId(),
- safetySourceIssue.getTitle(),
- safetySourceIssue.getSummary(),
- safetySourceIssue.getSeverityLevel(),
- safetySourceIssue.getIssueTypeId())
- .setSubtitle(safetySourceIssue.getSubtitle())
- .setIssueCategory(safetySourceIssue.getIssueCategory())
- .setOnDismissPendingIntent(safetySourceIssue.getOnDismissPendingIntent());
- List<SafetySourceIssue.Action> actions = safetySourceIssue.getActions();
+ private static SafetySourceIssue overrideTiramisuIssue(
+ Context context, SafetySourceIssue issue) {
+ SafetySourceIssue.Builder overriddenIssue =
+ SafetySourceDataOverrides.copyIssueToBuilderWithoutActions(issue);
+
+ List<SafetySourceIssue.Action> actions = issue.getActions();
for (int i = 0; i < actions.size(); i++) {
SafetySourceIssue.Action action = actions.get(i);
- overriddenSafetySourceIssue.addAction(
- overrideTiramisuSafetySourceIssueAction(context, action));
+ overriddenIssue.addAction(overrideTiramisuIssueAction(context, action));
}
- return overriddenSafetySourceIssue.build();
+
+ return overriddenIssue.build();
}
- private static SafetySourceIssue.Action overrideTiramisuSafetySourceIssueAction(
+ private static SafetySourceIssue.Action overrideTiramisuIssueAction(
Context context, SafetySourceIssue.Action action) {
- return new SafetySourceIssue.Action.Builder(
- action.getId(),
- action.getLabel(),
- overridePendingIntent(context, action.getPendingIntent(), false))
- .setWillResolve(action.willResolve())
- .setSuccessMessage(action.getSuccessMessage())
- .build();
+ PendingIntent pendingIntent =
+ overridePendingIntent(
+ context, action.getPendingIntent(), /* isIconAction= */ false);
+ return SafetySourceDataOverrides.overrideActionPendingIntent(action, pendingIntent);
}
- @Nullable
private static PendingIntent overridePendingIntent(
- Context context, @Nullable PendingIntent pendingIntent, boolean isIconAction) {
- if (pendingIntent == null) {
- return null;
- }
+ Context context, PendingIntent pendingIntent, boolean isIconAction) {
String settingsPackageName = pendingIntent.getCreatorPackage();
int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
Context settingsPackageContext =
@@ -206,7 +180,7 @@ final class AndroidLockScreenFix {
// This is important because there are scenarios where the Settings app provides different
// pending intents (e.g. in the work profile), and in this case we shouldn't override them.
if (isIconAction) {
- Log.w(
+ Log.i(
TAG,
"Replacing " + ANDROID_LOCK_SCREEN_SOURCE_ID + " icon action pending intent");
return PendingIntentFactory.getActivityPendingIntent(
@@ -215,7 +189,7 @@ final class AndroidLockScreenFix {
newLockScreenIconActionIntent(settingsPackageName),
PendingIntent.FLAG_IMMUTABLE);
}
- Log.w(TAG, "Replacing " + ANDROID_LOCK_SCREEN_SOURCE_ID + " entry or issue pending intent");
+ Log.i(TAG, "Replacing " + ANDROID_LOCK_SCREEN_SOURCE_ID + " entry or issue pending intent");
return PendingIntentFactory.getActivityPendingIntent(
settingsPackageContext,
ANDROID_LOCK_SCREEN_ENTRY_REQ_CODE,
diff --git a/service/java/com/android/safetycenter/data/DefaultActionOverrideFix.java b/service/java/com/android/safetycenter/data/DefaultActionOverrideFix.java
new file mode 100644
index 000000000..9ca188670
--- /dev/null
+++ b/service/java/com/android/safetycenter/data/DefaultActionOverrideFix.java
@@ -0,0 +1,180 @@
+/*
+ * 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.safetycenter.data;
+
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+import android.annotation.UserIdInt;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.safetycenter.SafetySourceData;
+import android.safetycenter.SafetySourceIssue;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+import com.android.modules.utils.build.SdkLevel;
+import com.android.permission.util.UserUtils;
+import com.android.safetycenter.PendingIntentFactory;
+import com.android.safetycenter.SafetyCenterConfigReader;
+import com.android.safetycenter.SafetyCenterFlags;
+
+import java.util.List;
+
+/**
+ * Replaces {@link SafetySourceIssue.Action}s with the corresponding source's default intent drawn
+ * from the Safety Center config.
+ *
+ * <p>Actions to be replaced are controlled by the {@code
+ * safety_center_actions_to_override_with_default_intent} DeviceConfig flag.
+ *
+ * <p>This is done to support cases where we allow OEMs to override intents in the config, but
+ * sources are unaware of and unable to access those overrides when providing issues and
+ * notifications. We use the default intent when sources provide a null pending intent in their
+ * status. This fix allows us to implement a similar behavior for actions, without changing the
+ * non-null requirement on their pending intent fields.
+ */
+final class DefaultActionOverrideFix {
+
+ private final Context mContext;
+ private final PendingIntentFactory mPendingIntentFactory;
+ private final SafetyCenterConfigReader mSafetyCenterConfigReader;
+
+ DefaultActionOverrideFix(
+ Context context,
+ PendingIntentFactory pendingIntentFactory,
+ SafetyCenterConfigReader safetyCenterConfigReader) {
+ mContext = context;
+ mPendingIntentFactory = pendingIntentFactory;
+ mSafetyCenterConfigReader = safetyCenterConfigReader;
+ }
+
+ static boolean shouldApplyFix(String sourceId) {
+ List<String> actionsToOverride =
+ SafetyCenterFlags.getActionsToOverrideWithDefaultIntentForSource(sourceId);
+ return !actionsToOverride.isEmpty();
+ }
+
+ SafetySourceData applyFix(
+ String sourceId,
+ SafetySourceData safetySourceData,
+ String packageName,
+ @UserIdInt int userId) {
+ if (safetySourceData.getIssues().isEmpty()) {
+ return safetySourceData;
+ }
+
+ PendingIntent defaultIntentForSource =
+ getDefaultIntentForSource(sourceId, packageName, userId);
+ if (defaultIntentForSource == null) {
+ // If there's no default intent, we can't override any actions with it.
+ return safetySourceData;
+ }
+
+ List<String> actionsToOverride =
+ SafetyCenterFlags.getActionsToOverrideWithDefaultIntentForSource(sourceId);
+ if (actionsToOverride.isEmpty()) {
+ // This shouldn't happen if shouldApplyFix is called first, but we check for good
+ // measure.
+ return safetySourceData;
+ }
+
+ SafetySourceData.Builder overriddenSafetySourceData =
+ SafetySourceDataOverrides.copyDataToBuilderWithoutIssues(safetySourceData);
+ List<SafetySourceIssue> issues = safetySourceData.getIssues();
+ for (int i = 0; i < issues.size(); i++) {
+ overriddenSafetySourceData.addIssue(
+ maybeOverrideActionsWithDefaultIntent(
+ issues.get(i), actionsToOverride, defaultIntentForSource));
+ }
+
+ return overriddenSafetySourceData.build();
+ }
+
+ @Nullable
+ private PendingIntent getDefaultIntentForSource(
+ String sourceId, String packageName, @UserIdInt int userId) {
+ SafetyCenterConfigReader.ExternalSafetySource externalSafetySource =
+ mSafetyCenterConfigReader.getExternalSafetySource(sourceId, packageName);
+ if (externalSafetySource == null) {
+ return null;
+ }
+
+ boolean isQuietModeEnabled =
+ UserUtils.isManagedProfile(userId, mContext)
+ && !UserUtils.isProfileRunning(userId, mContext);
+
+ return mPendingIntentFactory.getPendingIntent(
+ sourceId,
+ externalSafetySource.getSafetySource().getIntentAction(),
+ packageName,
+ userId,
+ isQuietModeEnabled);
+ }
+
+ private SafetySourceIssue maybeOverrideActionsWithDefaultIntent(
+ SafetySourceIssue issue, List<String> actionsToOverride, PendingIntent defaultIntent) {
+ SafetySourceIssue.Builder overriddenIssue =
+ SafetySourceDataOverrides.copyIssueToBuilderWithoutActions(issue);
+
+ List<SafetySourceIssue.Action> actions = issue.getActions();
+ for (int i = 0; i < actions.size(); i++) {
+ overriddenIssue.addAction(
+ maybeOverrideAction(actions.get(i), actionsToOverride, defaultIntent));
+ }
+
+ if (SdkLevel.isAtLeastU()) {
+ overriddenIssue.setCustomNotification(
+ maybeOverrideNotification(
+ issue.getCustomNotification(), actionsToOverride, defaultIntent));
+ }
+
+ return overriddenIssue.build();
+ }
+
+ @RequiresApi(UPSIDE_DOWN_CAKE)
+ @Nullable
+ private static SafetySourceIssue.Notification maybeOverrideNotification(
+ @Nullable SafetySourceIssue.Notification notification,
+ List<String> actionsToOverride,
+ PendingIntent defaultIntent) {
+ if (notification == null) {
+ return null;
+ }
+
+ SafetySourceIssue.Notification.Builder overriddenNotification =
+ new SafetySourceIssue.Notification.Builder(notification).clearActions();
+
+ List<SafetySourceIssue.Action> actions = notification.getActions();
+ for (int i = 0; i < actions.size(); i++) {
+ overriddenNotification.addAction(
+ maybeOverrideAction(actions.get(i), actionsToOverride, defaultIntent));
+ }
+
+ return overriddenNotification.build();
+ }
+
+ private static SafetySourceIssue.Action maybeOverrideAction(
+ SafetySourceIssue.Action action,
+ List<String> actionsToOverride,
+ PendingIntent defaultIntent) {
+ if (actionsToOverride.contains(action.getId())) {
+ return SafetySourceDataOverrides.overrideActionPendingIntent(action, defaultIntent);
+ }
+ return action;
+ }
+}
diff --git a/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java b/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java
index 734732401..018fedf41 100644
--- a/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java
+++ b/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java
@@ -16,11 +16,8 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static com.android.safetycenter.logging.SafetyCenterStatsdLogger.toSystemEventResult;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.safetycenter.SafetyCenterData;
@@ -30,9 +27,10 @@ import android.safetycenter.SafetySourceErrorDetails;
import android.safetycenter.SafetySourceIssue;
import android.safetycenter.config.SafetyCenterConfig;
import android.safetycenter.config.SafetySourcesGroup;
+import android.util.ArraySet;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.safetycenter.ApiLock;
import com.android.safetycenter.SafetyCenterConfigReader;
@@ -60,7 +58,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterDataManager {
@@ -88,7 +85,6 @@ public final class SafetyCenterDataManager {
new SafetyCenterIssueDismissalRepository(apiLock, safetyCenterConfigReader);
mSafetySourceDataRepository =
new SafetySourceDataRepository(
- context,
mSafetyCenterInFlightIssueActionRepository,
mSafetyCenterIssueDismissalRepository);
mSafetyCenterIssueRepository =
@@ -137,7 +133,7 @@ public final class SafetyCenterDataManager {
safetySourceData, safetySourceId, packageName, userId)) {
return false;
}
- SafetySourceKey key = SafetySourceKey.of(safetySourceId, userId);
+ SafetySourceKey safetySourceKey = SafetySourceKey.of(safetySourceId, userId);
// Must fetch refresh reason before calling processSafetyEvent because the latter may
// complete and clear the current refresh.
@@ -147,11 +143,15 @@ public final class SafetyCenterDataManager {
refreshReason = mSafetyCenterRefreshTracker.getRefreshReason();
}
- boolean sourceDataDiffers =
- mSafetySourceDataRepository.setSafetySourceData(
- safetySourceData, safetySourceId, userId);
+ // It is important to process the event first as it relies on the data available prior to
+ // changing it.
+ boolean sourceDataWillChange =
+ !mSafetySourceDataRepository.sourceHasData(safetySourceKey, safetySourceData);
boolean eventCausedChange =
- processSafetyEvent(safetySourceId, safetyEvent, userId, false, sourceDataDiffers);
+ processSafetyEvent(
+ safetySourceKey, safetyEvent, /* isError= */ false, sourceDataWillChange);
+ boolean sourceDataDiffers =
+ mSafetySourceDataRepository.setSafetySourceData(safetySourceKey, safetySourceData);
boolean safetyCenterDataChanged = sourceDataDiffers || eventCausedChange;
if (safetyCenterDataChanged) {
@@ -159,7 +159,12 @@ public final class SafetyCenterDataManager {
}
mSafetySourceStateCollectedLogger.writeSourceUpdatedAtom(
- key, safetySourceData, refreshReason, sourceDataDiffers, userId, safetyEvent);
+ safetySourceKey,
+ safetySourceData,
+ refreshReason,
+ sourceDataDiffers,
+ userId,
+ safetyEvent);
return safetyCenterDataChanged;
}
@@ -199,11 +204,11 @@ public final class SafetyCenterDataManager {
String packageName,
@UserIdInt int userId) {
if (!mSafetySourceDataValidator.validateRequest(
- null, safetySourceId, packageName, userId)) {
+ /* safetySourceData= */ null, safetySourceId, packageName, userId)) {
return false;
}
SafetyEvent safetyEvent = safetySourceErrorDetails.getSafetyEvent();
- SafetySourceKey key = SafetySourceKey.of(safetySourceId, userId);
+ SafetySourceKey safetySourceKey = SafetySourceKey.of(safetySourceId, userId);
// Must fetch refresh reason before calling processSafetyEvent because the latter may
// complete and clear the current refresh.
@@ -213,11 +218,15 @@ public final class SafetyCenterDataManager {
refreshReason = mSafetyCenterRefreshTracker.getRefreshReason();
}
+ // It is important to process the event first as it relies on the data available prior to
+ // changing it.
+ boolean sourceDataWillChange = !mSafetySourceDataRepository.sourceHasError(safetySourceKey);
+ boolean eventCausedChange =
+ processSafetyEvent(
+ safetySourceKey, safetyEvent, /* isError= */ true, sourceDataWillChange);
boolean sourceDataDiffers =
mSafetySourceDataRepository.reportSafetySourceError(
- safetySourceErrorDetails, safetySourceId, userId);
- boolean eventCausedChange =
- processSafetyEvent(safetySourceId, safetyEvent, userId, true, sourceDataDiffers);
+ safetySourceKey, safetySourceErrorDetails);
boolean safetyCenterDataChanged = sourceDataDiffers || eventCausedChange;
if (safetyCenterDataChanged) {
@@ -225,7 +234,12 @@ public final class SafetyCenterDataManager {
}
mSafetySourceStateCollectedLogger.writeSourceUpdatedAtom(
- key, null, refreshReason, sourceDataDiffers, userId, safetyEvent);
+ safetySourceKey,
+ /* safetySourceData= */ null,
+ refreshReason,
+ sourceDataDiffers,
+ userId,
+ safetyEvent);
return safetyCenterDataChanged;
}
@@ -389,6 +403,11 @@ public final class SafetyCenterDataManager {
safetyCenterIssueActionId);
}
+ /** Returns a list of IDs of in-flight actions for the given source and user */
+ ArraySet<SafetyCenterIssueActionId> getInFlightActions(String sourceId, @UserIdInt int userId) {
+ return mSafetyCenterInFlightIssueActionRepository.getInFlightActions(sourceId, userId);
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
////////////////////// SafetySourceDataRepository ///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -407,7 +426,7 @@ public final class SafetyCenterDataManager {
public SafetySourceData getSafetySourceData(
String safetySourceId, String packageName, @UserIdInt int userId) {
if (!mSafetySourceDataValidator.validateRequest(
- null, safetySourceId, packageName, userId)) {
+ /* safetySourceData= */ null, safetySourceId, packageName, userId)) {
return null;
}
return mSafetySourceDataRepository.getSafetySourceData(
@@ -472,38 +491,37 @@ public final class SafetyCenterDataManager {
}
private boolean processSafetyEvent(
- String safetySourceId,
+ SafetySourceKey safetySourceKey,
SafetyEvent safetyEvent,
- @UserIdInt int userId,
boolean isError,
- boolean sourceDataChanged) {
+ boolean sourceDataWillChange) {
int type = safetyEvent.getType();
switch (type) {
case SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED:
String refreshBroadcastId = safetyEvent.getRefreshBroadcastId();
if (refreshBroadcastId == null) {
- Log.w(TAG, "No refresh broadcast id in SafetyEvent of type " + type);
+ Log.w(TAG, "No refresh broadcast id in SafetyEvent of type: " + type);
return false;
}
return mSafetyCenterRefreshTracker.reportSourceRefreshCompleted(
- refreshBroadcastId, safetySourceId, userId, !isError, sourceDataChanged);
+ refreshBroadcastId, safetySourceKey, !isError, sourceDataWillChange);
case SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED:
case SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED:
String safetySourceIssueId = safetyEvent.getSafetySourceIssueId();
if (safetySourceIssueId == null) {
- Log.w(TAG, "No safety source issue id in SafetyEvent of type " + type);
+ Log.w(TAG, "No safety source issue id in SafetyEvent of type: " + type);
return false;
}
String safetySourceIssueActionId = safetyEvent.getSafetySourceIssueActionId();
if (safetySourceIssueActionId == null) {
- Log.w(TAG, "No safety source issue action id in SafetyEvent of type " + type);
+ Log.w(TAG, "No safety source issue action id in SafetyEvent of type: " + type);
return false;
}
SafetyCenterIssueKey safetyCenterIssueKey =
SafetyCenterIssueKey.newBuilder()
- .setSafetySourceId(safetySourceId)
+ .setSafetySourceId(safetySourceKey.getSourceId())
.setSafetySourceIssueId(safetySourceIssueId)
- .setUserId(userId)
+ .setUserId(safetySourceKey.getUserId())
.build();
SafetyCenterIssueActionId safetyCenterIssueActionId =
SafetyCenterIssueActionId.newBuilder()
diff --git a/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java b/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java
index 4fa6e5363..82eb3a6c7 100644
--- a/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java
+++ b/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java
@@ -16,33 +16,30 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriendlyString;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.os.SystemClock;
import android.safetycenter.SafetySourceIssue;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.permission.util.UserUtils;
+import com.android.safetycenter.SafetySourceIssues;
import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
import java.io.PrintWriter;
import java.time.Duration;
-import java.util.List;
import javax.annotation.concurrent.NotThreadSafe;
/** Maintains data about in-flight issue actions. */
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetyCenterInFlightIssueActionRepository {
@@ -113,6 +110,19 @@ final class SafetyCenterInFlightIssueActionRepository {
return mSafetyCenterIssueActionsInFlight.containsKey(safetyCenterIssueActionId);
}
+ /** Returns a list of IDs of in-flight actions for the given source and user */
+ ArraySet<SafetyCenterIssueActionId> getInFlightActions(String sourceId, @UserIdInt int userId) {
+ ArraySet<SafetyCenterIssueActionId> result = new ArraySet<>();
+ for (int i = 0; i < mSafetyCenterIssueActionsInFlight.size(); i++) {
+ SafetyCenterIssueActionId actionId = mSafetyCenterIssueActionsInFlight.keyAt(i);
+ SafetyCenterIssueKey issueKey = actionId.getSafetyCenterIssueKey();
+ if (sourceId.equals(issueKey.getSafetySourceId()) && issueKey.getUserId() == userId) {
+ result.add(actionId);
+ }
+ }
+ return result;
+ }
+
/**
* Returns {@link SafetySourceIssue.Action} identified by the given {@link
* SafetyCenterIssueActionId} and {@link SafetySourceIssue}.
@@ -125,18 +135,8 @@ final class SafetyCenterInFlightIssueActionRepository {
return null;
}
- List<SafetySourceIssue.Action> safetySourceIssueActions = safetySourceIssue.getActions();
- for (int i = 0; i < safetySourceIssueActions.size(); i++) {
- SafetySourceIssue.Action safetySourceIssueAction = safetySourceIssueActions.get(i);
-
- if (safetyCenterIssueActionId
- .getSafetySourceIssueActionId()
- .equals(safetySourceIssueAction.getId())) {
- return safetySourceIssueAction;
- }
- }
-
- return null;
+ return SafetySourceIssues.findAction(
+ safetySourceIssue, safetyCenterIssueActionId.getSafetySourceIssueActionId());
}
/** Dumps in-flight action data for debugging purposes. */
diff --git a/service/java/com/android/safetycenter/data/SafetyCenterIssueDeduplicator.java b/service/java/com/android/safetycenter/data/SafetyCenterIssueDeduplicator.java
index d5218a0aa..1a86640dd 100644
--- a/service/java/com/android/safetycenter/data/SafetyCenterIssueDeduplicator.java
+++ b/service/java/com/android/safetycenter/data/SafetyCenterIssueDeduplicator.java
@@ -16,7 +16,6 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriendlyString;
@@ -27,13 +26,13 @@ import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.safetycenter.config.SafetySourcesGroup;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.safetycenter.SafetySourceIssueInfo;
@@ -48,7 +47,6 @@ import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
/** Deduplicates issues based on deduplication info provided by the source and the issue. */
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetyCenterIssueDeduplicator {
@@ -56,7 +54,6 @@ final class SafetyCenterIssueDeduplicator {
private final SafetyCenterIssueDismissalRepository mSafetyCenterIssueDismissalRepository;
- @RequiresApi(TIRAMISU)
SafetyCenterIssueDeduplicator(
SafetyCenterIssueDismissalRepository safetyCenterIssueDismissalRepository) {
this.mSafetyCenterIssueDismissalRepository = safetyCenterIssueDismissalRepository;
@@ -72,7 +69,7 @@ final class SafetyCenterIssueDeduplicator {
* <p>In case any issue, in the bucket of duplicate issues, was dismissed, all issues of the
* same or lower severity will be dismissed as well.
*
- * @return deduplicated list of issues, and some other information gathere in the deduplication
+ * @return deduplicated list of issues, and some other information gathered in the deduplication
* process
*/
@RequiresApi(UPSIDE_DOWN_CAKE)
@@ -123,7 +120,7 @@ final class SafetyCenterIssueDeduplicator {
for (int i = 0; i < dedupBuckets.size(); i++) {
List<SafetySourceIssueInfo> duplicates = dedupBuckets.valueAt(i);
if (duplicates.isEmpty()) {
- Log.w(TAG, "List of duplicates in a dedupBucket is empty");
+ Log.w(TAG, "List of duplicates in a deduplication bucket is empty");
continue;
}
@@ -164,7 +161,7 @@ final class SafetyCenterIssueDeduplicator {
}
/**
- * Handles dismissals logic: in each bucket, dismissal details of the top (highest priority)
+ * Handles dismissals logic: in each bucket, dismissal details of the highest priority (top)
* dismissed issue will be copied to all other duplicate issues in that bucket, that are of
* equal or lower severity (not priority). Notification-dismissal details are handled similarly.
*/
@@ -329,7 +326,6 @@ final class SafetyCenterIssueDeduplicator {
}
/** Encapsulates deduplication result along with some additional information. */
- @RequiresApi(TIRAMISU) // to simplify code and minimize code path differences across SDKs
static final class DeduplicationInfo {
private final List<SafetySourceIssueInfo> mDeduplicatedIssues;
private final List<SafetySourceIssueInfo> mFilteredOutDuplicates;
diff --git a/service/java/com/android/safetycenter/data/SafetyCenterIssueDismissalRepository.java b/service/java/com/android/safetycenter/data/SafetyCenterIssueDismissalRepository.java
index bc9bfb2d6..c84aa54f3 100644
--- a/service/java/com/android/safetycenter/data/SafetyCenterIssueDismissalRepository.java
+++ b/service/java/com/android/safetycenter/data/SafetyCenterIssueDismissalRepository.java
@@ -16,11 +16,8 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriendlyString;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.content.ApexEnvironment;
@@ -30,7 +27,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.modules.utils.BackgroundThread;
import com.android.safetycenter.ApiLock;
@@ -48,6 +45,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@@ -66,7 +64,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* <p>This class isn't thread safe. Thread safety must be handled by the caller.
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetyCenterIssueDismissalRepository {
@@ -130,11 +127,7 @@ final class SafetyCenterIssueDismissalRepository {
Duration timeSinceLastDismissal = Duration.between(dismissedAt, Instant.now());
boolean isTimeToResurface = timeSinceLastDismissal.compareTo(delay) >= 0;
- if (isTimeToResurface) {
- return false;
- }
-
- return true;
+ return !isTimeToResurface;
}
/**
@@ -342,7 +335,7 @@ final class SafetyCenterIssueDismissalRepository {
* and all following calls won't have any effect.
*/
void resurfaceHiddenIssueAfterPeriod(SafetyCenterIssueKey safetyCenterIssueKey) {
- IssueData issueData = getOrWarn(safetyCenterIssueKey, "resurfaceIssueAfterPeriod");
+ IssueData issueData = getOrWarn(safetyCenterIssueKey, "resurfacing hidden issue");
if (issueData == null) {
return;
}
@@ -439,13 +432,22 @@ final class SafetyCenterIssueDismissalRepository {
fout.flush();
try {
Files.copy(issueDismissalRepositoryFile.toPath(), new FileOutputStream(fd));
+ fout.println();
+ } catch (NoSuchFileException e) {
+ fout.println("<No File> (equivalent to empty issue list)");
} catch (IOException e) {
- // TODO(b/266202404)
- e.printStackTrace(fout);
+ printError(e, fout);
}
fout.println();
}
+ // We want to dump the stack trace on a specific PrintWriter here, this is a false positive as
+ // the warning does not consider the overload that takes a PrintWriter as an argument (yet).
+ @SuppressWarnings("CatchAndPrintStackTrace")
+ private void printError(Throwable error, PrintWriter fout) {
+ error.printStackTrace(fout);
+ }
+
@Nullable
private IssueData getOrWarn(SafetyCenterIssueKey issueKey, String reason) {
IssueData issueData = mIssues.get(issueKey);
@@ -492,9 +494,9 @@ final class SafetyCenterIssueDismissalRepository {
try {
persistedSafetyCenterIssues =
SafetyCenterIssuesPersistence.read(getIssueDismissalRepositoryFile());
- Log.i(TAG, "Safety Center persisted issues read successfully");
+ Log.d(TAG, "Safety Center persisted issues read successfully");
} catch (PersistenceException e) {
- Log.e(TAG, "Cannot read Safety Center persisted issues", e);
+ Log.w(TAG, "Cannot read Safety Center persisted issues", e);
}
load(persistedSafetyCenterIssues);
diff --git a/service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java b/service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java
index 566c28c1e..2e6f707a3 100644
--- a/service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java
+++ b/service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java
@@ -16,8 +16,6 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static com.android.safetycenter.data.SafetyCenterIssueDeduplicator.DeduplicationInfo;
import static java.util.Collections.emptyList;
@@ -32,8 +30,6 @@ import android.safetycenter.config.SafetySource;
import android.safetycenter.config.SafetySourcesGroup;
import android.util.SparseArray;
-import androidx.annotation.RequiresApi;
-
import com.android.modules.utils.build.SdkLevel;
import com.android.permission.util.UserUtils;
import com.android.safetycenter.SafetyCenterConfigReader;
@@ -56,7 +52,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* <p>Responsible for generating lists of issues and deduplication of issues.
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetyCenterIssueRepository {
@@ -92,19 +87,6 @@ final class SafetyCenterIssueRepository {
* Updates the class as per the current state of issues. Should be called after any state update
* that can affect issues.
*/
- void updateIssues(UserProfileGroup userProfileGroup) {
- updateIssues(userProfileGroup.getProfileParentUserId(), /* isManagedProfile= */ false);
-
- int[] managedProfileUserIds = userProfileGroup.getManagedProfilesUserIds();
- for (int i = 0; i < managedProfileUserIds.length; i++) {
- updateIssues(managedProfileUserIds[i], /* isManagedProfile= */ true);
- }
- }
-
- /**
- * Updates the class as per the current state of issues. Should be called after any state update
- * that can affect issues.
- */
void updateIssues(@UserIdInt int userId) {
updateIssues(userId, UserUtils.isManagedProfile(userId, mContext));
}
diff --git a/service/java/com/android/safetycenter/data/SafetyEventFix.java b/service/java/com/android/safetycenter/data/SafetyEventFix.java
new file mode 100644
index 000000000..4050eddfb
--- /dev/null
+++ b/service/java/com/android/safetycenter/data/SafetyEventFix.java
@@ -0,0 +1,118 @@
+/*
+ * 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.safetycenter.data;
+
+import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED;
+import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED;
+
+import android.annotation.UserIdInt;
+import android.safetycenter.SafetyEvent;
+import android.safetycenter.SafetySourceData;
+import android.safetycenter.SafetySourceIssue;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
+
+import java.util.List;
+
+/**
+ * Works around sources sending unexpected {@link SafetyEvent}s by optionally replacing them using
+ * heuristics based on the incoming {@link SafetySourceData} and Safety Center's current state.
+ *
+ * @hide
+ */
+public final class SafetyEventFix {
+
+ private static final String TAG = "SafetyEventFix";
+
+ private SafetyEventFix() {}
+
+ /**
+ * Optionally returns a new {@link SafetyEvent} if heuristics indicate that the one provided by
+ * the source is inappropriate, otherwise returns the source-provided event unchanged.
+ *
+ * <p>If the incoming event has type {@link SafetyEvent#SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED}
+ * but the {@link SafetySourceData} no longer includes an issue, for which Safety Center has a
+ * record of an in-flight, resolving action, then the event will be exchanged for a new one of
+ * type {@link SafetyEvent#SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED}.
+ */
+ public static SafetyEvent maybeOverrideSafetyEvent(
+ SafetyCenterDataManager dataManager,
+ String safetySourceId,
+ @Nullable SafetySourceData safetySourceData,
+ SafetyEvent safetyEvent,
+ @UserIdInt int userId) {
+ if (safetySourceData == null
+ || safetyEvent.getType() != SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED) {
+ return safetyEvent;
+ }
+
+ ArraySet<SafetyCenterIssueActionId> possiblySuccessfulActions =
+ dataManager.getInFlightActions(safetySourceId, userId);
+
+ if (possiblySuccessfulActions.isEmpty()) {
+ return safetyEvent;
+ }
+
+ // Discard any actions for which the issue is still present in the latest source data, they
+ // cannot have been resolved successfully!
+ ArraySet<String> presentSourceIssueIds = getSourceIssueIds(safetySourceData);
+ for (int i = possiblySuccessfulActions.size() - 1; i >= 0; i--) {
+ String sourceIssueId =
+ possiblySuccessfulActions
+ .valueAt(i)
+ .getSafetyCenterIssueKey()
+ .getSafetySourceIssueId();
+ if (presentSourceIssueIds.contains(sourceIssueId)) {
+ possiblySuccessfulActions.removeAt(i);
+ }
+ }
+
+ if (possiblySuccessfulActions.isEmpty()) {
+ return safetyEvent;
+ }
+
+ if (possiblySuccessfulActions.size() > 1) {
+ Log.i(TAG, "Multiple actions resolved, not overriding " + safetyEvent);
+ return safetyEvent;
+ }
+
+ SafetyCenterIssueActionId successfulAction = possiblySuccessfulActions.valueAt(0);
+ SafetyEvent replacement = newActionSucceededEvent(successfulAction);
+ Log.i(TAG, "Replacing incoming " + safetyEvent + " with " + replacement);
+ return replacement;
+ }
+
+ private static ArraySet<String> getSourceIssueIds(SafetySourceData safetySourceData) {
+ List<SafetySourceIssue> issues = safetySourceData.getIssues();
+ ArraySet<String> issueIds = new ArraySet<>(issues.size());
+ for (int i = 0; i < issues.size(); i++) {
+ issueIds.add(issues.get(i).getId());
+ }
+ return issueIds;
+ }
+
+ private static SafetyEvent newActionSucceededEvent(SafetyCenterIssueActionId actionId) {
+ return new SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED)
+ .setSafetySourceIssueId(actionId.getSafetyCenterIssueKey().getSafetySourceIssueId())
+ .setSafetySourceIssueActionId(actionId.getSafetySourceIssueActionId())
+ .build();
+ }
+}
diff --git a/service/java/com/android/safetycenter/data/SafetySourceDataFix.java b/service/java/com/android/safetycenter/data/SafetySourceDataFix.java
new file mode 100644
index 000000000..a34f3b03b
--- /dev/null
+++ b/service/java/com/android/safetycenter/data/SafetySourceDataFix.java
@@ -0,0 +1,76 @@
+/*
+ * 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.safetycenter.data;
+
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.safetycenter.SafetySourceData;
+
+import androidx.annotation.Nullable;
+
+import com.android.safetycenter.PendingIntentFactory;
+import com.android.safetycenter.SafetyCenterConfigReader;
+
+/**
+ * Applies various workarounds and fixes to {@link SafetySourceData} as it's received.
+ *
+ * @hide
+ */
+public final class SafetySourceDataFix {
+
+ private final DefaultActionOverrideFix mDefaultActionOverrideFix;
+ private Context mContext;
+
+ public SafetySourceDataFix(
+ Context context,
+ PendingIntentFactory pendingIntentFactory,
+ SafetyCenterConfigReader safetyCenterConfigReader) {
+ mContext = context;
+ mDefaultActionOverrideFix =
+ new DefaultActionOverrideFix(
+ context, pendingIntentFactory, safetyCenterConfigReader);
+ }
+
+ /**
+ * Potentially overrides the {@link SafetySourceData}.
+ *
+ * <p>Should be called when the data is received from a source and before it's stored by Safety
+ * Center.
+ */
+ @Nullable
+ public SafetySourceData maybeOverrideSafetySourceData(
+ String sourceId,
+ @Nullable SafetySourceData safetySourceData,
+ String packageName,
+ @UserIdInt int userId) {
+ if (safetySourceData == null) {
+ return null;
+ }
+
+ if (AndroidLockScreenFix.shouldApplyFix(sourceId)) {
+ safetySourceData = AndroidLockScreenFix.applyFix(mContext, safetySourceData);
+ }
+
+ if (DefaultActionOverrideFix.shouldApplyFix(sourceId)) {
+ safetySourceData =
+ mDefaultActionOverrideFix.applyFix(
+ sourceId, safetySourceData, packageName, userId);
+ }
+
+ return safetySourceData;
+ }
+}
diff --git a/service/java/com/android/safetycenter/data/SafetySourceDataOverrides.java b/service/java/com/android/safetycenter/data/SafetySourceDataOverrides.java
new file mode 100644
index 000000000..b292ae6cb
--- /dev/null
+++ b/service/java/com/android/safetycenter/data/SafetySourceDataOverrides.java
@@ -0,0 +1,81 @@
+/*
+ * 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.safetycenter.data;
+
+import android.app.PendingIntent;
+import android.safetycenter.SafetySourceData;
+import android.safetycenter.SafetySourceIssue;
+import android.safetycenter.SafetySourceStatus;
+
+import com.android.modules.utils.build.SdkLevel;
+
+final class SafetySourceDataOverrides {
+ private SafetySourceDataOverrides() {}
+
+ static SafetySourceData.Builder copyDataToBuilderWithoutIssues(SafetySourceData data) {
+ if (SdkLevel.isAtLeastU()) {
+ return new SafetySourceData.Builder(data).clearIssues();
+ }
+
+ // Copy T-only fields
+ return new SafetySourceData.Builder().setStatus(data.getStatus());
+ }
+
+ static SafetySourceStatus.Builder copyStatusToBuilder(SafetySourceStatus status) {
+ if (SdkLevel.isAtLeastU()) {
+ return new SafetySourceStatus.Builder(status);
+ }
+
+ // Copy T-only fields
+ return new SafetySourceStatus.Builder(
+ status.getTitle(), status.getSummary(), status.getSeverityLevel())
+ .setPendingIntent(status.getPendingIntent())
+ .setEnabled(status.isEnabled())
+ .setIconAction(status.getIconAction());
+ }
+
+ static SafetySourceIssue.Builder copyIssueToBuilderWithoutActions(SafetySourceIssue issue) {
+ if (SdkLevel.isAtLeastU()) {
+ return new SafetySourceIssue.Builder(issue).clearActions();
+ }
+
+ // Copy T-only fields
+ return new SafetySourceIssue.Builder(
+ issue.getId(),
+ issue.getTitle(),
+ issue.getSummary(),
+ issue.getSeverityLevel(),
+ issue.getIssueTypeId())
+ .setIssueCategory(issue.getIssueCategory())
+ .setSubtitle(issue.getSubtitle())
+ .setOnDismissPendingIntent(issue.getOnDismissPendingIntent());
+ }
+
+ /**
+ * Returns an new {@link SafetySourceIssue.Action} object, replacing its {@link PendingIntent}
+ * with the one supplied.
+ */
+ static SafetySourceIssue.Action overrideActionPendingIntent(
+ SafetySourceIssue.Action action, PendingIntent pendingIntent) {
+ // TODO(b/303443020): Add setter for pendingIntent so this method can use the copy builder.
+ return new SafetySourceIssue.Action.Builder(
+ action.getId(), action.getLabel(), pendingIntent)
+ .setWillResolve(action.willResolve())
+ .setSuccessMessage(action.getSuccessMessage())
+ .build();
+ }
+}
diff --git a/service/java/com/android/safetycenter/data/SafetySourceDataRepository.java b/service/java/com/android/safetycenter/data/SafetySourceDataRepository.java
index b47f7925e..cdb8709d6 100644
--- a/service/java/com/android/safetycenter/data/SafetySourceDataRepository.java
+++ b/service/java/com/android/safetycenter/data/SafetySourceDataRepository.java
@@ -16,18 +16,14 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__DATA_PROVIDED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__NO_DATA_PROVIDED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__REFRESH_TIMEOUT;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_CLEARED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_ERROR;
-import android.annotation.Nullable;
import android.annotation.UptimeMillisLong;
import android.annotation.UserIdInt;
-import android.content.Context;
import android.os.SystemClock;
import android.safetycenter.SafetyCenterData;
import android.safetycenter.SafetyEvent;
@@ -38,7 +34,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.safetycenter.SafetySourceKey;
import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
@@ -57,7 +53,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* <p>This class isn't thread safe. Thread safety must be handled by the caller.
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetySourceDataRepository {
@@ -68,24 +63,20 @@ final class SafetySourceDataRepository {
private final ArrayMap<SafetySourceKey, Long> mSafetySourceLastUpdated = new ArrayMap<>();
private final ArrayMap<SafetySourceKey, Integer> mSourceStates = new ArrayMap<>();
- private final Context mContext;
private final SafetyCenterInFlightIssueActionRepository
mSafetyCenterInFlightIssueActionRepository;
private final SafetyCenterIssueDismissalRepository mSafetyCenterIssueDismissalRepository;
SafetySourceDataRepository(
- Context context,
SafetyCenterInFlightIssueActionRepository safetyCenterInFlightIssueActionRepository,
SafetyCenterIssueDismissalRepository safetyCenterIssueDismissalRepository) {
- mContext = context;
mSafetyCenterInFlightIssueActionRepository = safetyCenterInFlightIssueActionRepository;
mSafetyCenterIssueDismissalRepository = safetyCenterIssueDismissalRepository;
}
/**
- * Sets the latest {@link SafetySourceData} for the given {@code safetySourceId}, {@link
- * SafetyEvent}, {@code packageName} and {@code userId}, and returns {@code true} if this caused
- * any changes which would alter {@link SafetyCenterData}.
+ * Sets the latest {@link SafetySourceData} for the given {@link SafetySourceKey}, and returns
+ * {@code true} if this caused any changes which would alter {@link SafetyCenterData}.
*
* <p>This method does not perform any validation, {@link
* SafetyCenterDataManager#setSafetySourceData(SafetySourceData, String, SafetyEvent, String,
@@ -98,22 +89,16 @@ final class SafetySourceDataRepository {
* <p>This method may modify the {@link SafetyCenterIssueDismissalRepository}.
*/
boolean setSafetySourceData(
- @Nullable SafetySourceData safetySourceData,
- String safetySourceId,
- @UserIdInt int userId) {
- SafetySourceKey key = SafetySourceKey.of(safetySourceId, userId);
- safetySourceData =
- AndroidLockScreenFix.maybeOverrideSafetySourceData(
- mContext, safetySourceId, safetySourceData);
-
- boolean sourceDataDiffers = !Objects.equals(safetySourceData, mSafetySourceData.get(key));
- boolean removedSourceError = mSafetySourceErrors.remove(key);
+ SafetySourceKey safetySourceKey, @Nullable SafetySourceData safetySourceData) {
+ boolean sourceDataDiffers =
+ !Objects.equals(safetySourceData, mSafetySourceData.get(safetySourceKey));
+ boolean removedSourceError = mSafetySourceErrors.remove(safetySourceKey);
if (sourceDataDiffers) {
- setSafetySourceDataInternal(key, safetySourceData);
+ setSafetySourceDataInternal(safetySourceKey, safetySourceData);
}
- setLastUpdatedNow(key);
+ setLastUpdatedNow(safetySourceKey);
return sourceDataDiffers || removedSourceError;
}
@@ -155,19 +140,33 @@ final class SafetySourceDataRepository {
}
/**
- * Reports the given {@link SafetySourceErrorDetails} for the given {@code safetySourceId} and
- * {@code userId}, and returns {@code true} if this changed the repository's data.
+ * Returns whether the repository has the given {@link SafetySourceData} for the given {@link
+ * SafetySourceKey}.
+ */
+ boolean sourceHasData(
+ SafetySourceKey safetySourceKey, @Nullable SafetySourceData safetySourceData) {
+ if (mSafetySourceErrors.contains(safetySourceKey)) {
+ // Any error will cause the SafetySourceData to be discarded in favor of an error
+ // message, so it can't possibly match the SafetySourceData passed in parameter.
+ return false;
+ }
+ return Objects.equals(safetySourceData, mSafetySourceData.get(safetySourceKey));
+ }
+
+ /**
+ * Reports the given {@link SafetySourceErrorDetails} for the given {@link SafetySourceKey}, and
+ * returns {@code true} if this changed the repository's data.
*
* <p>This method does not perform any validation, {@link
* SafetyCenterDataManager#reportSafetySourceError(SafetySourceErrorDetails, String, String,
* int)} should be called wherever validation is required.
*/
boolean reportSafetySourceError(
- SafetySourceErrorDetails safetySourceErrorDetails,
- String safetySourceId,
- @UserIdInt int userId) {
+ SafetySourceKey safetySourceKey, SafetySourceErrorDetails safetySourceErrorDetails) {
SafetyEvent safetyEvent = safetySourceErrorDetails.getSafetyEvent();
- Log.w(TAG, "Error reported from source: " + safetySourceId + ", for event: " + safetyEvent);
+ Log.w(
+ TAG,
+ "Error reported from source: " + safetySourceKey + ", for event: " + safetyEvent);
int safetyEventType = safetyEvent.getType();
if (safetyEventType == SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED
@@ -175,9 +174,9 @@ final class SafetySourceDataRepository {
return false;
}
- SafetySourceKey sourceKey = SafetySourceKey.of(safetySourceId, userId);
- mSourceStates.put(sourceKey, SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_ERROR);
- return setSafetySourceError(sourceKey);
+ mSourceStates.put(
+ safetySourceKey, SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_ERROR);
+ return setSafetySourceError(safetySourceKey);
}
/**
@@ -339,9 +338,9 @@ final class SafetySourceDataRepository {
SafetySourceKey key = mSafetySourceErrors.valueAt(i);
fout.println("\t[" + i + "] " + key);
}
+ fout.println();
dumpArrayMap(fout, mSafetySourceLastUpdated, "LAST UPDATED");
dumpArrayMap(fout, mSourceStates, "SOURCE STATES");
- fout.println();
}
private static <K, V> void dumpArrayMap(PrintWriter fout, ArrayMap<K, V> map, String label) {
@@ -350,5 +349,6 @@ final class SafetySourceDataRepository {
for (int i = 0; i < count; i++) {
fout.println("\t[" + i + "] " + map.keyAt(i) + " -> " + map.valueAt(i));
}
+ fout.println();
}
}
diff --git a/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java b/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java
index cc265d7f9..1ca6cb466 100644
--- a/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java
+++ b/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java
@@ -16,9 +16,6 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -29,7 +26,7 @@ import android.safetycenter.SafetySourceStatus;
import android.safetycenter.config.SafetySource;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
import com.android.permission.util.UserUtils;
@@ -48,11 +45,10 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* <p>This class isn't thread safe. Thread safety must be handled by the caller.
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetySourceDataValidator {
- private static final String TAG = "SafetySourceDataValidator";
+ private static final String TAG = "SafetySourceDataValidat";
private final Context mContext;
private final SafetyCenterConfigReader mSafetyCenterConfigReader;
@@ -110,6 +106,8 @@ final class SafetySourceDataValidator {
}
if (safetySource.getType() == SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC
+ && safetySource.getInitialDisplayState()
+ != SafetySource.INITIAL_DISPLAY_STATE_HIDDEN
&& safetySourceStatus == null) {
throw new IllegalArgumentException(
"Missing status for dynamic safety source: " + safetySourceId);
@@ -192,13 +190,13 @@ final class SafetySourceDataValidator {
&& !checkCerts(
packageName,
SafetyCenterFlags.getAdditionalAllowedPackageCerts(packageName))) {
- Log.e(
+ Log.w(
TAG,
- "Package "
+ "Package: "
+ packageName
- + " for source "
+ + ", for source: "
+ safetySourceId
- + " signed with invalid signature");
+ + " is signed with invalid signature");
throw new IllegalArgumentException("Invalid signature for package " + packageName);
}
}
@@ -210,7 +208,7 @@ final class SafetySourceDataValidator {
byte[] certificate = new Signature(certHash).toByteArray();
if (mPackageManager.hasSigningCertificate(
packageName, certificate, PackageManager.CERT_INPUT_SHA256)) {
- Log.d(TAG, "Package " + packageName + " has expected signature");
+ Log.v(TAG, "Package: " + packageName + " has expected signature");
hasMatchingCert = true;
}
} catch (IllegalArgumentException e) {
diff --git a/service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java b/service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java
index b6bf280ae..e73459598 100644
--- a/service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java
+++ b/service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java
@@ -16,10 +16,7 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import android.annotation.ElapsedRealtimeLong;
-import android.annotation.Nullable;
import android.content.Context;
import android.safetycenter.SafetyCenterManager;
import android.safetycenter.SafetyEvent;
@@ -27,7 +24,7 @@ import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceIssue;
import android.safetycenter.SafetySourceStatus;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.permission.util.UserUtils;
import com.android.safetycenter.SafetySourceIssueInfo;
@@ -44,7 +41,6 @@ import javax.annotation.concurrent.NotThreadSafe;
* Collates information from various data-related classes and uses that information to log {@code
* SafetySourceStateCollected} atoms.
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
final class SafetySourceStateCollectedLogger {
@@ -132,10 +128,11 @@ final class SafetySourceStateCollectedLogger {
}
}
+ Integer severityLevel = maxSeverityLevel > Integer.MIN_VALUE ? maxSeverityLevel : null;
SafetyCenterStatsdLogger.writeSafetySourceStateCollected(
sourceKey.getSourceId(),
isManagedProfile,
- maxSeverityLevel > Integer.MIN_VALUE ? maxSeverityLevel : null,
+ severityLevel,
openIssuesCount,
dismissedIssuesCount,
getDuplicateCount(sourceKey),
diff --git a/service/java/com/android/safetycenter/data/package-info.java b/service/java/com/android/safetycenter/data/package-info.java
index 597847505..a125b3176 100644
--- a/service/java/com/android/safetycenter/data/package-info.java
+++ b/service/java/com/android/safetycenter/data/package-info.java
@@ -14,6 +14,11 @@
* limitations under the License.
*/
@NonNullByDefault
+@RequiresApi(TIRAMISU)
package com.android.safetycenter.data;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.annotation.RequiresApi;
+
import com.android.safetycenter.annotations.NonNullByDefault;
diff --git a/service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java b/service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java
index d9514f56e..168d73a0f 100644
--- a/service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java
+++ b/service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java
@@ -16,8 +16,6 @@
package com.android.safetycenter.logging;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static com.android.permission.PermissionStatsLog.SAFETY_STATE;
import android.annotation.UserIdInt;
@@ -30,8 +28,6 @@ import android.safetycenter.config.SafetySourcesGroup;
import android.util.Log;
import android.util.StatsEvent;
-import androidx.annotation.RequiresApi;
-
import com.android.internal.annotations.GuardedBy;
import com.android.modules.utils.build.SdkLevel;
import com.android.permission.PermissionStatsLog;
@@ -56,7 +52,6 @@ import java.util.List;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback {
private static final String TAG = "SafetyCenterPullAtom";
@@ -93,17 +88,17 @@ public final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback
return StatsManager.PULL_SKIP;
}
if (!SafetyCenterFlags.getSafetyCenterEnabled()) {
- Log.w(TAG, "Attempt to pull SAFETY_STATE, but Safety Center is disabled");
+ Log.i(TAG, "Attempt to pull SAFETY_STATE, but Safety Center is disabled");
return StatsManager.PULL_SKIP;
}
List<UserProfileGroup> userProfileGroups =
UserProfileGroup.getAllUserProfileGroups(mContext);
synchronized (mApiLock) {
if (!SafetyCenterFlags.getAllowStatsdLogging()) {
- Log.w(TAG, "Skipping pulling and writing atoms due to logging being disabled");
+ Log.i(TAG, "Skipping pulling and writing atoms due to logging being disabled");
return StatsManager.PULL_SKIP;
}
- Log.i(TAG, "Pulling and writing atoms…");
+ Log.d(TAG, "Pulling and writing atoms…");
for (int i = 0; i < userProfileGroups.size(); i++) {
UserProfileGroup userProfileGroup = userProfileGroups.get(i);
List<SafetySourcesGroup> loggableGroups =
@@ -111,8 +106,8 @@ public final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback
statsEvents.add(
createOverallSafetyStateAtomLocked(userProfileGroup, loggableGroups));
// The SAFETY_SOURCE_STATE_COLLECTED atoms are written instead of being pulled,
- // they do not support pull but we want to collect them at the same time as
- // the above pulled atom.
+ // as they do not support pull. We still want to collect them at the same time as
+ // the above pulled atom, which is why they're written here.
writeSafetySourceStateCollectedAtomsLocked(userProfileGroup, loggableGroups);
}
}
@@ -165,7 +160,8 @@ public final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback
int[] managedIds = userProfileGroup.getManagedRunningProfilesUserIds();
for (int k = 0; k < managedIds.length; k++) {
- writeSafetySourceStateCollectedAtomLocked(loggableSource, managedIds[k], true);
+ writeSafetySourceStateCollectedAtomLocked(
+ loggableSource, managedIds[k], /* isUserManaged= */ true);
}
}
}
diff --git a/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java b/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java
index 8ca662d27..710c3f7ac 100644
--- a/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java
+++ b/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java
@@ -16,8 +16,6 @@
package com.android.safetycenter.logging;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_PRIMARY_ACTION_CLICKED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_SECONDARY_ACTION_CLICKED;
@@ -74,7 +72,6 @@ import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SE
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
-import android.annotation.Nullable;
import android.safetycenter.SafetyCenterManager;
import android.safetycenter.SafetyCenterManager.RefreshRequestType;
import android.safetycenter.SafetyCenterStatus;
@@ -83,7 +80,7 @@ import android.safetycenter.SafetySourceData;
import android.util.Log;
import android.util.StatsEvent;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.permission.PermissionStatsLog;
import com.android.safetycenter.SafetyCenterFlags;
@@ -101,7 +98,6 @@ import java.time.Duration;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetyCenterStatsdLogger {
private static final String TAG = "SafetyCenterStatsdLog";
diff --git a/service/java/com/android/safetycenter/logging/package-info.java b/service/java/com/android/safetycenter/logging/package-info.java
index dcc1828b4..79e504ebd 100644
--- a/service/java/com/android/safetycenter/logging/package-info.java
+++ b/service/java/com/android/safetycenter/logging/package-info.java
@@ -14,6 +14,11 @@
* limitations under the License.
*/
@NonNullByDefault
+@RequiresApi(TIRAMISU)
package com.android.safetycenter.logging;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.annotation.RequiresApi;
+
import com.android.safetycenter.annotations.NonNullByDefault;
diff --git a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationChannels.java b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationChannels.java
index e2df717a7..ccd2bfabc 100644
--- a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationChannels.java
+++ b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationChannels.java
@@ -16,11 +16,6 @@
package com.android.safetycenter.notifications;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.Nullable;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
@@ -31,10 +26,11 @@ import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceIssue;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.permission.util.UserUtils;
-import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+import com.android.safetycenter.SafetyCenterFlags;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import java.util.List;
@@ -43,7 +39,6 @@ import java.util.List;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetyCenterNotificationChannels {
private static final String TAG = "SafetyCenterNC";
@@ -53,11 +48,10 @@ public final class SafetyCenterNotificationChannels {
private static final String CHANNEL_ID_RECOMMENDATION = "safety_center_recommendation";
private static final String CHANNEL_ID_CRITICAL_WARNING = "safety_center_critical_warning";
- private final SafetyCenterResourcesContext mResourcesContext;
+ private final SafetyCenterResourcesApk mSafetyCenterResourcesApk;
- public SafetyCenterNotificationChannels(
- SafetyCenterResourcesContext safetyCenterResourceContext) {
- mResourcesContext = safetyCenterResourceContext;
+ public SafetyCenterNotificationChannels(SafetyCenterResourcesApk safetyCenterResourcesApk) {
+ mSafetyCenterResourcesApk = safetyCenterResourcesApk;
}
/** Returns a {@link NotificationManager} which will send notifications to the given user. */
@@ -70,18 +64,25 @@ public final class SafetyCenterNotificationChannels {
? contextAsUser.getSystemService(NotificationManager.class)
: null;
if (notificationManager == null) {
- Log.w(TAG, "Could not retrieve NotificationManager for user " + userHandle);
+ Log.w(
+ TAG,
+ "Could not retrieve NotificationManager for user id: "
+ + userHandle.getIdentifier());
}
return notificationManager;
}
@Nullable
- private static Context getContextAsUser(Context baseContext, UserHandle userHandle) {
+ static Context getContextAsUser(Context baseContext, UserHandle userHandle) {
+ // This call requires the INTERACT_ACROSS_USERS permission.
+ final long callingId = Binder.clearCallingIdentity();
try {
- return baseContext.createContextAsUser(userHandle, 0);
+ return baseContext.createContextAsUser(userHandle, /* flags= */ 0);
} catch (RuntimeException e) {
- Log.w(TAG, "Could not create Context as user " + userHandle, e);
+ Log.w(TAG, "Could not create Context as user id: " + userHandle.getIdentifier(), e);
return null;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
}
@@ -102,10 +103,18 @@ public final class SafetyCenterNotificationChannels {
/**
* Creates all Safety Center {@link NotificationChannel}s instances and their group, for all
- * current users, dropping any calling identity so those channels can be unblockable. Throws a
- * {@link RuntimeException} if any channel is malformed and could not be created.
+ * current users, dropping any calling identity so those channels can be unblockable.
*/
public void createAllChannelsForAllUsers(Context context) {
+ if (!SafetyCenterFlags.getNotificationsEnabled()) {
+ // TODO(b/284271124): Decide what to do with existing channels if flag gets toggled.
+ Log.i(
+ TAG,
+ "Not creating notification channels because Safety Center notifications are"
+ + " disabled");
+ return;
+ }
+
List<UserHandle> users = UserUtils.getUserHandles(context);
for (int i = 0; i < users.size(); i++) {
createAllChannelsForUser(context, users.get(i));
@@ -115,31 +124,54 @@ public final class SafetyCenterNotificationChannels {
/**
* Creates all Safety Center {@link NotificationChannel}s instances and their group for the
* given {@link UserHandle}, dropping any calling identity so those channels can be unblockable.
- * Throws a {@link RuntimeException} if any channel is malformed and could not be created.
*/
public void createAllChannelsForUser(Context context, UserHandle user) {
+ if (!SafetyCenterFlags.getNotificationsEnabled()) {
+ // TODO(b/284271124): Decide what to do with existing channels if flag gets toggled.
+ Log.i(
+ TAG,
+ "Not creating notification channels because Safety Center notifications are"
+ + " disabled");
+ return;
+ }
+
+ NotificationManager notificationManager = getNotificationManagerForUser(context, user);
+ if (notificationManager == null) {
+ return;
+ }
+
try {
- NotificationManager notificationManager =
- requireNonNull(getNotificationManagerForUser(context, user));
createAllChannelsWithoutCallingIdentity(notificationManager);
} catch (RuntimeException e) {
- Log.w(TAG, "Error creating notification channels for user " + user.getIdentifier(), e);
+ Log.w(
+ TAG,
+ "Error creating notification channels for user id: " + user.getIdentifier(),
+ e);
}
}
@Nullable
private String getChannelIdForIssue(SafetySourceIssue issue) {
- switch (issue.getSeverityLevel()) {
+ int issueSeverityLevel = issue.getSeverityLevel();
+ switch (issueSeverityLevel) {
case SafetySourceData.SEVERITY_LEVEL_INFORMATION:
return CHANNEL_ID_INFORMATION;
case SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION:
return CHANNEL_ID_RECOMMENDATION;
case SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING:
return CHANNEL_ID_CRITICAL_WARNING;
- default:
- Log.w(TAG, "No applicable notification channel for issue " + issue);
+ case SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED:
+ Log.w(TAG, "SafetySourceData.SeverityLevel is unspecified for issue: " + issue);
return null;
}
+
+ Log.w(
+ TAG,
+ "Unexpected SafetySourceData.SeverityLevel: "
+ + issueSeverityLevel
+ + ", for issue: "
+ + issue);
+ return null;
}
/**
@@ -149,7 +181,8 @@ public final class SafetyCenterNotificationChannels {
* created.
*/
private void createAllChannelsWithoutCallingIdentity(NotificationManager notificationManager) {
- // Clearing calling identity to be able to make unblockable system notification channels
+ // Clearing calling identity to be able to make unblockable system notification channels and
+ // call this for other users with the INTERACT_ACROSS_USERS permission.
final long callingId = Binder.clearCallingIdentity();
try {
notificationManager.createNotificationChannelGroup(getChannelGroupDefinition());
@@ -161,6 +194,17 @@ public final class SafetyCenterNotificationChannels {
}
}
+ private void clearAllChannelsWithoutCallingIdentity(NotificationManager notificationManager) {
+ // Clearing calling identity to do this for other users with the INTERACT_ACROSS_USERS
+ // permission.
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ notificationManager.deleteNotificationChannelGroup(CHANNEL_GROUP_ID);
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
private NotificationChannelGroup getChannelGroupDefinition() {
return new NotificationChannelGroup(
CHANNEL_GROUP_ID, getString("notification_channel_group_name"));
@@ -200,6 +244,6 @@ public final class SafetyCenterNotificationChannels {
}
private String getString(String name) {
- return mResourcesContext.getStringByName(name);
+ return mSafetyCenterResourcesApk.getStringByName(name);
}
}
diff --git a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationFactory.java b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationFactory.java
index 62da1fc7d..84001f249 100644
--- a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationFactory.java
+++ b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationFactory.java
@@ -16,20 +16,20 @@
package com.android.safetycenter.notifications;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID;
import static android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID;
import static android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_USER_HANDLE;
+import static com.android.safetycenter.notifications.SafetyCenterNotificationChannels.getContextAsUser;
+
import android.annotation.ColorInt;
-import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.UserHandle;
@@ -37,14 +37,14 @@ import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceIssue;
import android.text.TextUtils;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
import com.android.safetycenter.PendingIntentFactory;
import com.android.safetycenter.internaldata.SafetyCenterIds;
import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
-import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import java.time.Duration;
import java.util.List;
@@ -53,24 +53,22 @@ import java.util.List;
* Factory that builds {@link Notification} objects from {@link SafetySourceIssue} instances with
* appropriate {@link PendingIntent}s for click and dismiss callbacks.
*/
-@RequiresApi(TIRAMISU)
final class SafetyCenterNotificationFactory {
- private static final String TAG = "SafetyCenterNF";
private static final int OPEN_SAFETY_CENTER_REQUEST_CODE = 1221;
private static final Duration SUCCESS_NOTIFICATION_TIMEOUT = Duration.ofSeconds(10);
private final Context mContext;
private final SafetyCenterNotificationChannels mNotificationChannels;
- private final SafetyCenterResourcesContext mResourcesContext;
+ private final SafetyCenterResourcesApk mSafetyCenterResourcesApk;
SafetyCenterNotificationFactory(
Context context,
SafetyCenterNotificationChannels notificationChannels,
- SafetyCenterResourcesContext resourcesContext) {
+ SafetyCenterResourcesApk safetyCenterResourcesApk) {
mContext = context;
mNotificationChannels = notificationChannels;
- mResourcesContext = resourcesContext;
+ mSafetyCenterResourcesApk = safetyCenterResourcesApk;
}
/**
@@ -84,13 +82,22 @@ final class SafetyCenterNotificationFactory {
Notification newNotificationForSuccessfulAction(
NotificationManager notificationManager,
SafetySourceIssue issue,
- SafetySourceIssue.Action action) {
- String channelId = mNotificationChannels.getCreatedChannelId(notificationManager, issue);
+ SafetySourceIssue.Action action,
+ @UserIdInt int userId) {
+ if (action.getSuccessMessage() == null) {
+ return null;
+ }
+ String channelId = mNotificationChannels.getCreatedChannelId(notificationManager, issue);
if (channelId == null) {
return null;
}
+ PendingIntent contentIntent = newSafetyCenterPendingIntent(userId);
+ if (contentIntent == null) {
+ return null;
+ }
+
Notification.Builder builder =
new Notification.Builder(mContext, channelId)
.setSmallIcon(
@@ -99,7 +106,8 @@ final class SafetyCenterNotificationFactory {
.setContentTitle(action.getSuccessMessage())
.setShowWhen(true)
.setTimeoutAfter(SUCCESS_NOTIFICATION_TIMEOUT.toMillis())
- .setContentIntent(newSafetyCenterPendingIntent(null));
+ .setContentIntent(contentIntent)
+ .setAutoCancel(true);
Integer color = getNotificationColor(SafetySourceData.SEVERITY_LEVEL_INFORMATION);
if (color != null) {
@@ -122,7 +130,6 @@ final class SafetyCenterNotificationFactory {
SafetySourceIssue issue,
SafetyCenterIssueKey issueKey) {
String channelId = mNotificationChannels.getCreatedChannelId(notificationManager, issue);
-
if (channelId == null) {
return null;
}
@@ -140,6 +147,11 @@ final class SafetyCenterNotificationFactory {
}
}
+ PendingIntent contentIntent = newSafetyCenterPendingIntent(issueKey);
+ if (contentIntent == null) {
+ return null;
+ }
+
Notification.Builder builder =
new Notification.Builder(mContext, channelId)
.setSmallIcon(getNotificationIcon(issue.getSeverityLevel()))
@@ -147,7 +159,7 @@ final class SafetyCenterNotificationFactory {
.setShowWhen(true)
.setContentTitle(title)
.setContentText(text)
- .setContentIntent(newSafetyCenterPendingIntent(issueKey))
+ .setContentIntent(contentIntent)
.setDeleteIntent(
SafetyCenterNotificationReceiver.newNotificationDismissedIntent(
mContext, issueKey));
@@ -163,27 +175,59 @@ final class SafetyCenterNotificationFactory {
builder.addAction(notificationAction);
}
+ if (issue.getSeverityLevel() == SafetySourceData.SEVERITY_LEVEL_INFORMATION) {
+ builder.setAutoCancel(true);
+ }
+
return builder.build();
}
/**
- * Returns a {@link PendingIntent} to open Safety Center, optionally navigating to and/or
- * highlighting a specific issue if {@code issueKey} is given.
+ * Returns a {@link PendingIntent} to open Safety Center, navigating to a specific issue, or
+ * {@code null} if no such intent can be created.
*/
- private PendingIntent newSafetyCenterPendingIntent(@Nullable SafetyCenterIssueKey issueKey) {
- Intent intent = new Intent(Intent.ACTION_SAFETY_CENTER);
- if (issueKey != null) {
- // Set the encoded issue key as the intent's identifier to ensure the PendingIntents of
- // different notifications do not collide:
- intent.setIdentifier(SafetyCenterIds.encodeToString(issueKey));
- intent.putExtra(EXTRA_SAFETY_SOURCE_ID, issueKey.getSafetySourceId());
- intent.putExtra(EXTRA_SAFETY_SOURCE_ISSUE_ID, issueKey.getSafetySourceIssueId());
- intent.putExtra(EXTRA_SAFETY_SOURCE_USER_HANDLE, UserHandle.of(issueKey.getUserId()));
+ @Nullable
+ private PendingIntent newSafetyCenterPendingIntent(SafetyCenterIssueKey issueKey) {
+ UserHandle userHandle = UserHandle.of(issueKey.getUserId());
+ Context userContext = getContextAsUser(mContext, userHandle);
+ if (userContext == null) {
+ return null;
}
+
+ Intent intent = newSafetyCenterIntent();
+ // Set the encoded issue key as the intent's identifier to ensure the PendingIntents of
+ // different notifications do not collide:
+ intent.setIdentifier(SafetyCenterIds.encodeToString(issueKey));
+ intent.putExtra(EXTRA_SAFETY_SOURCE_ID, issueKey.getSafetySourceId());
+ intent.putExtra(EXTRA_SAFETY_SOURCE_ISSUE_ID, issueKey.getSafetySourceIssueId());
+ intent.putExtra(EXTRA_SAFETY_SOURCE_USER_HANDLE, userHandle);
+
+ return PendingIntentFactory.getActivityPendingIntent(
+ userContext, OPEN_SAFETY_CENTER_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE);
+ }
+
+ /**
+ * Returns a {@link PendingIntent} to open Safety Center, or {@code null} if no such intent can
+ * be created.
+ */
+ @Nullable
+ private PendingIntent newSafetyCenterPendingIntent(@UserIdInt int userId) {
+ Context userContext = getContextAsUser(mContext, UserHandle.of(userId));
+ if (userContext == null) {
+ return null;
+ }
+ return PendingIntentFactory.getActivityPendingIntent(
+ userContext,
+ OPEN_SAFETY_CENTER_REQUEST_CODE,
+ newSafetyCenterIntent(),
+ PendingIntent.FLAG_IMMUTABLE);
+ }
+
+ private static Intent newSafetyCenterIntent() {
+ Intent intent = new Intent(Intent.ACTION_SAFETY_CENTER);
// This extra is defined in the PermissionController APK, cannot be referenced directly:
intent.putExtra("navigation_source_intent_extra", "NOTIFICATION");
- return PendingIntentFactory.getActivityPendingIntent(
- mContext, OPEN_SAFETY_CENTER_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE);
+ return intent;
}
private Icon getNotificationIcon(@SafetySourceData.SeverityLevel int severityLevel) {
@@ -191,7 +235,7 @@ final class SafetyCenterNotificationFactory {
if (severityLevel == SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING) {
iconResName = "ic_notification_badge_critical";
}
- Icon icon = mResourcesContext.getIconByDrawableName(iconResName);
+ Icon icon = mSafetyCenterResourcesApk.getIconByDrawableName(iconResName);
if (icon == null) {
// In case it was impossible to fetch the above drawable for any reason use this
// fallback which should be present on all Android devices:
@@ -207,12 +251,13 @@ final class SafetyCenterNotificationFactory {
if (severityLevel == SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING) {
colorResName = "notification_tint_critical";
}
- return mResourcesContext.getColorByName(colorResName);
+ return mSafetyCenterResourcesApk.getColorByName(colorResName);
}
private Bundle getNotificationExtras() {
Bundle extras = new Bundle();
- String appName = mResourcesContext.getStringByName("notification_channel_group_name");
+ String appName =
+ mSafetyCenterResourcesApk.getStringByName("notification_channel_group_name");
if (!TextUtils.isEmpty(appName)) {
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName);
}
@@ -222,7 +267,9 @@ final class SafetyCenterNotificationFactory {
private Notification.Action toNotificationAction(
SafetyCenterIssueKey issueKey, SafetySourceIssue.Action issueAction) {
PendingIntent pendingIntent = getPendingIntentForAction(issueKey, issueAction);
- return new Notification.Action.Builder(null, issueAction.getLabel(), pendingIntent).build();
+ return new Notification.Action.Builder(
+ /* icon= */ null, issueAction.getLabel(), pendingIntent)
+ .build();
}
private PendingIntent getPendingIntentForAction(
@@ -230,7 +277,7 @@ final class SafetyCenterNotificationFactory {
if (issueAction.willResolve()) {
return getReceiverPendingIntentForResolvingAction(issueKey, issueAction);
} else {
- return getDirectPendingIntentForNonResolvingAction(issueKey, issueAction);
+ return getDirectPendingIntentForNonResolvingAction(issueAction);
}
}
@@ -251,12 +298,7 @@ final class SafetyCenterNotificationFactory {
}
private PendingIntent getDirectPendingIntentForNonResolvingAction(
- SafetyCenterIssueKey issueKey, SafetySourceIssue.Action issueAction) {
+ SafetySourceIssue.Action issueAction) {
return issueAction.getPendingIntent();
}
-
- private static boolean isDarkTheme(Context context) {
- return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
- }
}
diff --git a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java
index 0b4ac5ab3..ed0e95177 100644
--- a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java
+++ b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java
@@ -16,9 +16,6 @@
package com.android.safetycenter.notifications;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
-import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -27,7 +24,7 @@ import android.content.IntentFilter;
import android.safetycenter.SafetySourceIssue;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.GuardedBy;
import com.android.permission.util.UserUtils;
@@ -36,6 +33,7 @@ import com.android.safetycenter.PendingIntentFactory;
import com.android.safetycenter.SafetyCenterDataChangeNotifier;
import com.android.safetycenter.SafetyCenterFlags;
import com.android.safetycenter.SafetyCenterService;
+import com.android.safetycenter.SafetySourceIssues;
import com.android.safetycenter.UserProfileGroup;
import com.android.safetycenter.data.SafetyCenterDataManager;
import com.android.safetycenter.internaldata.SafetyCenterIds;
@@ -54,7 +52,6 @@ import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
private static final String TAG = "SafetyCenterNR";
@@ -120,13 +117,13 @@ public final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
private static SafetyCenterIssueActionId getIssueActionIdExtra(Intent intent) {
String issueActionIdString = intent.getStringExtra(EXTRA_ISSUE_ACTION_ID);
if (issueActionIdString == null) {
- Log.w(TAG, "Received notification action broadcast with null issue action ID");
+ Log.w(TAG, "Received notification action broadcast with null issue action id");
return null;
}
try {
return SafetyCenterIds.issueActionIdFromString(issueActionIdString);
} catch (IllegalArgumentException e) {
- Log.w(TAG, "Could not decode the issue action ID", e);
+ Log.w(TAG, "Could not decode the issue action id", e);
return null;
}
}
@@ -162,22 +159,28 @@ public final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_NOTIFICATION_DISMISSED);
filter.addAction(ACTION_NOTIFICATION_ACTION_CLICKED);
- context.registerReceiver(this, filter, Context.RECEIVER_NOT_EXPORTED);
+ context.registerReceiver(/* receiver= */ this, filter, Context.RECEIVER_NOT_EXPORTED);
}
@Override
public void onReceive(Context context, Intent intent) {
- if (!SafetyCenterFlags.getSafetyCenterEnabled()
- || !SafetyCenterFlags.getNotificationsEnabled()) {
+ if (!SafetyCenterFlags.getSafetyCenterEnabled()) {
+ Log.i(TAG, "Received notification broadcast but Safety Center is disabled");
+ return;
+ }
+
+ if (!SafetyCenterFlags.getNotificationsEnabled()) {
+ // TODO(b/284271124): Decide what to do with existing notifications
+ Log.i(TAG, "Received notification broadcast but notifications are disabled");
return;
}
- Log.d(TAG, "Received broadcast with action " + intent.getAction());
String action = intent.getAction();
if (action == null) {
- Log.w(TAG, "Received broadcast with null action!");
+ Log.w(TAG, "Received broadcast with null action");
return;
}
+ Log.d(TAG, "Received broadcast with action: " + action);
switch (action) {
case ACTION_NOTIFICATION_DISMISSED:
@@ -240,16 +243,8 @@ public final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
UserUtils.isManagedProfile(issueKey.getUserId(), context),
issue.getIssueTypeId(),
issue.getSeverityLevel(),
- isPrimaryAction(issue, issueActionId));
+ SafetySourceIssues.isPrimaryAction(
+ issue, issueActionId.getSafetySourceIssueActionId()));
}
}
-
- /** Returns {@code true} if {@code actionId} is the first action of {@code issue}. */
- private boolean isPrimaryAction(SafetySourceIssue issue, SafetyCenterIssueActionId actionId) {
- return !issue.getActions().isEmpty()
- && issue.getActions()
- .get(0)
- .getId()
- .equals(actionId.getSafetySourceIssueActionId());
- }
}
diff --git a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java
index 668158097..d17090c34 100644
--- a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java
+++ b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java
@@ -16,13 +16,11 @@
package com.android.safetycenter.notifications;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED;
import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriendlyString;
import android.annotation.IntDef;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Notification;
import android.app.NotificationManager;
@@ -36,18 +34,19 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import androidx.annotation.RequiresApi;
+import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
import com.android.permission.util.UserUtils;
import com.android.safetycenter.SafetyCenterFlags;
import com.android.safetycenter.SafetySourceIssueInfo;
+import com.android.safetycenter.SafetySourceIssues;
import com.android.safetycenter.UserProfileGroup;
import com.android.safetycenter.data.SafetyCenterDataManager;
import com.android.safetycenter.internaldata.SafetyCenterIds;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
-import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+import com.android.safetycenter.resources.SafetyCenterResourcesApk;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -66,7 +65,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterNotificationSender {
@@ -120,13 +118,13 @@ public final class SafetyCenterNotificationSender {
public static SafetyCenterNotificationSender newInstance(
Context context,
- SafetyCenterResourcesContext resourcesContext,
+ SafetyCenterResourcesApk safetyCenterResourcesApk,
SafetyCenterNotificationChannels notificationChannels,
SafetyCenterDataManager dataManager) {
return new SafetyCenterNotificationSender(
context,
new SafetyCenterNotificationFactory(
- context, notificationChannels, resourcesContext),
+ context, notificationChannels, safetyCenterResourcesApk),
dataManager);
}
@@ -137,7 +135,7 @@ public final class SafetyCenterNotificationSender {
* <p>The given {@link SafetyEvent} have type {@link
* SafetyEvent#SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED} and include issue and action IDs
* that correspond to a {@link SafetySourceIssue} for which a notification is currently
- * displayed. Otherwise this method has no effect.
+ * displayed. Otherwise, this method has no effect.
*
* @param sourceId of the source which reported the issue
* @param safetyEvent the source provided upon successful action resolution
@@ -145,6 +143,12 @@ public final class SafetyCenterNotificationSender {
*/
public void notifyActionSuccess(
String sourceId, SafetyEvent safetyEvent, @UserIdInt int userId) {
+ if (!SafetyCenterFlags.getNotificationsEnabled()) {
+ // TODO(b/284271124): Decide what to do with existing notifications if flag gets
+ // toggled.
+ return;
+ }
+
if (safetyEvent.getType() != SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED) {
Log.w(TAG, "Received safety event of wrong type");
return;
@@ -174,12 +178,8 @@ public final class SafetyCenterNotificationSender {
return;
}
- SafetySourceIssue.Action successfulAction = null;
- for (int i = 0; i < notifiedIssue.getActions().size(); i++) {
- if (notifiedIssue.getActions().get(i).getId().equals(sourceIssueActionId)) {
- successfulAction = notifiedIssue.getActions().get(i);
- }
- }
+ SafetySourceIssue.Action successfulAction =
+ SafetySourceIssues.findAction(notifiedIssue, sourceIssueActionId);
if (successfulAction == null) {
Log.w(TAG, "Successful action not found");
return;
@@ -193,7 +193,7 @@ public final class SafetyCenterNotificationSender {
Notification notification =
mNotificationFactory.newNotificationForSuccessfulAction(
- notificationManager, notifiedIssue, successfulAction);
+ notificationManager, notifiedIssue, successfulAction, userId);
if (notification == null) {
Log.w(TAG, "Could not create successful action notification");
return;
@@ -224,6 +224,7 @@ public final class SafetyCenterNotificationSender {
*/
public void updateNotifications(@UserIdInt int userId) {
if (!SafetyCenterFlags.getNotificationsEnabled()) {
+ // TODO(b/284271124): Decide what to do with existing notifications
return;
}
@@ -286,7 +287,7 @@ public final class SafetyCenterNotificationSender {
fout.println();
}
- /** Get all of the key-issue pairs for which notifications should be posted or updated now. */
+ /** Gets all the key-issue pairs for which notifications should be posted or updated now. */
private ArrayMap<SafetyCenterIssueKey, SafetySourceIssue> getIssuesToNotify(
@UserIdInt int userId) {
ArrayMap<SafetyCenterIssueKey, SafetySourceIssue> result = new ArrayMap<>();
@@ -325,14 +326,20 @@ public final class SafetyCenterNotificationSender {
@NotificationBehaviorInternal
private int getBehavior(SafetySourceIssue issue, SafetyCenterIssueKey issueKey) {
if (SdkLevel.isAtLeastU()) {
- switch (issue.getNotificationBehavior()) {
+ int notificationBehavior = issue.getNotificationBehavior();
+ switch (notificationBehavior) {
case SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER:
return NOTIFICATION_BEHAVIOR_INTERNAL_NEVER;
case SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED:
return NOTIFICATION_BEHAVIOR_INTERNAL_DELAYED;
case SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY:
return NOTIFICATION_BEHAVIOR_INTERNAL_IMMEDIATELY;
+ case SafetySourceIssue.NOTIFICATION_BEHAVIOR_UNSPECIFIED:
+ return getBehaviorForIssueWithUnspecifiedBehavior(issue, issueKey);
}
+ Log.w(
+ TAG,
+ "Unexpected SafetySourceIssue.NotificationBehavior: " + notificationBehavior);
}
// On Android T all issues are assumed to have "unspecified" behavior
return getBehaviorForIssueWithUnspecifiedBehavior(issue, issueKey);
diff --git a/service/java/com/android/safetycenter/notifications/package-info.java b/service/java/com/android/safetycenter/notifications/package-info.java
index 85b487b30..83d886fe1 100644
--- a/service/java/com/android/safetycenter/notifications/package-info.java
+++ b/service/java/com/android/safetycenter/notifications/package-info.java
@@ -14,6 +14,11 @@
* limitations under the License.
*/
@NonNullByDefault
+@RequiresApi(TIRAMISU)
package com.android.safetycenter.notifications;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.annotation.RequiresApi;
+
import com.android.safetycenter.annotations.NonNullByDefault;
diff --git a/service/java/com/android/safetycenter/package-info.java b/service/java/com/android/safetycenter/package-info.java
index 0bccf89fa..57142c658 100644
--- a/service/java/com/android/safetycenter/package-info.java
+++ b/service/java/com/android/safetycenter/package-info.java
@@ -14,6 +14,11 @@
* limitations under the License.
*/
@NonNullByDefault
+@RequiresApi(TIRAMISU)
package com.android.safetycenter;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.annotation.RequiresApi;
+
import com.android.safetycenter.annotations.NonNullByDefault;
diff --git a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
index 1806f8e13..d90ffade9 100644
--- a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
+++ b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
@@ -91,7 +91,7 @@ class RolesPersistenceTest {
.writeText("<roles version=\"-1\"><role name=\"com.foo.bar\"><holder")
val persistedState = persistence.readForUser(user)
- checkPersistedState(persistedState!!)
+ checkPersistedState(persistedState)
}
@Test
@@ -103,11 +103,11 @@ class RolesPersistenceTest {
assertThat(persistedState).isNull()
}
- private fun checkPersistedState(persistedState: RolesState) {
+ private fun checkPersistedState(persistedState: RolesState?) {
assertThat(persistedState).isEqualTo(state)
- assertThat(persistedState.version).isEqualTo(state.version)
- assertThat(persistedState.packagesHash).isEqualTo(state.packagesHash)
- assertThat(persistedState.roles).isEqualTo(state.roles)
+ assertThat(persistedState?.version).isEqualTo(state.version)
+ assertThat(persistedState?.packagesHash).isEqualTo(state.packagesHash)
+ assertThat(persistedState?.roles).isEqualTo(state.roles)
}
companion object {
diff --git a/tests/cts/safetycenter/AndroidTest.xml b/tests/cts/safetycenter/AndroidTest.xml
index 5ec0c380e..6d8c3069c 100644
--- a/tests/cts/safetycenter/AndroidTest.xml
+++ b/tests/cts/safetycenter/AndroidTest.xml
@@ -43,9 +43,12 @@
aren't polluted by `BOOT_COMPLETED` or similar broadcasts still being delivered, which
causes our `ActivityManager#waitForBroadcastIdle()` calls to timeout. -->
<option name="run-command" value="am wait-for-broadcast-idle" />
+ <option name="run-command" value="am wait-for-broadcast-barrier" />
<!-- 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" />
+ <!-- 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>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
index 33f52b564..c344d7ebd 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
@@ -19,7 +19,7 @@ package android.safetycenter.cts
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.os.Build
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.os.Bundle
import android.safetycenter.SafetyCenterData
import android.safetycenter.SafetyCenterEntry
@@ -164,7 +164,7 @@ class SafetyCenterDataTest {
SafetyCenterData(status2, listOf(issue2), listOf(entryOrGroup2), listOf(staticEntryGroup2))
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getStatus_withDefaultBuilder_returnsStatus() {
val safetyCenterData = SafetyCenterData.Builder(status1).build()
@@ -172,7 +172,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getIssues_withDefaultBuilder_returnsEmptyList() {
val safetyCenterData = SafetyCenterData.Builder(status1).build()
@@ -180,7 +180,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getIssues_whenSetExplicitly_returnsIssues() {
val safetyCenterData =
SafetyCenterData.Builder(status1).addIssue(issue1).addIssue(issue2).build()
@@ -189,7 +189,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getEntriesOrGroups_withDefaultBuilder_returnsEmptyList() {
val safetyCenterData = SafetyCenterData.Builder(status1).build()
@@ -197,7 +197,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getEntriesOrGroups_whenSetExplicitly_returnsEntriesOrGroups() {
val safetyCenterData =
SafetyCenterData.Builder(status1)
@@ -211,7 +211,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getStaticGroups_withDefaultBuilder_returnsEmptyList() {
val safetyCenterData = SafetyCenterData.Builder(status1).build()
@@ -219,7 +219,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getStaticEntryGroups_whenSetExplicitly_returnsStaticEntryGroups() {
val safetyCenterData =
SafetyCenterData.Builder(status1)
@@ -233,7 +233,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getDismissedIssues_withDefaultBuilder_returnsEmptyList() {
val safetyCenterData = SafetyCenterData.Builder(status1).build()
@@ -241,7 +241,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getDismissedIssues_whenSetExplicitly_returnsIssues() {
val safetyCenterData =
SafetyCenterData.Builder(status1)
@@ -253,7 +253,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getExtras_withDefaultBuilder_returnsEmptyBundle() {
val safetyCenterData = SafetyCenterData.Builder(status1).build()
@@ -261,7 +261,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getExtras_whenSetExplicitly_returnsExtras() {
val safetyCenterData =
SafetyCenterData.Builder(status1).setExtras(filledExtrasIssuesToGroups1).build()
@@ -275,7 +275,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getExtras_whenCleared_returnsEmptyBundle() {
val safetyCenterData =
SafetyCenterData.Builder(status1)
@@ -299,7 +299,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getDismissedIssues_returnsDismissedIssues() {
val data3 = data1.withDismissedIssuesIfAtLeastU(listOf(issue2))
@@ -308,7 +308,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getDismissedIssues_mutationsAreNotAllowed() {
val mutatedDismissedIssues = data1.dismissedIssues
@@ -353,7 +353,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun builder_addIssue_doesNotMutatePreviouslyBuiltInstance() {
val safetyCenterDataBuilder = SafetyCenterData.Builder(status1).addIssue(issue1)
val issues = safetyCenterDataBuilder.build().issues
@@ -364,7 +364,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun builder_clearIssues_removesAllIssues() {
val safetyCenterData =
SafetyCenterData.Builder(status1)
@@ -377,7 +377,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun builder_addEntryOrGroup_doesNotMutatePreviouslyBuiltInstance() {
val safetyCenterDataBuilder =
SafetyCenterData.Builder(status1).addEntryOrGroup(entryOrGroup1)
@@ -389,7 +389,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun builder_clearEntriesOrGroups_removesAllEntriesOrGroups() {
val safetyCenterData =
SafetyCenterData.Builder(status1)
@@ -402,7 +402,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun builder_addStaticEntryGroup_doesNotMutatePreviouslyBuiltInstance() {
val safetyCenterDataBuilder =
SafetyCenterData.Builder(status1).addStaticEntryGroup(staticEntryGroup1)
@@ -414,7 +414,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun builder_clearStaticEntryGroups_removesAllStaticEntryGroups() {
val safetyCenterData =
SafetyCenterData.Builder(status1)
@@ -427,7 +427,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun builder_addDismissedIssue_doesNotMutatePreviouslyBuiltInstance() {
val safetyCenterDataBuilder = SafetyCenterData.Builder(status1).addDismissedIssue(issue1)
val dismissedIssues = safetyCenterDataBuilder.build().dismissedIssues
@@ -438,7 +438,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun builder_clearDismissedIssues_removesAllDismissedIssues() {
val safetyCenterData =
SafetyCenterData.Builder(status1)
@@ -463,7 +463,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun parcelRoundTrip_withDismissedIssues_recreatesEqual() {
val data3 = data1.withDismissedIssuesIfAtLeastU(listOf(issue2))
val data4 = data2.withDismissedIssuesIfAtLeastU(listOf(issue1))
@@ -473,7 +473,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun parcelRoundTrip_withExtras_recreatesEqual() {
val safetyCenterDataWithExtras = data1.withExtrasIfAtLeastU(filledAllExtras)
val safetyCenterDatafromParcel =
@@ -545,7 +545,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun equalsHashCode_atLeastU_usingEqualsHashCodeToStringTester() {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = SafetyCenterData.CREATOR,
@@ -680,7 +680,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun toString_withSingleKnownExtra_containsKnownExtra() {
val safetyCenterDataWithExtras = data1.withExtrasIfAtLeastU(filledExtrasIssuesToGroups1)
@@ -693,7 +693,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun toString_withAllKnownExtras_containsKnownExtras() {
val safetyCenterDataWithAllExtras = data1.withExtrasIfAtLeastU(filledAllExtras)
@@ -706,7 +706,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun toString_withOneKnowAndOneUnknownExtra_containsKnownAndUnknownExtras() {
val safetyCenterDataWithExtras = data1.withExtrasIfAtLeastU(filledOneKnownOneUnknown)
@@ -719,7 +719,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun toString_withSingleUnknownExtra_containsUnknownExtras() {
val safetyCenterDataWithExtras = data1.withExtrasIfAtLeastU(unknownExtras)
@@ -732,7 +732,7 @@ class SafetyCenterDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun toString_withoutExtras_containsNoExtras() {
val safetyCenterDataWithoutExtras = data1
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
index 8d44f2736..be7ca343c 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
@@ -19,7 +19,6 @@ package android.safetycenter.cts
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.os.Build
import android.os.Build.VERSION_CODES.TIRAMISU
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.safetycenter.SafetyCenterIssue
@@ -31,8 +30,8 @@ import androidx.test.ext.truth.os.ParcelableSubject.assertThat
import androidx.test.filters.SdkSuppress
import com.android.safetycenter.testing.EqualsHashCodeToStringTester
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFails
import kotlin.test.assertFailsWith
-import org.junit.Assume.assumeFalse
import org.junit.Test
import org.junit.runner.RunWith
@@ -111,7 +110,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getAttributionTitle_returnsAttributionTitle() {
assertThat(
SafetyCenterIssue.Builder(issue1)
@@ -130,7 +129,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getAttributionTitle_withNullAttributionTitle_returnsNull() {
val safetyCenterIssue =
SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this")
@@ -228,7 +227,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getGroupId_withNonNullValue_returnsGroupId() {
val issue = SafetyCenterIssue.Builder(issue1).setGroupId("group_id").build()
@@ -236,7 +235,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getGroupId_withNullValue_returnsNull() {
val issue =
SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this")
@@ -247,23 +246,16 @@ class SafetyCenterIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun getGroupId_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun getGroupId_withVersionLessThanU_throws() {
val issue =
SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this")
.build()
- val exception = assertFailsWith(UnsupportedOperationException::class) { issue.groupId }
-
- assertThat(exception)
- .hasMessageThat()
- .isEqualTo("Method not supported for versions lower than UPSIDE_DOWN_CAKE")
+ assertFails { issue.groupId }
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setGroupId_withNullValue_returnsNull() {
val issue = SafetyCenterIssue.Builder(issue1).setGroupId(null).build()
@@ -272,19 +264,8 @@ class SafetyCenterIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun setGroupId_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
-
- val exception =
- assertFailsWith(UnsupportedOperationException::class) {
- SafetyCenterIssue.Builder(issue1).setGroupId("group_id").build()
- }
-
- assertThat(exception)
- .hasMessageThat()
- .isEqualTo("Method not supported for versions lower than UPSIDE_DOWN_CAKE")
+ fun setGroupId_withVersionLessThanU_throws() {
+ assertFails { SafetyCenterIssue.Builder(issue1).setGroupId("group_id").build() }
}
@Test
@@ -300,7 +281,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun parcelRoundTrip_recreatesEqual_atLeastAndroidU() {
val safetyCenterIssue =
SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this")
@@ -330,7 +311,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
newUpsideDownCakeEqualsHashCodeToStringTester().test()
}
@@ -373,21 +354,14 @@ class SafetyCenterIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun action_getConfirmationDialogDetails_withVersionLessThanU_throwsUnsupportedOperation() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
-
- assertFailsWith(UnsupportedOperationException::class) { action1.confirmationDialogDetails }
+ fun action_getConfirmationDialogDetails_withVersionLessThanU_throws() {
+ assertFails { action1.confirmationDialogDetails }
}
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun action_setConfirmationDialogDetails_withVersionLessThanU_throwsUnsupportedOperation() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
- assertFailsWith(UnsupportedOperationException::class) {
+ fun action_setConfirmationDialogDetails_withVersionLessThanU_throws() {
+ assertFails {
SafetyCenterIssue.Action.Builder("action_id", "Action label", pendingIntent1)
.setConfirmationDialogDetails(
ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -396,7 +370,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun action_getConfirmationDialogDetails_withDefaultBuilder_returnsNull() {
val action =
SafetyCenterIssue.Action.Builder("action_id", "Action label", pendingIntent1).build()
@@ -405,7 +379,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun action_getConfirmationDialogDetails_whenSetExplicitly_returnsConfirmation() {
val action =
SafetyCenterIssue.Action.Builder("action_id", "Action label", pendingIntent1)
@@ -431,7 +405,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun action_parcelRoundTrip_recreatesEqual_atLeastAndroidU() {
val action =
SafetyCenterIssue.Action.Builder(action1)
@@ -449,7 +423,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun action_equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
issueActionNewTiramisuEqualsHashCodeToStringTester(
@@ -596,7 +570,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_getTitle_returnsTitle() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -604,7 +578,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_getText_returnsText() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -612,7 +586,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_getAcceptButtonText_returnsAcceptButtonText() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -620,7 +594,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_getDenyButtonText_returnsDenyButtonText() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -628,7 +602,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_describeContents_returns0() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -636,7 +610,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_parcelRoundTrip_recreatesEqual() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -644,7 +618,7 @@ class SafetyCenterIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = ConfirmationDialogDetails.CREATOR
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
index 9f986c706..298d7643c 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
@@ -17,7 +17,6 @@
package android.safetycenter.cts
import android.content.Context
-import android.os.Build
import android.os.Build.VERSION_CODES.TIRAMISU
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.os.UserHandle.USER_NULL
@@ -40,7 +39,7 @@ import android.safetycenter.cts.testing.FakeExecutor
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG
import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.addOnSafetyCenterDataChangedListenerWithPermission
@@ -59,7 +58,6 @@ import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.set
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.setSafetySourceDataWithPermission
import com.android.safetycenter.testing.SafetyCenterEnabledChangedReceiver
import com.android.safetycenter.testing.SafetyCenterFlags
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestConfigs
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_ALL_OPTIONAL_ID
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_BAREBONE_ID
@@ -77,6 +75,7 @@ import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.STATIC
import com.android.safetycenter.testing.SafetyCenterTestData
import com.android.safetycenter.testing.SafetyCenterTestHelper
import com.android.safetycenter.testing.SafetyCenterTestListener
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceIntentHandler.Request
import com.android.safetycenter.testing.SafetySourceIntentHandler.Response
import com.android.safetycenter.testing.SafetySourceReceiver
@@ -89,15 +88,15 @@ import com.android.safetycenter.testing.SafetySourceTestData.Companion.CRITICAL_
import com.android.safetycenter.testing.SafetySourceTestData.Companion.CRITICAL_ISSUE_ID
import com.android.safetycenter.testing.SafetySourceTestData.Companion.EVENT_SOURCE_STATE_CHANGED
import com.android.safetycenter.testing.SafetySourceTestData.Companion.RECOMMENDATION_ISSUE_ID
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.google.common.base.Preconditions.checkState
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import kotlin.test.assertFails
import kotlin.test.assertFailsWith
import kotlinx.coroutines.TimeoutCancellationException
-import org.junit.After
-import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
-import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -105,36 +104,14 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SafetyCenterManagerTest {
private val context: Context = getApplicationContext()
- private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context)
+ private val safetyCenterResourcesApk = SafetyCenterResourcesApk.forTests(context)
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetySourceTestData = SafetySourceTestData(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
- // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
-
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
-
- @Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
- }
-
- @After
- fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
- }
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
@Test
fun isSafetyCenterEnabled_withFlagEnabled_returnsTrue() {
@@ -251,7 +228,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_wronglySignedPackage_throwsIllegalArgumentException() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceWithFakeCert)
@@ -266,7 +243,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_wronglySignedPackageButAllowedByFlag_isAllowed() {
SafetyCenterFlags.allowedAdditionalPackageCerts =
mapOf(context.packageName to setOf(safetyCenterTestConfigs.packageCertHash))
@@ -281,7 +258,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_invalidPackageCertificate_throwsIllegalArgumentException() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceWithInvalidCert)
@@ -309,9 +286,9 @@ class SafetyCenterManagerTest {
assertThat(thrown)
.hasMessageThat()
- .isEqualTo(
- "Safety source: $DYNAMIC_IN_STATELESS_ID is in a stateless group but specified a " +
- "severity level: $SEVERITY_LEVEL_INFORMATION"
+ .matches(
+ "Safety source: $DYNAMIC_IN_STATELESS_ID is in a (stateless|rigid) group but " +
+ "specified a severity level: $SEVERITY_LEVEL_INFORMATION"
)
}
@@ -834,10 +811,7 @@ class SafetyCenterManagerTest {
val enabledChangedReceiver = SafetyCenterEnabledChangedReceiver(context)
assertFailsWith(TimeoutCancellationException::class) {
- enabledChangedReceiver.setSafetyCenterEnabledWithoutReceiverPermissionAndWait(
- false,
- TIMEOUT_SHORT
- )
+ enabledChangedReceiver.setSafetyCenterEnabledWithoutReceiverPermissionAndWait(false)
}
enabledChangedReceiver.unregister()
}
@@ -874,10 +848,7 @@ class SafetyCenterManagerTest {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
assertFailsWith(TimeoutCancellationException::class) {
- SafetySourceReceiver.setSafetyCenterEnabledWithoutReceiverPermissionAndWait(
- false,
- TIMEOUT_SHORT
- )
+ SafetySourceReceiver.setSafetyCenterEnabledWithoutReceiverPermissionAndWait(false)
}
}
@@ -955,7 +926,7 @@ class SafetyCenterManagerTest {
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT
)
}
@@ -983,71 +954,6 @@ class SafetyCenterManagerTest {
}
@Test
- fun refreshSafetySources_reasonPageOpen_allowedByFlag_broadcastSent() {
- safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig)
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
- SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID)
- SafetySourceReceiver.setResponse(
- Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.informationWithIssue)
- )
-
- safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
- REFRESH_REASON_PAGE_OPEN
- )
-
- val apiSafetySourceData =
- safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
- assertThat(apiSafetySourceData).isEqualTo(safetySourceTestData.informationWithIssue)
- }
-
- @Test
- fun refreshSafetySources_reasonPageOpen_allowedByFlagLater_broadcastSentLater() {
- safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig)
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
- SafetySourceReceiver.setResponse(
- Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.informationWithIssue)
- )
-
- assertFailsWith(TimeoutCancellationException::class) {
- safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
- REFRESH_REASON_PAGE_OPEN,
- TIMEOUT_SHORT
- )
- }
- val apiSafetySourceDataBeforeSettingFlag =
- safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
- SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID)
- safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
- REFRESH_REASON_PAGE_OPEN
- )
- val apiSafetySourceDataAfterSettingFlag =
- safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
-
- assertThat(apiSafetySourceDataBeforeSettingFlag).isEqualTo(safetySourceTestData.information)
- assertThat(apiSafetySourceDataAfterSettingFlag)
- .isEqualTo(safetySourceTestData.informationWithIssue)
- }
-
- @Test
- fun refreshSafetySources_reasonPageOpen_noDataForSource_broadcastSent() {
- safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig)
- SafetySourceReceiver.setResponse(
- Request.Refresh(SINGLE_SOURCE_ID),
- Response.SetData(safetySourceTestData.information)
- )
-
- safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
- REFRESH_REASON_PAGE_OPEN
- )
-
- val apiSafetySourceData =
- safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
- assertThat(apiSafetySourceData).isEqualTo(safetySourceTestData.information)
- }
-
- @Test
fun refreshSafetySources_whenSourceClearsData_sourceSendsNullData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
@@ -1135,8 +1041,7 @@ class SafetyCenterManagerTest {
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithoutReceiverPermissionAndWait(
- REFRESH_REASON_RESCAN_BUTTON_CLICK,
- TIMEOUT_SHORT
+ REFRESH_REASON_RESCAN_BUTTON_CLICK
)
}
val apiSafetySourceData =
@@ -1154,7 +1059,7 @@ class SafetyCenterManagerTest {
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT
)
}
}
@@ -1409,7 +1314,7 @@ class SafetyCenterManagerTest {
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT
)
}
}
@@ -1493,7 +1398,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun refreshSafetySources_withRefreshReasonPeriodic_noBackgroundRefreshSourceDoesNotSendData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
@@ -1505,7 +1410,7 @@ class SafetyCenterManagerTest {
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PERIODIC,
- TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT
)
}
@@ -1515,7 +1420,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun refreshSafetySources_withRefreshReasonPeriodic_backgroundRefreshSourceSendsData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
@@ -1532,7 +1437,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun refreshSafetySources_withSafetySourceIds_onlySpecifiedSourcesSendData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
SafetySourceReceiver.apply {
@@ -1567,7 +1472,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun refreshSafetySources_withEmptySafetySourceIds_noSourcesSendData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
SafetySourceReceiver.setResponse(
@@ -1578,8 +1483,8 @@ class SafetyCenterManagerTest {
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- TIMEOUT_SHORT,
- emptyList()
+ safetySourceIds = emptyList(),
+ timeout = TIMEOUT_SHORT,
)
}
@@ -1589,27 +1494,19 @@ class SafetyCenterManagerTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun refreshSafetySources_versionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun refreshSafetySources_versionLessThanU_throws() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
- val exception =
- assertFailsWith(UnsupportedOperationException::class) {
- safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
- REFRESH_REASON_PAGE_OPEN,
- safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_3)
- )
- }
-
- assertThat(exception)
- .hasMessageThat()
- .isEqualTo("Method not supported for versions lower than UPSIDE_DOWN_CAKE")
+ assertFails {
+ safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+ REFRESH_REASON_PAGE_OPEN,
+ safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_3)
+ )
+ }
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun refreshSafetySources_withSafetySourceIds_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.refreshSafetySources(REFRESH_REASON_PAGE_OPEN, listOf())
@@ -2059,7 +1956,7 @@ class SafetyCenterManagerTest {
assertThat(error)
.isEqualTo(
SafetyCenterErrorDetails(
- safetyCenterResourcesContext.getStringByName("redirecting_error")
+ safetyCenterResourcesApk.getStringByName("redirecting_error")
)
)
}
@@ -2085,7 +1982,7 @@ class SafetyCenterManagerTest {
assertThat(error)
.isEqualTo(
SafetyCenterErrorDetails(
- safetyCenterResourcesContext.getStringByName("resolving_action_error")
+ safetyCenterResourcesApk.getStringByName("resolving_action_error")
)
)
}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
index 695265059..d882fc3cb 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
@@ -42,12 +42,12 @@ import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.rep
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.setSafetyCenterConfigForTestsWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.setSafetySourceDataWithPermission
import com.android.safetycenter.testing.SafetyCenterEnabledChangedReceiver
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestConfigs
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID
import com.android.safetycenter.testing.SafetyCenterTestData
import com.android.safetycenter.testing.SafetyCenterTestHelper
import com.android.safetycenter.testing.SafetyCenterTestListener
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceReceiver
import com.android.safetycenter.testing.SafetySourceReceiver.Companion.executeSafetyCenterIssueActionWithPermissionAndWait
import com.android.safetycenter.testing.SafetySourceReceiver.Companion.refreshSafetySourcesWithReceiverPermissionAndWait
@@ -56,14 +56,13 @@ import com.android.safetycenter.testing.SafetySourceTestData.Companion.CRITICAL_
import com.android.safetycenter.testing.SafetySourceTestData.Companion.CRITICAL_ISSUE_ID
import com.android.safetycenter.testing.SafetySourceTestData.Companion.EVENT_SOURCE_STATE_CHANGED
import com.android.safetycenter.testing.SettingsPackage.getSettingsPackageName
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.android.safetycenter.testing.UiTestHelper.waitDisplayed
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import kotlin.test.assertFailsWith
import kotlinx.coroutines.TimeoutCancellationException
-import org.junit.After
import org.junit.Assume.assumeTrue
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -72,39 +71,17 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SafetyCenterUnsupportedTest {
- @get:Rule val disableAnimationRule = DisableAnimationRule()
-
- @get:Rule val freezeRotationRule = FreezeRotationRule()
-
private val context: Context = getApplicationContext()
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetySourceTestData = SafetySourceTestData(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
- // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = !context.deviceSupportsSafetyCenter()
-
- @Before
- fun assumeDeviceDoesntSupportSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
-
- @Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
- }
- @After
- fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
- }
+ @get:Rule(order = 1)
+ val supportsSafetyCenterRule = SupportsSafetyCenterRule(context, requireSupportIs = false)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
+ @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule()
+ @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule()
@Test
fun launchActivity_opensSettings() {
@@ -238,7 +215,7 @@ class SafetyCenterUnsupportedTest {
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN,
- TIMEOUT_SHORT
+ timeout = TIMEOUT_SHORT
)
}
}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
index 3dec4f509..4d1cb6a8b 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
@@ -16,7 +16,7 @@
package android.safetycenter.cts
-import android.os.Build
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.safetycenter.SafetyEvent
import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED
import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED
@@ -190,7 +190,7 @@ class SafetyEventTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
newTiramisuEqualsHashCodeToStringTester(
createCopyFromBuilder = { SafetyEvent.Builder(it).build() }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
index d7208c098..ba2ab3d76 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
@@ -20,7 +20,7 @@ import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context
import android.content.Intent
-import android.os.Build
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.os.Bundle
import android.safetycenter.SafetySourceData
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING
@@ -96,7 +96,7 @@ class SafetySourceDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getExtras_withDefaultBuilder_returnsEmptyBundle() {
val safetySourceData =
SafetySourceData.Builder().setStatus(createStatus(SEVERITY_LEVEL_INFORMATION)).build()
@@ -105,7 +105,7 @@ class SafetySourceDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getExtras_whenSetExplicitly_returnsExtras() {
val safetySourceData =
SafetySourceData.Builder()
@@ -118,7 +118,7 @@ class SafetySourceDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getExtras_whenCleared_returnsEmptyBundle() {
val safetySourceData =
SafetySourceData.Builder()
@@ -325,7 +325,7 @@ class SafetySourceDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun parcelRoundTrip_withExtras_recreatesEqual() {
val safetySourceData =
SafetySourceData.Builder()
@@ -384,7 +384,7 @@ class SafetySourceDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun equalsHashCode_atLeastU_usingEqualsHashCodeToStringTester() {
val firstStatus = createStatus(SEVERITY_LEVEL_INFORMATION, 1)
val secondStatus = createStatus(SEVERITY_LEVEL_INFORMATION, 2)
@@ -454,7 +454,7 @@ class SafetySourceDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun toString_withExtras_containsHasExtras() {
val safetySourceDataWithExtras =
SafetySourceData.Builder()
@@ -468,7 +468,7 @@ class SafetySourceDataTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun toString_withoutExtras_doesNotContainHasExtras() {
val safetySourceDataWithoutExtras =
SafetySourceData.Builder().setStatus(createStatus(SEVERITY_LEVEL_INFORMATION)).build()
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
index 4749c3616..2d19a3175 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
@@ -20,7 +20,6 @@ import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context
import android.content.Intent
-import android.os.Build
import android.os.Build.VERSION_CODES.TIRAMISU
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING
@@ -42,8 +41,8 @@ import androidx.test.filters.SdkSuppress
import com.android.modules.utils.build.SdkLevel
import com.android.safetycenter.testing.EqualsHashCodeToStringTester
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFails
import kotlin.test.assertFailsWith
-import org.junit.Assume.assumeFalse
import org.junit.Test
import org.junit.runner.RunWith
@@ -119,22 +118,16 @@ class SafetySourceIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun action_getConfirmationDialogDetails_withVersionLessThanU_throwsUnsupportedOperation() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun action_getConfirmationDialogDetails_withVersionLessThanU_throws() {
val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
- assertFailsWith(UnsupportedOperationException::class) { action.confirmationDialogDetails }
+ assertFails { action.confirmationDialogDetails }
}
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun action_setConfirmationDialogDetails_withVersionLessThanU_throwsUnsupportedOperation() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
- assertFailsWith(UnsupportedOperationException::class) {
+ fun action_setConfirmationDialogDetails_withVersionLessThanU_throws() {
+ assertFails {
Action.Builder("action_id", "Action label", pendingIntent1)
.setConfirmationDialogDetails(
ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -143,7 +136,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun action_getConfirmationDialogDetails_withDefaultBuilder_returnsNull() {
val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
@@ -151,7 +144,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun action_getConfirmationDialogDetails_whenSetExplicitly_returnsConfirmation() {
val action =
Action.Builder("action_id", "Action label", pendingIntent1)
@@ -186,7 +179,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun action_build_withActivityPendingIntentAndWillResolve_throwsIllegalArgumentException() {
assertFailsWith(IllegalArgumentException::class) {
Action.Builder("action_id", "Action label", pendingIntent1).setWillResolve(true).build()
@@ -211,7 +204,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun action_parcelRoundTrip_recreatesEqual_atLeastAndroidU() {
val action =
Action.Builder("action_id", "Action label", pendingIntent1)
@@ -229,7 +222,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun action_equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
actionNewTiramisuEqualsHashCodeToStringTester(
@@ -293,7 +286,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_getTitle_returnsTitle() {
val notification = Notification.Builder("Notification title", "Notification text").build()
@@ -301,7 +294,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_getText_returnsText() {
val notification = Notification.Builder("Notification title", "Notification text").build()
@@ -309,7 +302,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_getActions_withDefaultBuilder_returnsEmptyList() {
val notification = Notification.Builder("", "").build()
@@ -317,7 +310,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_getActions_returnsActions() {
val notification =
Notification.Builder("", "").addAction(action1).addAction(action2).build()
@@ -326,7 +319,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_getActions_mutationsAreNotAllowed() {
val notification =
Notification.Builder("", "").addAction(action1).addAction(action2).build()
@@ -335,7 +328,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_describeContents_returns0() {
val notification =
Notification.Builder("Notification title", "Notification text")
@@ -347,7 +340,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_parcelRoundTrip_recreatesEqual() {
val notification =
Notification.Builder("Notification title", "Notification text")
@@ -359,7 +352,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_builder_withNullTitle_throwsNullPointerException() {
assertFailsWith(NullPointerException::class) {
Notification.Builder(Generic.asNull(), "Notification text")
@@ -367,7 +360,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_builder_withNullText_throwsNullPointerException() {
assertFailsWith(NullPointerException::class) {
Notification.Builder("Notification title", Generic.asNull())
@@ -375,7 +368,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_builder_addAction_doesNotMutatePreviouslyBuiltInstance() {
val notificationBuilder = Notification.Builder("", "").addAction(action1)
val actions = notificationBuilder.build().actions
@@ -386,7 +379,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_builder_addAction_withNull_throwsIllegalArgumentException() {
assertFailsWith(NullPointerException::class) {
Notification.Builder("", "").addAction(Generic.asNull())
@@ -394,7 +387,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_builder_addActions_keepsPreviouslyAddedActions() {
val notificationBuilder = Notification.Builder("", "").addAction(action1)
@@ -404,7 +397,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_builder_addActions_doesNotMutatePreviouslyBuiltInstance() {
val notificationBuilder = Notification.Builder("", "").addActions(listOf(action1))
val actions = notificationBuilder.build().actions
@@ -415,7 +408,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_builder_addActions_withNull_throwsIllegalArgumentException() {
assertFailsWith(NullPointerException::class) {
Notification.Builder("", "").addActions(Generic.asNull())
@@ -423,7 +416,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_builder_clearActions_removesAllActions() {
val notification =
Notification.Builder("", "")
@@ -437,7 +430,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_build_withDuplicateActionIds_throwsIllegalArgumentException() {
val notificationBuilder =
Notification.Builder("Notification title", "Notification text")
@@ -452,7 +445,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_build_withMoreThanTwoActions_throwsIllegalArgumentException() {
val notificationBuilder =
Notification.Builder("Notification title", "Notification text")
@@ -468,7 +461,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun notification_equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = Notification.CREATOR,
@@ -490,7 +483,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_getTitle_returnsTitle() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -498,7 +491,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_getText_returnsText() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -506,7 +499,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_getAcceptButtonText_returnsAcceptButtonText() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -514,7 +507,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_getDenyButtonText_returnsDenyButtonText() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -522,7 +515,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_describeContents_returns0() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -530,7 +523,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_parcelRoundTrip_recreatesEqual() {
val confirmationDialogDetails = ConfirmationDialogDetails("Title", "Text", "Accept", "Deny")
@@ -538,7 +531,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun actionConfirmation_equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
EqualsHashCodeToStringTester.ofParcelable(
parcelableCreator = ConfirmationDialogDetails.CREATOR
@@ -636,7 +629,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getAttributionTitle_withNullAttributionTitle_returnsNull() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -653,7 +646,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getAttributionTitle_returnsAttributionTitle() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -672,10 +665,7 @@ class SafetySourceIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun getAttributionTitle_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun getAttributionTitle_withVersionLessThanU_throws() {
val safetySourceIssue =
SafetySourceIssue.Builder(
"Issue id",
@@ -687,15 +677,12 @@ class SafetySourceIssueTest {
.addAction(action1)
.build()
- assertFailsWith(UnsupportedOperationException::class) { safetySourceIssue.attributionTitle }
+ assertFails { safetySourceIssue.attributionTitle }
}
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun setAttributionTitle_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun setAttributionTitle_withVersionLessThanU_throws() {
val safetySourceIssueBuilder =
SafetySourceIssue.Builder(
"Issue id",
@@ -705,9 +692,7 @@ class SafetySourceIssueTest {
"issue_type_id"
)
- assertFailsWith(UnsupportedOperationException::class) {
- safetySourceIssueBuilder.setAttributionTitle("title")
- }
+ assertFails { safetySourceIssueBuilder.setAttributionTitle("title") }
}
@Test
@@ -760,7 +745,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getIssueCategory_whenSetExplicitlyWithUValueOnU_returnsIssueCategory() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -884,7 +869,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getDeduplicationId_withDefaultBuilder_returnsNull() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -901,7 +886,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getDeduplicationId_whenSetExplicitly_returnsDeduplicationId() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -920,10 +905,7 @@ class SafetySourceIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun getDeduplicationId_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun getDeduplicationId_withVersionLessThanU_throws() {
val safetySourceIssue =
SafetySourceIssue.Builder(
"Issue id",
@@ -935,15 +917,12 @@ class SafetySourceIssueTest {
.addAction(action1)
.build()
- assertFailsWith(UnsupportedOperationException::class) { safetySourceIssue.deduplicationId }
+ assertFails { safetySourceIssue.deduplicationId }
}
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun setDeduplicationId_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun setDeduplicationId_withVersionLessThanU_throws() {
val safetySourceIssueBuilder =
SafetySourceIssue.Builder(
"Issue id",
@@ -953,9 +932,7 @@ class SafetySourceIssueTest {
"issue_type_id"
)
- assertFailsWith(UnsupportedOperationException::class) {
- safetySourceIssueBuilder.setDeduplicationId("id")
- }
+ assertFails { safetySourceIssueBuilder.setDeduplicationId("id") }
}
@Test
@@ -975,7 +952,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getCustomNotification_withDefaultBuilder_returnsNull() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -992,7 +969,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getCustomNotification_whenSetExplicitly_returnsCustomNotification() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -1020,10 +997,7 @@ class SafetySourceIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun getCustomNotification_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun getCustomNotification_withVersionLessThanU_throws() {
val safetySourceIssue =
SafetySourceIssue.Builder(
"Issue id",
@@ -1035,17 +1009,12 @@ class SafetySourceIssueTest {
.addAction(action1)
.build()
- assertFailsWith(UnsupportedOperationException::class) {
- safetySourceIssue.customNotification
- }
+ assertFails { safetySourceIssue.customNotification }
}
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun setCustomNotification_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun setCustomNotification_withVersionLessThanU_throws() {
val safetySourceIssueBuilder =
SafetySourceIssue.Builder(
"Issue id",
@@ -1055,13 +1024,11 @@ class SafetySourceIssueTest {
"issue_type_id"
)
- assertFailsWith(UnsupportedOperationException::class) {
- safetySourceIssueBuilder.setCustomNotification(null)
- }
+ assertFails { safetySourceIssueBuilder.setCustomNotification(null) }
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getNotificationBehavior_withDefaultBuilder_returnsUnspecified() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -1079,7 +1046,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getNotificationBehavior_whenSetExplicitly_returnsSpecifiedBehavior() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -1099,10 +1066,7 @@ class SafetySourceIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun getNotificationBehavior_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun getNotificationBehavior_withVersionLessThanU_throws() {
val safetySourceIssue =
SafetySourceIssue.Builder(
"Issue id",
@@ -1114,13 +1078,11 @@ class SafetySourceIssueTest {
.addAction(action1)
.build()
- assertFailsWith(UnsupportedOperationException::class) {
- safetySourceIssue.notificationBehavior
- }
+ assertFails { safetySourceIssue.notificationBehavior }
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setNotificationBehavior_withInvalidNotificationBehavior_throwsIllegalArgumentException() {
val builder =
SafetySourceIssue.Builder(
@@ -1141,10 +1103,7 @@ class SafetySourceIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun setNotificationBehavior_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun setNotificationBehavior_withVersionLessThanU_throws() {
val safetySourceIssueBuilder =
SafetySourceIssue.Builder(
"Issue id",
@@ -1154,13 +1113,11 @@ class SafetySourceIssueTest {
"issue_type_id"
)
- assertFailsWith(UnsupportedOperationException::class) {
- safetySourceIssueBuilder.setNotificationBehavior(0)
- }
+ assertFails { safetySourceIssueBuilder.setNotificationBehavior(0) }
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getIssueActionability_withDefaultBuilder_returnsManual() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -1178,7 +1135,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getIssueActionability_whenSetExplicitly_returnsValueSet() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -1198,10 +1155,7 @@ class SafetySourceIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun getIssueActionability_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun getIssueActionability_withVersionLessThanU_throws() {
val safetySourceIssue =
SafetySourceIssue.Builder(
"Issue id",
@@ -1213,13 +1167,11 @@ class SafetySourceIssueTest {
.addAction(action1)
.build()
- assertFailsWith(UnsupportedOperationException::class) {
- safetySourceIssue.issueActionability
- }
+ assertFails { safetySourceIssue.issueActionability }
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setIssueActionability_withInvalidIssueActionability_throwsIllegalArgumentException() {
val builder =
SafetySourceIssue.Builder(
@@ -1240,10 +1192,7 @@ class SafetySourceIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
- fun setIssueActionability_withVersionLessThanU_throwsUnsupportedOperationException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
+ fun setIssueActionability_withVersionLessThanU_throws() {
val safetySourceIssueBuilder =
SafetySourceIssue.Builder(
"Issue id",
@@ -1253,9 +1202,7 @@ class SafetySourceIssueTest {
"issue_type_id"
)
- assertFailsWith(UnsupportedOperationException::class) {
- safetySourceIssueBuilder.setIssueActionability(0)
- }
+ assertFails { safetySourceIssueBuilder.setIssueActionability(0) }
}
@Test
@@ -1366,9 +1313,6 @@ class SafetySourceIssueTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
fun build_withUIssueCategoryValueOnT_throwsIllegalArgumentException() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
val builder =
SafetySourceIssue.Builder(
"Issue id",
@@ -1480,7 +1424,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun build_withNoActionsAndManualActionabilityOnU_throwsIllegalArgumentException() {
val safetySourceIssueBuilder =
SafetySourceIssue.Builder(
@@ -1500,7 +1444,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun build_withNoActionsAndTipActionabilityOnU_success() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -1517,7 +1461,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun build_withNoActionsAndAutomaticActionabilityOnU_success() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -1574,7 +1518,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun parcelRoundTrip_recreatesEqual_atLeastAndroidU() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -1601,7 +1545,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun parcelRoundTrip_recreatesEqual_atLeastUpsideDownCake() {
val safetySourceIssue =
SafetySourceIssue.Builder(
@@ -1636,7 +1580,7 @@ class SafetySourceIssueTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastUpsideDownCake() {
newUpsideDownCakeEqualsHashCodeToStringTester().test()
}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
index 5aeea21f1..2a20cd45d 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
@@ -20,7 +20,7 @@ import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context
import android.content.Intent
-import android.os.Build
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
@@ -285,7 +285,7 @@ class SafetySourceStatusTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
newTiramisuEqualsHashCodeToStringTester(
createCopyFromBuilder = { SafetySourceStatus.Builder(it).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 7dff9dd54..68dcd4a11 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt
@@ -16,7 +16,7 @@
package android.safetycenter.cts.config
-import android.os.Build
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.safetycenter.config.SafetyCenterConfig
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.truth.os.ParcelableSubject.assertThat
@@ -86,7 +86,7 @@ class SafetyCenterConfigTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
newTiramisuEqualsHashCodeToStringTester(
createCopyFromBuilder = { SafetyCenterConfig.Builder(it).build() }
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 055e82ad3..59cc6547a 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
@@ -17,7 +17,7 @@
package android.safetycenter.cts.config
import android.content.res.Resources
-import android.os.Build
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.safetycenter.config.SafetySource
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.truth.os.ParcelableSubject.assertThat
@@ -75,7 +75,7 @@ class SafetySourceTest {
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getOptionalPackageName_returnsPackageNameOrNull() {
assertThat(DYNAMIC_BAREBONE.optionalPackageName).isEqualTo(PACKAGE_NAME)
assertThat(dynamicAllOptional().optionalPackageName).isEqualTo(PACKAGE_NAME)
@@ -259,7 +259,7 @@ class SafetySourceTest {
assertThat(issueOnlyAllOptional().isRefreshOnPageOpenAllowed).isEqualTo(true)
}
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
@Test
fun areNotificationsAllowed_returnsNotificationsAllowed() {
assertThat(DYNAMIC_BAREBONE.areNotificationsAllowed()).isFalse()
@@ -273,7 +273,7 @@ class SafetySourceTest {
assertThat(issueOnlyAllOptional().areNotificationsAllowed()).isTrue()
}
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
@Test
fun getDeduplicationGroupsList_returnsDeduplicationGroups() {
assertThat(DYNAMIC_BAREBONE.deduplicationGroup).isNull()
@@ -287,7 +287,7 @@ class SafetySourceTest {
assertThat(issueOnlyAllOptional().deduplicationGroup).isEqualTo(DEDUPLICATION_GROUP)
}
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
@Test
fun getPackageCertificateHashes_returnsPackageCerts() {
assertThat(DYNAMIC_BAREBONE.packageCertificateHashes).isEmpty()
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 015d18842..f741369eb 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt
@@ -211,7 +211,7 @@ class SafetySourcesGroupTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun build_hiddenGroupWithDynamicSource_throwsIllegalStateException() {
val builder =
SafetySourcesGroup.Builder()
@@ -228,7 +228,7 @@ class SafetySourcesGroupTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun build_hiddenGroupWithStaticSource_throwsIllegalStateException() {
val builder =
SafetySourcesGroup.Builder()
@@ -245,7 +245,7 @@ class SafetySourcesGroupTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun build_statefulGroupWithIssueOnlySource_throwsIllegalStateException() {
val builder =
SafetySourcesGroup.Builder()
@@ -264,7 +264,7 @@ class SafetySourcesGroupTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun build_statelessGroupWithIssueOnlySource_throwsIllegalStateException() {
val builder =
SafetySourcesGroup.Builder()
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 32959629c..7c9e2cffc 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt
@@ -24,15 +24,14 @@ import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.safetycenter.config.SafetyCenterConfigParser
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetyCenterConfigWithPermission
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import org.junit.After
-import org.junit.Assume.assumeTrue
-import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -40,33 +39,12 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class XmlConfigTest {
private val context: Context = getApplicationContext()
- private val safetyCenterContext = SafetyCenterResourcesContext.forTests(context)
- private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
+ private val safetyCenterResourcesApk = SafetyCenterResourcesApk.forTests(context)
private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
- // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
-
- @Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
- }
-
- @After
- fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
- }
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2)
+ val safetyCenterTestRule = SafetyCenterTestRule(SafetyCenterTestHelper(context))
@Test
fun safetyCenterConfigResource_validConfig() {
@@ -93,7 +71,7 @@ class XmlConfigTest {
}
private fun assertThatIntentResolves(intentAction: String) {
- val pm = safetyCenterContext.packageManager
+ val pm = context.packageManager
assertWithMessage("Intent '%s' cannot be resolved.", intentAction)
.that(pm.queryIntentActivities(Intent(intentAction), ResolveInfoFlags.of(0)))
.isNotEmpty()
@@ -109,8 +87,8 @@ class XmlConfigTest {
private fun parseXmlConfig() =
SafetyCenterConfigParser.parseXmlResource(
- safetyCenterContext.safetyCenterConfig!!,
- safetyCenterContext.resources!!
+ safetyCenterResourcesApk.safetyCenterConfig!!,
+ safetyCenterResourcesApk.resources
)
companion object {
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt
index 8fb56b09e..111f01243 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt
@@ -24,15 +24,14 @@ import com.android.compatibility.common.util.DisableAnimationRule
import com.android.compatibility.common.util.FreezeRotationRule
import com.android.compatibility.common.util.UiAutomatorUtils2.getUiDevice
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SettingsPackage.getSettingsPackageName
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.android.safetycenter.testing.UiTestHelper.resetRotation
import com.android.safetycenter.testing.UiTestHelper.waitDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitPageTitleDisplayed
import org.junit.After
-import org.junit.Assume.assumeTrue
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,36 +40,16 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SafetyCenterActivityTest {
- @get:Rule val disableAnimationRule = DisableAnimationRule()
-
- @get:Rule val freezeRotationRule = FreezeRotationRule()
-
private val context: Context = getApplicationContext()
-
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
- // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
-
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
- @Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
- }
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
+ @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule()
+ @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule()
@After
fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
getUiDevice().resetRotation()
}
diff --git a/tests/functional/safetycenter/multiusers/AndroidTest.xml b/tests/functional/safetycenter/multiusers/AndroidTest.xml
index c1e19d2e4..20032357a 100644
--- a/tests/functional/safetycenter/multiusers/AndroidTest.xml
+++ b/tests/functional/safetycenter/multiusers/AndroidTest.xml
@@ -43,9 +43,12 @@
aren't polluted by `BOOT_COMPLETED` or similar broadcasts still being delivered, which
causes our `ActivityManager#waitForBroadcastIdle()` calls to timeout. -->
<option name="run-command" value="am wait-for-broadcast-idle" />
+ <option name="run-command" value="am wait-for-broadcast-barrier" />
<!-- 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" />
+ <!-- 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>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/functional/safetycenter/multiusers/TEST_MAPPING b/tests/functional/safetycenter/multiusers/TEST_MAPPING
index aedf92b86..65f53b453 100644
--- a/tests/functional/safetycenter/multiusers/TEST_MAPPING
+++ b/tests/functional/safetycenter/multiusers/TEST_MAPPING
@@ -1,30 +1,11 @@
{
- "presubmit": [
- {
- "name": "SafetyCenterFunctionalMultiUsersTestCases",
- "options": [
- {
- "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
- }
- ]
- }
- ],
"postsubmit": [
+ // Note that these test cases are running in postsubmit only. Bedstead tests can be pretty slow
+ // (due to adding/removing users, which makes the device somewhat unresponsive). This can also
+ // cause flakyness due to timeouts. In postsubmit, these tests have more time to run which makes
+ // them less flaky (and this flakyness is arguably less of an issue in this case).
{
"name": "SafetyCenterFunctionalMultiUsersTestCases"
}
- ],
- "mainline-presubmit": [
- {
- "name": "SafetyCenterFunctionalMultiUsersTestCases[com.google.android.permission.apex]",
- "options": [
- {
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
- "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
- }
- ]
- }
]
}
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 07ae129ec..acbc5cfc0 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
@@ -20,9 +20,9 @@ import android.Manifest.permission.INTERACT_ACROSS_USERS
import android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
import android.app.PendingIntent
import android.content.Context
-import android.content.Intent
import android.os.UserHandle
import android.safetycenter.SafetyCenterData
+import android.safetycenter.SafetyCenterEntry
import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING
import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNKNOWN
import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED
@@ -30,32 +30,35 @@ import android.safetycenter.SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_
import android.safetycenter.SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY
import android.safetycenter.SafetyCenterEntryGroup
import android.safetycenter.SafetyCenterEntryOrGroup
+import android.safetycenter.SafetyCenterIssue
import android.safetycenter.SafetyCenterManager
import android.safetycenter.SafetyCenterStaticEntry
import android.safetycenter.SafetyCenterStaticEntryGroup
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.harrier.annotations.EnsureHasNoWorkProfile
import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile
-import com.android.bedstead.harrier.annotations.Postsubmit
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner
import com.android.bedstead.nene.TestApis
import com.android.bedstead.nene.types.OptionalBoolean.TRUE
import com.android.compatibility.common.util.DisableAnimationRule
import com.android.compatibility.common.util.FreezeRotationRule
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
+import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT
+import com.android.safetycenter.testing.NotificationCharacteristics
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetyCenterDataWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetySourceDataWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.setSafetySourceDataWithPermission
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
+import com.android.safetycenter.testing.SafetyCenterFlags
import com.android.safetycenter.testing.SafetyCenterTestConfigs
-import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_BAREBONE_ID
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_DISABLED_ID
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_GROUP_ID
@@ -76,27 +79,30 @@ import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.STATIC
import com.android.safetycenter.testing.SafetyCenterTestData
import com.android.safetycenter.testing.SafetyCenterTestData.Companion.withoutExtras
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceTestData
import com.android.safetycenter.testing.SafetySourceTestData.Companion.EVENT_SOURCE_STATE_CHANGED
+import com.android.safetycenter.testing.SafetySourceTestData.Companion.ISSUE_TYPE_ID
import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
+import com.android.safetycenter.testing.TestNotificationListener
import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitAllTextNotDisplayed
import com.google.common.base.Preconditions.checkState
import com.google.common.truth.Truth.assertThat
import kotlin.test.assertFailsWith
import org.junit.After
-import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.ClassRule
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Functional tests for our APIs and UI on a device with multiple users. e.g. with a managed or
- * secondary user(s).
+ * additional user(s).
*/
+@LargeTest
@RunWith(BedsteadJUnit4::class)
class SafetyCenterMultiUsersTest {
@@ -104,70 +110,65 @@ class SafetyCenterMultiUsersTest {
@JvmField @ClassRule @Rule val deviceState: DeviceState = DeviceState()
}
- @get:Rule val disableAnimationRule = DisableAnimationRule()
-
- @get:Rule val freezeRotationRule = FreezeRotationRule()
-
private val context: Context = ApplicationProvider.getApplicationContext()
- private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context)
+ private val safetyCenterResourcesApk = SafetyCenterResourcesApk.forTests(context)
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetySourceTestData = SafetySourceTestData(context)
private val safetyCenterTestData = SafetyCenterTestData(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
- // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
private var inQuietMode = false
- private val primaryProfileOnlyIssues =
- listOf(
- safetyCenterTestData.safetyCenterIssueCritical(
- DYNAMIC_BAREBONE_ID,
- groupId = DYNAMIC_GROUP_ID
- ),
- safetyCenterTestData.safetyCenterIssueCritical(
- ISSUE_ONLY_BAREBONE_ID,
- attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
- ),
- safetyCenterTestData.safetyCenterIssueRecommendation(
- DYNAMIC_DISABLED_ID,
- groupId = DYNAMIC_GROUP_ID
- ),
- safetyCenterTestData.safetyCenterIssueRecommendation(
- ISSUE_ONLY_ALL_OPTIONAL_ID,
- attributionTitle = null,
- groupId = ISSUE_ONLY_GROUP_ID
- ),
- safetyCenterTestData.safetyCenterIssueInformation(
- DYNAMIC_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
- ),
- safetyCenterTestData.safetyCenterIssueInformation(
- ISSUE_ONLY_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ private val primaryProfileOnlyIssues: List<SafetyCenterIssue>
+ get() =
+ listOf(
+ safetyCenterTestData.safetyCenterIssueCritical(
+ DYNAMIC_BAREBONE_ID,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueCritical(
+ ISSUE_ONLY_BAREBONE_ID,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueRecommendation(
+ DYNAMIC_DISABLED_ID,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueRecommendation(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_IN_STATELESS_ID,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ groupId = MIXED_STATELESS_GROUP_ID
+ )
)
- )
- private val dynamicBareboneDefault =
- safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_BAREBONE_ID)
+ private val dynamicBareboneDefault: SafetyCenterEntry
+ get() = safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_BAREBONE_ID)
- private val dynamicBareboneUpdated =
- safetyCenterTestData.safetyCenterEntryCritical(DYNAMIC_BAREBONE_ID)
+ private val dynamicBareboneUpdated: SafetyCenterEntry
+ get() = safetyCenterTestData.safetyCenterEntryCritical(DYNAMIC_BAREBONE_ID)
- private val dynamicDisabledDefault =
- safetyCenterTestData
- .safetyCenterEntryDefaultBuilder(DYNAMIC_DISABLED_ID)
- .setPendingIntent(null)
- .setEnabled(false)
- .build()
+ private val dynamicDisabledDefault: SafetyCenterEntry
+ get() =
+ safetyCenterTestData
+ .safetyCenterEntryDefaultBuilder(DYNAMIC_DISABLED_ID)
+ .setPendingIntent(null)
+ .setEnabled(false)
+ .build()
- private val dynamicDisabledUpdated =
- safetyCenterTestData.safetyCenterEntryRecommendation(DYNAMIC_DISABLED_ID)
+ private val dynamicDisabledUpdated: SafetyCenterEntry
+ get() = safetyCenterTestData.safetyCenterEntryRecommendation(DYNAMIC_DISABLED_ID)
- private val dynamicDisabledForWorkDefaultBuilder
+ private val dynamicDisabledForWorkDefaultBuilder: SafetyCenterEntry.Builder
get() =
safetyCenterTestData
.safetyCenterEntryDefaultBuilder(
@@ -178,26 +179,54 @@ class SafetyCenterMultiUsersTest {
.setPendingIntent(null)
.setEnabled(false)
- private val dynamicDisabledForWorkDefault
+ private val dynamicDisabledForWorkDefault: SafetyCenterEntry
get() = dynamicDisabledForWorkDefaultBuilder.build()
- private val dynamicDisabledForWorkPaused
+ private val dynamicDisabledForWorkPausedUpdated: SafetyCenterEntry
get() =
- dynamicDisabledForWorkDefaultBuilder
- // TODO(b/233188021): This needs to use the Enterprise API to override the "work"
- // keyword.
- .setSummary(safetyCenterResourcesContext.getStringByName("work_profile_paused"))
+ safetyCenterTestData
+ .safetyCenterEntryDefaultBuilder(
+ DYNAMIC_DISABLED_ID,
+ deviceState.workProfile().id(),
+ title = "Ok title for Work",
+ pendingIntent = null
+ )
+ .setSummary(
+ safetyCenterResourcesApk.getStringByName("work_profile_paused"),
+ )
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
+ .setEnabled(false)
.build()
- private val dynamicDisabledForWorkUpdated
+ private val dynamicDisabledForWorkUpdated: SafetyCenterEntry
get() = safetyCenterEntryOkForWork(DYNAMIC_DISABLED_ID, deviceState.workProfile().id())
- private val dynamicHiddenUpdated =
- safetyCenterTestData.safetyCenterEntryUnspecified(DYNAMIC_HIDDEN_ID, pendingIntent = null)
+ private val dynamicHiddenUpdated: SafetyCenterEntry
+ get() =
+ safetyCenterTestData.safetyCenterEntryUnspecified(
+ DYNAMIC_HIDDEN_ID,
+ pendingIntent = null
+ )
- private val dynamicHiddenForWorkUpdated
+ private val dynamicHiddenForWorkUpdated: SafetyCenterEntry
get() = safetyCenterEntryOkForWork(DYNAMIC_HIDDEN_ID, deviceState.workProfile().id())
+ private val dynamicHiddenForWorkPausedUpdated
+ get() =
+ safetyCenterTestData
+ .safetyCenterEntryDefaultBuilder(
+ DYNAMIC_HIDDEN_ID,
+ deviceState.workProfile().id(),
+ title = "Ok title for Work",
+ pendingIntent = null
+ )
+ .setSummary(
+ safetyCenterResourcesApk.getStringByName("work_profile_paused"),
+ )
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
+ .setEnabled(false)
+ .build()
+
private val staticGroupBuilder =
SafetyCenterEntryGroup.Builder(STATIC_GROUP_ID, "OK")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
@@ -223,7 +252,8 @@ class SafetyCenterMultiUsersTest {
)
.setPendingIntent(
createTestActivityRedirectPendingIntentForUser(
- deviceState.workProfile().userHandle()
+ deviceState.workProfile().userHandle(),
+ explicit = false
)
)
@@ -233,50 +263,55 @@ class SafetyCenterMultiUsersTest {
private val staticAllOptionalForWorkPaused
get() =
staticAllOptionalForWorkBuilder
- // TODO(b/233188021): This needs to use the Enterprise API to override the "work"
- // keyword.
- .setSummary(safetyCenterResourcesContext.getStringByName("work_profile_paused"))
+ .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused"))
.setEnabled(false)
.build()
- private val staticEntry =
+ private fun createStaticEntry(explicit: Boolean = true): SafetyCenterStaticEntry =
SafetyCenterStaticEntry.Builder("OK")
.setSummary("OK")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build()
-
- private val staticEntryUpdated =
- SafetyCenterStaticEntry.Builder("Unspecified title")
- .setSummary("Unspecified summary")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent(explicit)
+ )
.build()
- private val staticEntryForWorkBuilder
+ private val staticEntryUpdated: SafetyCenterStaticEntry
get() =
- SafetyCenterStaticEntry.Builder("Paste")
- .setSummary("OK")
- .setPendingIntent(
- createTestActivityRedirectPendingIntentForUser(
- deviceState.workProfile().userHandle()
- )
+ SafetyCenterStaticEntry.Builder("Unspecified title")
+ .setSummary("Unspecified summary")
+ .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
+ .build()
+
+ private fun staticEntryForWorkBuilder(title: CharSequence = "Paste", explicit: Boolean = true) =
+ SafetyCenterStaticEntry.Builder(title)
+ .setSummary("OK")
+ .setPendingIntent(
+ createTestActivityRedirectPendingIntentForUser(
+ deviceState.workProfile().userHandle(),
+ explicit
)
+ )
- private val staticEntryForWork
- get() = staticEntryForWorkBuilder.build()
+ private fun createStaticEntryForWork(explicit: Boolean = true): SafetyCenterStaticEntry =
+ staticEntryForWorkBuilder(explicit = explicit).build()
- private val staticEntryForWorkPaused
+ private fun createStaticEntryForWorkPaused(): SafetyCenterStaticEntry =
+ staticEntryForWorkBuilder(explicit = false)
+ .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused"))
+ .build()
+
+ private val staticEntryForWorkPausedUpdated: SafetyCenterStaticEntry
get() =
- staticEntryForWorkBuilder
- // TODO(b/233188021): This needs to use the Enterprise API to override the "work"
- // keyword.
- .setSummary(safetyCenterResourcesContext.getStringByName("work_profile_paused"))
+ staticEntryForWorkBuilder(title = "Unspecified title for Work")
+ .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused"))
.build()
- private val staticEntryForWorkUpdated =
- SafetyCenterStaticEntry.Builder("Unspecified title for Work")
- .setSummary("Unspecified summary")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build()
+ private val staticEntryForWorkUpdated: SafetyCenterStaticEntry
+ get() =
+ SafetyCenterStaticEntry.Builder("Unspecified title for Work")
+ .setSummary("Unspecified summary")
+ .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
+ .build()
private val safetyCenterDataForAdditionalUser
get() =
@@ -298,34 +333,26 @@ class SafetyCenterMultiUsersTest {
emptyList()
)
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2)
+ val safetyCenterTestRule =
+ SafetyCenterTestRule(safetyCenterTestHelper, withNotifications = true)
+ @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule()
+ @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule()
@Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
+ fun setRefreshTimeoutsBeforeTest() {
+ SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_SHORT)
}
@After
- fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
- resetQuietMode()
+ fun resetQuietModeAfterTest() {
+ setQuietMode(false)
}
@Test
@EnsureHasWorkProfile
- @Ignore
- // Tests that check the UI takes a lot of time and they might get timeout in the postsubmits.
- // TODO(b/242999951): Write this test using the APIs instead of checking the UI.
- fun launchActivity_withProfileOwner_displaysWorkPolicyInfo() {
+ fun getSafetyCenterData_withProfileOwner_hasWorkPolicyInfo() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.workPolicyInfoConfig)
findWorkPolicyInfo()
@@ -333,10 +360,7 @@ class SafetyCenterMultiUsersTest {
@Test
@EnsureHasDeviceOwner
- @Ignore
- // Tests that check the UI takes a lot of time and they might get timeout in the postsubmits.
- // TODO(b/242999951): Write this test using the APIs instead of checking the UI.
- fun launchActivity_withDeviceOwner_displaysWorkPolicyInfo() {
+ fun getSafetyCenterData_withDeviceOwner_hasWorkPolicyInfo() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.workPolicyInfoConfig)
findWorkPolicyInfo()
@@ -344,21 +368,24 @@ class SafetyCenterMultiUsersTest {
@Test
@EnsureHasWorkProfile
- @Ignore
- // Tests that check the UI takes a lot of time and they might get timeout in the postsubmits.
- // TODO(b/242999951): Write this test using the APIs instead of checking the UI.
- fun launchActivity_withQuietModeEnabled_shouldNotDisplayWorkPolicyInfo() {
+ fun launchActivity_withQuietModeEnabled_hasWorkPolicyInfo() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.workPolicyInfoConfig)
- findWorkPolicyInfo()
setQuietMode(true)
+
+ findWorkPolicyInfo()
+ }
+
+ @Test
+ @EnsureHasNoWorkProfile
+ @EnsureHasNoDeviceOwner
+ fun launchActivity_withoutWorkProfileOrDeviceOwner_doesntHaveWorkPolicyInfo() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.workPolicyInfoConfig)
+
context.launchSafetyCenterActivity { waitAllTextNotDisplayed("Your work policy info") }
}
@Test
- @Ignore
- // Test involving toggling of quiet mode are flaky.
- // TODO(b/237365018): Re-enable them back once we figure out a way to make them stable.
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun getSafetySourceData_withQuietModeEnabled_dataIsNotCleared() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -381,7 +408,6 @@ class SafetyCenterMultiUsersTest {
@Test
@EnsureHasAdditionalUser(installInstrumentedApp = TRUE)
- @Postsubmit(reason = "Test takes too much time to setup")
fun getSafetySourceData_afterAdditionalUserRemoved_returnsNull() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
val additionalUserSafetyCenterManager =
@@ -409,7 +435,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun getSafetySourceData_withoutInteractAcrossUserPermission_shouldThrowError() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -422,7 +447,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun getSafetySourceData_withoutAppInstalledForWorkProfile_shouldReturnNull() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -444,7 +468,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun getSafetySourceData_withRemovedProfile_shouldReturnNull() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -466,10 +489,7 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
- @Ignore
- // Test involving toggling of quiet mode are flaky.
fun getSafetySourceData_withProfileInQuietMode_shouldReturnData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
val managedSafetyCenterManager =
@@ -490,7 +510,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasNoWorkProfile
fun getSafetyCenterData_withComplexConfigWithoutWorkProfile_returnsPrimaryDataFromConfig() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
@@ -506,9 +525,7 @@ class SafetyCenterMultiUsersTest {
SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
.setSummary(
- safetyCenterResourcesContext.getStringByName(
- "group_unknown_summary"
- )
+ safetyCenterResourcesApk.getStringByName("group_unknown_summary")
)
.setEntries(listOf(dynamicBareboneDefault, dynamicDisabledDefault))
.setSeverityUnspecifiedIconType(
@@ -522,13 +539,17 @@ class SafetyCenterMultiUsersTest {
.build()
)
),
- listOf(SafetyCenterStaticEntryGroup("OK", listOf(staticEntry, staticEntry)))
+ listOf(
+ SafetyCenterStaticEntryGroup(
+ "OK",
+ listOf(createStaticEntry(), createStaticEntry(explicit = false))
+ )
+ )
)
assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun getSafetyCenterData_withComplexConfigWithoutDataProvided_returnsDataFromConfig() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
@@ -544,9 +565,7 @@ class SafetyCenterMultiUsersTest {
SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
.setSummary(
- safetyCenterResourcesContext.getStringByName(
- "group_unknown_summary"
- )
+ safetyCenterResourcesApk.getStringByName("group_unknown_summary")
)
.setEntries(
listOf(
@@ -571,7 +590,12 @@ class SafetyCenterMultiUsersTest {
listOf(
SafetyCenterStaticEntryGroup(
"OK",
- listOf(staticEntry, staticEntryForWork, staticEntry, staticEntryForWork)
+ listOf(
+ createStaticEntry(),
+ createStaticEntryForWork(),
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false)
+ )
)
)
)
@@ -579,7 +603,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun getSafetyCenterData_withComplexConfigWithPrimaryDataProvided_returnsPrimaryDataProvided() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
@@ -622,9 +645,9 @@ class SafetyCenterMultiUsersTest {
"OK",
listOf(
staticEntryUpdated,
- staticEntryForWork,
- staticEntry,
- staticEntryForWork
+ createStaticEntryForWork(),
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false)
)
)
)
@@ -633,7 +656,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun getSafetyCenterData_withComplexConfigWithAllDataProvided_returnsAllDataProvided() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
@@ -733,8 +755,8 @@ class SafetyCenterMultiUsersTest {
listOf(
staticEntryUpdated,
staticEntryForWorkUpdated,
- staticEntry,
- staticEntryForWork
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false)
)
)
)
@@ -744,15 +766,12 @@ class SafetyCenterMultiUsersTest {
@Test
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
- @Ignore
- // Test involving toggling of quiet mode are flaky.
- // TODO(b/237365018): Re-enable them back once we figure out a way to make them stable.
fun getSafetyCenterData_withQuietMode_shouldHaveWorkProfilePausedSummaryAndNoWorkIssues() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
updatePrimaryProfileSources()
updateWorkProfileSources()
-
setQuietMode(true)
+
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
val safetyCenterDataFromComplexConfig =
@@ -768,8 +787,9 @@ class SafetyCenterMultiUsersTest {
listOf(
dynamicBareboneUpdated,
dynamicDisabledUpdated,
- dynamicDisabledForWorkPaused,
- dynamicHiddenUpdated
+ dynamicDisabledForWorkPausedUpdated,
+ dynamicHiddenUpdated,
+ dynamicHiddenForWorkPausedUpdated,
)
)
.setSeverityUnspecifiedIconType(
@@ -794,9 +814,9 @@ class SafetyCenterMultiUsersTest {
"OK",
listOf(
staticEntryUpdated,
- staticEntryForWorkPaused,
- staticEntry,
- staticEntryForWorkPaused
+ staticEntryForWorkPausedUpdated,
+ createStaticEntry(explicit = false),
+ createStaticEntryForWorkPaused()
)
)
)
@@ -807,7 +827,6 @@ class SafetyCenterMultiUsersTest {
@Test
@EnsureHasAdditionalUser(installInstrumentedApp = TRUE)
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
- @Postsubmit(reason = "Test takes too much time to setup")
fun getSafetyCenterData_withDataForDifferentUserProfileGroup_shouldBeUnaffected() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
val dataForPrimaryUser = safetySourceTestData.information
@@ -830,7 +849,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Ignore // Removing a managed profile causes a refresh, which makes some tests flaky.
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun getSafetyCenterData_afterManagedProfileRemoved_returnsDefaultData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -845,9 +863,7 @@ class SafetyCenterMultiUsersTest {
SafetyCenterEntryGroup.Builder(SINGLE_SOURCE_GROUP_ID, "OK")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
.setSummary(
- safetyCenterResourcesContext.getStringByName(
- "group_unknown_summary"
- )
+ safetyCenterResourcesApk.getStringByName("group_unknown_summary")
)
.setEntries(
listOf(
@@ -905,7 +921,6 @@ class SafetyCenterMultiUsersTest {
@Test
@EnsureHasAdditionalUser(installInstrumentedApp = TRUE)
- @Postsubmit(reason = "Test takes too much time to setup")
fun getSafetyCenterData_afterAdditionalUserRemoved_returnsDefaultData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
val additionalUserSafetyCenterManager =
@@ -926,7 +941,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun setSafetySourceData_primaryProfileIssueOnlySource_shouldNotBeAbleToSetDataToWorkProfile() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
@@ -944,7 +958,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun setSafetySourceData_withoutInteractAcrossUserPermission_shouldThrowError() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -962,7 +975,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun setSafetySourceData_withoutAppInstalledForWorkProfile_shouldNoOp() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -984,7 +996,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun setSafetySourceData_withRemovedProfile_shouldNoOp() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -1006,7 +1017,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasCloneProfile(installInstrumentedApp = TRUE)
fun setSafetySourceData_withUnsupportedProfile_shouldNoOp() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -1027,10 +1037,7 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
- @Ignore
- // Test involving toggling of quiet mode are flaky.
fun setSafetySourceData_withProfileInQuietMode_shouldSetData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
val dataForWork = safetySourceTestData.informationWithIssueForWork
@@ -1051,7 +1058,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun setSafetySourceData_issuesOnlySourceWithWorkProfile_shouldBeAbleToSetData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceAllProfileConfig)
@@ -1079,7 +1085,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun setSafetySourceData_primaryProfileSource_shouldNotBeAbleToSetDataToWorkProfile() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -1096,7 +1101,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun setSafetySourceData_sourceWithWorkProfile_shouldBeAbleToSetData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -1122,7 +1126,32 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
+ @EnsureHasWorkProfile(installInstrumentedApp = TRUE)
+ fun setSafetySourceData_notificationsAllowed_workProfile_sendsNotification() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
+ SafetyCenterFlags.notificationsEnabled = true
+ SafetyCenterFlags.notificationsAllowedSources = setOf(SINGLE_SOURCE_ALL_PROFILE_ID)
+ SafetyCenterFlags.immediateNotificationBehaviorIssues =
+ setOf("$SINGLE_SOURCE_ALL_PROFILE_ID/$ISSUE_TYPE_ID")
+ val dataForWork = safetySourceTestData.informationWithIssueForWork
+ val managedSafetyCenterManager =
+ getSafetyCenterManagerForUser(deviceState.workProfile().userHandle())
+
+ managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ SINGLE_SOURCE_ALL_PROFILE_ID,
+ dataForWork
+ )
+
+ TestNotificationListener.waitForNotificationsMatching(
+ NotificationCharacteristics(
+ title = "Information issue title",
+ text = "Information issue summary",
+ actions = listOf("Review")
+ )
+ )
+ }
+
+ @Test
@EnsureHasAdditionalUser(installInstrumentedApp = TRUE)
fun setSafetySourceData_forStoppedUser_shouldSetData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -1144,7 +1173,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasAdditionalUser(installInstrumentedApp = TRUE)
fun setSafetySourceData_forBothPrimaryAdditionalUser_shouldSetData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -1170,7 +1198,6 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasAdditionalUser(installInstrumentedApp = TRUE)
fun setSafetySourceData_forAdditionalUser_shouldNotAffectDataForPrimaryUser() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -1190,8 +1217,6 @@ class SafetyCenterMultiUsersTest {
private fun findWorkPolicyInfo() {
context.launchSafetyCenterActivity {
- // TODO(b/233188021): This needs to use the Enterprise API to override the "work"
- // keyword.
waitAllTextDisplayed("Your work policy info", "Settings managed by your IT admin")
}
}
@@ -1207,11 +1232,14 @@ class SafetyCenterMultiUsersTest {
}
}
- private fun createTestActivityRedirectPendingIntentForUser(user: UserHandle): PendingIntent {
+ private fun createTestActivityRedirectPendingIntentForUser(
+ user: UserHandle,
+ explicit: Boolean = true
+ ): PendingIntent {
return callWithShellPermissionIdentity(INTERACT_ACROSS_USERS) {
SafetySourceTestData.createRedirectPendingIntent(
getContextForUser(user),
- Intent(ACTION_TEST_ACTIVITY)
+ SafetySourceTestData.createTestActivityIntent(getContextForUser(user), explicit)
)
}
}
@@ -1238,16 +1266,23 @@ class SafetyCenterMultiUsersTest {
getSafetyCenterDataWithPermission()
}
- private fun setQuietMode(value: Boolean) {
- deviceState.workProfile().setQuietMode(value)
- inQuietMode = value
- }
-
- private fun resetQuietMode() {
- if (!inQuietMode) {
+ private fun setQuietMode(enableQuietMode: Boolean) {
+ if (inQuietMode == enableQuietMode) {
return
}
- setQuietMode(false)
+ if (enableQuietMode) {
+ deviceState.workProfile().setQuietMode(true)
+ } else {
+ // This is needed to ensure the refresh broadcast doesn't leak onto other tests.
+ disableQuietModeAndWaitForRefreshToComplete()
+ }
+ inQuietMode = enableQuietMode
+ }
+
+ private fun disableQuietModeAndWaitForRefreshToComplete() {
+ val listener = safetyCenterTestHelper.addListener()
+ deviceState.workProfile().setQuietMode(false)
+ listener.waitForSafetyCenterRefresh()
}
private fun safetyCenterEntryOkForWork(sourceId: String, managedUserId: Int) =
diff --git a/tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml b/tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml
index 5357ed4f7..a1826653f 100644
--- a/tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml
+++ b/tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml
@@ -43,9 +43,12 @@
aren't polluted by `BOOT_COMPLETED` or similar broadcasts still being delivered, which
causes our `ActivityManager#waitForBroadcastIdle()` calls to timeout. -->
<option name="run-command" value="am wait-for-broadcast-idle" />
+ <option name="run-command" value="am wait-for-broadcast-barrier" />
<!-- 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" />
+ <!-- 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>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
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 b1c39e563..73f435615 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
@@ -17,7 +17,7 @@
package android.safetycenter.functional.ui
import android.content.Context
-import android.os.Build.VERSION.CODENAME
+import android.os.Build
import android.os.Build.VERSION_CODES.TIRAMISU
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.os.Bundle
@@ -37,7 +37,6 @@ import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG
import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
import com.android.safetycenter.testing.SafetyCenterFlags
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestConfigs
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ISSUE_ONLY_ALL_OPTIONAL_ID
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID
@@ -47,12 +46,15 @@ import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_4
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_5
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceIntentHandler.Request
import com.android.safetycenter.testing.SafetySourceIntentHandler.Response
import com.android.safetycenter.testing.SafetySourceReceiver
import com.android.safetycenter.testing.SafetySourceTestData
import com.android.safetycenter.testing.SafetySourceTestData.Companion.CRITICAL_ISSUE_ID
import com.android.safetycenter.testing.SafetySourceTestData.Companion.RECOMMENDATION_ISSUE_ID
+import com.android.safetycenter.testing.SettingsPackage.getSettingsPackageName
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.android.safetycenter.testing.UiTestHelper.MORE_ISSUES_LABEL
import com.android.safetycenter.testing.UiTestHelper.RESCAN_BUTTON_LABEL
import com.android.safetycenter.testing.UiTestHelper.clickConfirmDismissal
@@ -67,13 +69,12 @@ import com.android.safetycenter.testing.UiTestHelper.waitButtonDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitCollapsedIssuesDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitExpandedIssuesDisplayed
+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 org.junit.After
-import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -82,38 +83,18 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SafetyCenterActivityTest {
- @get:Rule val disableAnimationRule = DisableAnimationRule()
-
- @get:Rule val freezeRotationRule = FreezeRotationRule()
-
private val context: Context = getApplicationContext()
-
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetySourceTestData = SafetySourceTestData(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
- // JUnit's Assume is not supported in @BeforeClass by the tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
-
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
- @Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
- }
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
+ @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule()
+ @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule()
@After
fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
getUiDevice().resetRotation()
}
@@ -549,7 +530,7 @@ class SafetyCenterActivityTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun issueCard_withAttribution_hasProperContentDescriptions() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -722,7 +703,7 @@ class SafetyCenterActivityTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun issueCard_resolveIssue_withDialogClickYes_resolves() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
@@ -748,7 +729,7 @@ class SafetyCenterActivityTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun issueCard_resolveIssue_withDialog_rotates_clickYes_resolves() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
@@ -778,7 +759,7 @@ class SafetyCenterActivityTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun issueCard_resolveIssue_withDialogClicksNo_cancels() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
@@ -880,7 +861,7 @@ class SafetyCenterActivityTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun issueCard_withAttributionTitleSetBySource_displaysAttributionTitle() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -891,7 +872,7 @@ class SafetyCenterActivityTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun issueCard_attributionNotSetBySource_displaysGroupTitleAsAttribution() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -902,7 +883,7 @@ class SafetyCenterActivityTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun issueCard_attributionNotSetBySourceAndGroupTitleNull_doesNotDisplayAttributionTitle() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceNoGroupTitleConfig)
@@ -915,9 +896,6 @@ class SafetyCenterActivityTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
fun issueCard_attributionNotSetBySourceOnTiramisu_doesNotDisplayAttributionTitle() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(CODENAME == "UpsideDownCake")
- assumeFalse(CODENAME == "VanillaIceCream")
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
val data = safetySourceTestData.recommendationWithGeneralIssue
@@ -1430,10 +1408,6 @@ class SafetyCenterActivityTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
fun launchSafetyCenter_enableSubpagesFlagOnT_stillShowsExpandAndCollapseEntries() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(CODENAME == "UpsideDownCake")
- assumeFalse(CODENAME == "VanillaIceCream")
-
SafetyCenterFlags.showSubpages = true
val sourceTestData = safetySourceTestData.information
val config = safetyCenterTestConfigs.multipleSourceGroupsConfig
@@ -1478,7 +1452,7 @@ class SafetyCenterActivityTest {
@Test
fun startStaticEntryActivity_withConfigToBeSettingsActivity_trueExtraInBundle() {
- safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleStaticSettingsSource)
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleStaticSettingsSourceConfig)
context.launchSafetyCenterActivity {
waitDisplayed(By.text("OK")) { it.click() }
@@ -1487,17 +1461,40 @@ class SafetyCenterActivityTest {
}
}
- companion object {
- private const val EXPAND_ISSUE_GROUP_QS_FRAGMENT_KEY = "expand_issue_group_qs_fragment_key"
- private const val SAFETY_SOURCE_1_TITLE = "Safety Source 1 Title"
- private const val SAFETY_SOURCE_1_SUMMARY = "Safety Source 1 Summary"
- private const val SAFETY_SOURCE_2_TITLE = "Safety Source 2 Title"
- private const val SAFETY_SOURCE_2_SUMMARY = "Safety Source 2 Summary"
- private const val SAFETY_SOURCE_3_TITLE = "Safety Source 3 Title"
- private const val SAFETY_SOURCE_3_SUMMARY = "Safety Source 3 Summary"
- private const val SAFETY_SOURCE_4_TITLE = "Safety Source 4 Title"
- private const val SAFETY_SOURCE_4_SUMMARY = "Safety Source 4 Summary"
- private const val SAFETY_SOURCE_5_TITLE = "Safety Source 5 Title"
- private const val SAFETY_SOURCE_5_SUMMARY = "Safety Source 5 Summary"
+ @Test
+ fun launchActivity_openWithPrivacyControlsIntent_showsPrivacyControls() {
+ context.launchSafetyCenterActivity(intentAction = PRIVACY_CONTROLS_ACTION) {
+ waitPageTitleDisplayed("Privacy controls")
+ }
+ }
+
+ @Test
+ fun launchActivity_openWithPrivacyControlsIntentWithScDisabled_showsLegacyPrivacyPage() {
+ // This test should technically run on T+ but we have to restrict it to V+ as b/286690307 is
+ // causing a flake which was only fixed on master.
+ assumeTrue(
+ Build.VERSION.SDK_INT > UPSIDE_DOWN_CAKE || Build.VERSION.CODENAME == "VanillaIceCream"
+ )
+ safetyCenterTestHelper.setEnabled(false)
+
+ context.launchSafetyCenterActivity(intentAction = PRIVACY_CONTROLS_ACTION) {
+ waitDisplayed(By.pkg(context.getSettingsPackageName()))
+ waitPageTitleDisplayed("Privacy")
+ }
+ }
+
+ private companion object {
+ const val EXPAND_ISSUE_GROUP_QS_FRAGMENT_KEY = "expand_issue_group_qs_fragment_key"
+ const val SAFETY_SOURCE_1_TITLE = "Safety Source 1 Title"
+ const val SAFETY_SOURCE_1_SUMMARY = "Safety Source 1 Summary"
+ const val SAFETY_SOURCE_2_TITLE = "Safety Source 2 Title"
+ const val SAFETY_SOURCE_2_SUMMARY = "Safety Source 2 Summary"
+ const val SAFETY_SOURCE_3_TITLE = "Safety Source 3 Title"
+ const val SAFETY_SOURCE_3_SUMMARY = "Safety Source 3 Summary"
+ const val SAFETY_SOURCE_4_TITLE = "Safety Source 4 Title"
+ const val SAFETY_SOURCE_4_SUMMARY = "Safety Source 4 Summary"
+ const val SAFETY_SOURCE_5_TITLE = "Safety Source 5 Title"
+ const val SAFETY_SOURCE_5_SUMMARY = "Safety Source 5 Summary"
+ const val PRIVACY_CONTROLS_ACTION = "android.settings.PRIVACY_CONTROLS"
}
}
diff --git a/tests/functional/safetycenter/singleuser/AndroidManifest.xml b/tests/functional/safetycenter/singleuser/AndroidManifest.xml
index 7ca38dede..18a3b167a 100644
--- a/tests/functional/safetycenter/singleuser/AndroidManifest.xml
+++ b/tests/functional/safetycenter/singleuser/AndroidManifest.xml
@@ -17,14 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.safetycenter.functional">
<application>
- <service android:name=".testing.TestNotificationListener"
- android:label="TestNotificationListener"
- android:exported="false"
- android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
- <intent-filter>
- <action android:name="android.service.notification.NotificationListenerService" />
- </intent-filter>
- </service>
+
<uses-library android:name="android.test.runner"/>
</application>
diff --git a/tests/functional/safetycenter/singleuser/AndroidTest.xml b/tests/functional/safetycenter/singleuser/AndroidTest.xml
index d9d5b1361..3aa173508 100644
--- a/tests/functional/safetycenter/singleuser/AndroidTest.xml
+++ b/tests/functional/safetycenter/singleuser/AndroidTest.xml
@@ -43,9 +43,12 @@
aren't polluted by `BOOT_COMPLETED` or similar broadcasts still being delivered, which
causes our `ActivityManager#waitForBroadcastIdle()` calls to timeout. -->
<option name="run-command" value="am wait-for-broadcast-idle" />
+ <option name="run-command" value="am wait-for-broadcast-barrier" />
<!-- 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" />
+ <!-- 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>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
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 b8fd17b37..ef217a199 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt
+++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt
@@ -16,10 +16,8 @@
package android.safetycenter.functional
-import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.os.Build
import android.os.Build.VERSION_CODES.TIRAMISU
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.os.UserHandle
@@ -63,25 +61,25 @@ import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
-import com.android.compatibility.common.preconditions.ScreenLockHelper
import com.android.compatibility.common.util.SystemUtil
import com.android.modules.utils.build.SdkLevel
import com.android.safetycenter.internaldata.SafetyCenterBundles
import com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY
import com.android.safetycenter.internaldata.SafetyCenterEntryId
import com.android.safetycenter.internaldata.SafetyCenterIds
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG
import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT
import com.android.safetycenter.testing.Coroutines.waitForWithTimeout
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.dismissSafetyCenterIssueWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetyCenterConfigWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetyCenterDataWithPermission
+import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetySourceDataWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.refreshSafetySourcesWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.reportSafetySourceErrorWithPermission
import com.android.safetycenter.testing.SafetyCenterFlags
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestConfigs
+import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY_EXPORTED
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_ALL_OPTIONAL_ID
@@ -116,6 +114,7 @@ import com.android.safetycenter.testing.SafetyCenterTestData.Companion.withAttri
import com.android.safetycenter.testing.SafetyCenterTestData.Companion.withDismissedIssuesIfAtLeastU
import com.android.safetycenter.testing.SafetyCenterTestData.Companion.withoutExtras
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceIntentHandler.Request
import com.android.safetycenter.testing.SafetySourceIntentHandler.Response
import com.android.safetycenter.testing.SafetySourceReceiver
@@ -130,15 +129,14 @@ import com.android.safetycenter.testing.SafetySourceTestData.Companion.INFORMATI
import com.android.safetycenter.testing.SafetySourceTestData.Companion.RECOMMENDATION_ISSUE_ID
import com.android.safetycenter.testing.SettingsPackage.getSettingsPackageName
import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.google.common.base.Preconditions.checkState
import com.google.common.truth.Truth.assertThat
import java.time.Duration
import kotlin.test.assertFailsWith
import kotlinx.coroutines.TimeoutCancellationException
-import org.junit.After
import org.junit.Assume.assumeFalse
-import org.junit.Assume.assumeTrue
-import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -146,589 +144,646 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SafetyCenterManagerTest {
private val context: Context = getApplicationContext()
- private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context)
+ private val safetyCenterResourcesApk = SafetyCenterResourcesApk.forTests(context)
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetySourceTestData = SafetySourceTestData(context)
private val safetyCenterTestData = SafetyCenterTestData(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
- private val safetyCenterStatusOk =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_summary")
- )
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
- .build()
+ private val safetyCenterStatusOk: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary")
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
+ .build()
+
+ private val safetyCenterStatusUnknownScanning: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName("scanning_title"),
+ safetyCenterResourcesApk.getStringByName("loading_summary")
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN)
+ .setRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS)
+ .build()
+
+ private val safetyCenterStatusOkOneAlert: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
+ safetyCenterTestData.getAlertString(1)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
+ .build()
+
+ private val safetyCenterStatusOkReviewOneAlert: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_ok_review_title"
+ ),
+ safetyCenterTestData.getAlertString(1)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
+ .build()
+
+ private val safetyCenterStatusOkReview: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_ok_review_title"
+ ),
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_ok_review_summary"
+ )
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
+ .build()
+
+ private val safetyCenterStatusGeneralRecommendationOneAlert: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_safety_recommendation_title"
+ ),
+ safetyCenterTestData.getAlertString(1)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+
+ private val safetyCenterStatusAccountRecommendationOneAlert: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_account_recommendation_title"
+ ),
+ safetyCenterTestData.getAlertString(1)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+
+ private val safetyCenterStatusDeviceRecommendationOneAlert: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_device_recommendation_title"
+ ),
+ safetyCenterTestData.getAlertString(1)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
- private val safetyCenterStatusUnknownScanning =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName("scanning_title"),
- safetyCenterResourcesContext.getStringByName("loading_summary")
+ private val safetyCenterStatusGeneralCriticalOneAlert: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_critical_safety_warning_title"
+ ),
+ safetyCenterTestData.getAlertString(1)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ private val safetyCenterStatusGeneralCriticalTwoAlerts: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_critical_safety_warning_title"
+ ),
+ safetyCenterTestData.getAlertString(2)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ private val safetyCenterStatusAccountCriticalOneAlert: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_critical_account_warning_title"
+ ),
+ safetyCenterTestData.getAlertString(1)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ private val safetyCenterStatusAccountCriticalTwoAlerts: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_critical_account_warning_title"
+ ),
+ safetyCenterTestData.getAlertString(2)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ private val safetyCenterStatusDeviceCriticalOneAlert: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_critical_device_warning_title"
+ ),
+ safetyCenterTestData.getAlertString(1)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ private val safetyCenterStatusDeviceCriticalTwoAlerts: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_critical_device_warning_title"
+ ),
+ safetyCenterTestData.getAlertString(2)
+ )
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ private val safetyCenterEntryOrGroupRecommendation: SafetyCenterEntryOrGroup
+ get() =
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
+ )
+
+ private val safetyCenterEntryOrGroupCritical: SafetyCenterEntryOrGroup
+ get() =
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryCritical(SINGLE_SOURCE_ID)
+ )
+
+ private val safetyCenterEntryGroupMixedFromComplexConfig: SafetyCenterEntryOrGroup
+ get() =
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(MIXED_STATEFUL_GROUP_ID, "OK")
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
+ .setSummary(safetyCenterResourcesApk.getStringByName("group_unknown_summary"))
+ .setEntries(
+ listOf(
+ safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_IN_STATEFUL_ID),
+ SafetyCenterEntry.Builder(
+ SafetyCenterTestData.entryId(STATIC_IN_STATEFUL_ID),
+ "OK"
+ )
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
+ .setSummary("OK")
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent(
+ explicit = false
+ )
+ )
+ .setSeverityUnspecifiedIconType(
+ SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON
+ )
+ .build()
+ )
+ )
+ .setSeverityUnspecifiedIconType(
+ SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION
+ )
+ .build()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN)
- .setRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS)
- .build()
- private val safetyCenterStatusOkOneAlert =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
- safetyCenterTestData.getAlertString(1)
+ private val safetyCenterStaticEntryGroupFromComplexConfig: SafetyCenterStaticEntryGroup
+ get() =
+ SafetyCenterStaticEntryGroup(
+ "OK",
+ listOf(
+ SafetyCenterStaticEntry.Builder("OK")
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent(
+ explicit = false
+ )
+ )
+ .build(),
+ SafetyCenterStaticEntry.Builder("OK")
+ .setSummary("OK")
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent(
+ explicit = false
+ )
+ )
+ .build()
+ )
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
- .build()
- private val safetyCenterStatusOkReviewOneAlert =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_ok_review_title"
- ),
- safetyCenterTestData.getAlertString(1)
+ private val safetyCenterStaticEntryGroupMixedFromComplexConfig: SafetyCenterStaticEntryGroup
+ get() =
+ SafetyCenterStaticEntryGroup(
+ "OK",
+ listOf(
+ SafetyCenterStaticEntry.Builder("OK")
+ .setSummary("OK")
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
+ )
+ .build(),
+ SafetyCenterStaticEntry.Builder("OK")
+ .setSummary("OK")
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent(
+ explicit = false
+ )
+ )
+ .build()
+ )
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
- .build()
- private val safetyCenterStatusOkReview =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_ok_review_title"
- ),
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_ok_review_summary"
+ private val safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig:
+ SafetyCenterStaticEntryGroup
+ get() =
+ SafetyCenterStaticEntryGroup(
+ "OK",
+ listOf(
+ SafetyCenterStaticEntry.Builder("Unspecified title")
+ .setSummary("Unspecified summary")
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
+ )
+ .build(),
+ SafetyCenterStaticEntry.Builder("OK")
+ .setSummary("OK")
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent(
+ explicit = false
+ )
+ )
+ .build()
)
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
- .build()
- private val safetyCenterStatusGeneralRecommendationOneAlert =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_safety_recommendation_title"
+ private val safetyCenterDataFromConfigScanning: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusUnknownScanning,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID)
+ )
),
- safetyCenterTestData.getAlertString(1)
+ emptyList()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
- .build()
- private val safetyCenterStatusAccountRecommendationOneAlert =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_account_recommendation_title"
+ private val safetyCenterDataFromConfig: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusUnknown,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID)
+ )
),
- safetyCenterTestData.getAlertString(1)
+ emptyList()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
- .build()
- private val safetyCenterStatusDeviceRecommendationOneAlert =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_device_recommendation_title"
+ private val safetyCenterDataUnspecified: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusOk,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryUnspecified(SINGLE_SOURCE_ID)
+ )
),
- safetyCenterTestData.getAlertString(1)
+ emptyList()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
- .build()
- private val safetyCenterStatusGeneralCriticalOneAlert =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_safety_warning_title"
+ private val safetyCenterDataOk: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusOk,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID)
+ )
),
- safetyCenterTestData.getAlertString(1)
+ emptyList()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
- .build()
- private val safetyCenterStatusGeneralCriticalTwoAlerts =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_safety_warning_title"
+ private val safetyCenterDataOkWithIconAction: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusOk,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData
+ .safetyCenterEntryOkBuilder(SINGLE_SOURCE_ID)
+ .setIconAction(
+ ICON_ACTION_TYPE_INFO,
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
+ )
+ .build()
+ )
),
- safetyCenterTestData.getAlertString(2)
+ emptyList()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
- .build()
- private val safetyCenterStatusAccountCriticalOneAlert =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_account_warning_title"
+ private val safetyCenterDataUnknownScanningWithError: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusUnknownScanning,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID)
+ )
),
- safetyCenterTestData.getAlertString(1)
+ emptyList()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
- .build()
- private val safetyCenterStatusAccountCriticalTwoAlerts =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_account_warning_title"
+ private val safetyCenterDataUnknownReviewError: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusUnknown,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID)
+ )
),
- safetyCenterTestData.getAlertString(2)
+ emptyList()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
- .build()
- private val safetyCenterStatusDeviceCriticalOneAlert =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_device_warning_title"
+ private val safetyCenterDataOkOneAlert: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusOkOneAlert,
+ listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID)
+ )
),
- safetyCenterTestData.getAlertString(1)
+ emptyList()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
- .build()
- private val safetyCenterStatusDeviceCriticalTwoAlerts =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_device_warning_title"
- ),
- safetyCenterTestData.getAlertString(2)
+ private val safetyCenterDataOkReviewCriticalEntry: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusOkReview,
+ emptyList(),
+ listOf(safetyCenterEntryOrGroupCritical),
+ emptyList()
)
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
- .build()
- private val safetyCenterEntryOrGroupRecommendation =
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
- )
+ private val safetyCenterDataOkReviewRecommendationEntry: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusOkReview,
+ emptyList(),
+ listOf(safetyCenterEntryOrGroupRecommendation),
+ emptyList()
+ )
- private val safetyCenterEntryOrGroupCritical =
- SafetyCenterEntryOrGroup(safetyCenterTestData.safetyCenterEntryCritical(SINGLE_SOURCE_ID))
+ private val safetyCenterDataOkReviewOneAlert: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusOkReviewOneAlert,
+ listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)),
+ listOf(safetyCenterEntryOrGroupCritical),
+ emptyList()
+ )
- private val safetyCenterEntryGroupMixedFromComplexConfig =
- SafetyCenterEntryOrGroup(
- SafetyCenterEntryGroup.Builder(MIXED_STATEFUL_GROUP_ID, "OK")
- .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
- .setSummary(safetyCenterResourcesContext.getStringByName("group_unknown_summary"))
- .setEntries(
- listOf(
- safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_IN_STATEFUL_ID),
- SafetyCenterEntry.Builder(
- SafetyCenterTestData.entryId(STATIC_IN_STATEFUL_ID),
- "OK"
- )
- .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
- .setSummary("OK")
- .setPendingIntent(
- safetySourceTestData.testActivityRedirectPendingIntent
- )
- .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON)
- .build()
+ private val safetyCenterDataGeneralRecommendationOneAlert: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusGeneralRecommendationOneAlert,
+ listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
)
- )
- .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION)
- .build()
- )
-
- private val safetyCenterStaticEntryGroupFromComplexConfig =
- SafetyCenterStaticEntryGroup(
- "OK",
- listOf(
- SafetyCenterStaticEntry.Builder("OK")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build(),
- SafetyCenterStaticEntry.Builder("OK")
- .setSummary("OK")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build()
+ ),
+ emptyList()
)
- )
- private val safetyCenterStaticEntryGroupMixedFromComplexConfig =
- SafetyCenterStaticEntryGroup(
- "OK",
- listOf(
- SafetyCenterStaticEntry.Builder("OK")
- .setSummary("OK")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build(),
- SafetyCenterStaticEntry.Builder("OK")
- .setSummary("OK")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build()
+ private val safetyCenterDataGeneralRecommendationAlertWithConfirmation: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusGeneralRecommendationOneAlert,
+ listOf(
+ safetyCenterTestData.safetyCenterIssueRecommendation(
+ SINGLE_SOURCE_ID,
+ confirmationDialog = true
+ )
+ ),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
+ )
+ ),
+ emptyList()
)
- )
- private val safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig =
- SafetyCenterStaticEntryGroup(
- "OK",
- listOf(
- SafetyCenterStaticEntry.Builder("Unspecified title")
- .setSummary("Unspecified summary")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build(),
- SafetyCenterStaticEntry.Builder("OK")
- .setSummary("OK")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build()
+ private val safetyCenterDataAccountRecommendationOneAlert: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusAccountRecommendationOneAlert,
+ listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
+ )
+ ),
+ emptyList()
)
- )
-
- private val safetyCenterDataFromConfigScanning =
- SafetyCenterData(
- safetyCenterStatusUnknownScanning,
- emptyList(),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID)
- )
- ),
- emptyList()
- )
-
- private val safetyCenterDataFromConfig =
- SafetyCenterData(
- safetyCenterTestData.safetyCenterStatusUnknown,
- emptyList(),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID)
- )
- ),
- emptyList()
- )
-
- private val safetyCenterDataUnspecified =
- SafetyCenterData(
- safetyCenterStatusOk,
- emptyList(),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryUnspecified(SINGLE_SOURCE_ID)
- )
- ),
- emptyList()
- )
-
- private val safetyCenterDataOk =
- SafetyCenterData(
- safetyCenterStatusOk,
- emptyList(),
- listOf(
- SafetyCenterEntryOrGroup(safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID))
- ),
- emptyList()
- )
-
- private val safetyCenterDataOkWithIconAction =
- SafetyCenterData(
- safetyCenterStatusOk,
- emptyList(),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData
- .safetyCenterEntryOkBuilder(SINGLE_SOURCE_ID)
- .setIconAction(
- ICON_ACTION_TYPE_INFO,
- safetySourceTestData.testActivityRedirectPendingIntent
- )
- .build()
- )
- ),
- emptyList()
- )
-
- private val safetyCenterDataUnknownScanningWithError =
- SafetyCenterData(
- safetyCenterStatusUnknownScanning,
- emptyList(),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID)
- )
- ),
- emptyList()
- )
-
- private val safetyCenterDataUnknownReviewError =
- SafetyCenterData(
- safetyCenterTestData.safetyCenterStatusUnknown,
- emptyList(),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID)
- )
- ),
- emptyList()
- )
-
- private val safetyCenterDataOkOneAlert =
- SafetyCenterData(
- safetyCenterStatusOkOneAlert,
- listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)),
- listOf(
- SafetyCenterEntryOrGroup(safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID))
- ),
- emptyList()
- )
-
- private val safetyCenterDataOkReviewCriticalEntry =
- SafetyCenterData(
- safetyCenterStatusOkReview,
- emptyList(),
- listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
- )
-
- private val safetyCenterDataOkReviewRecommendationEntry =
- SafetyCenterData(
- safetyCenterStatusOkReview,
- emptyList(),
- listOf(safetyCenterEntryOrGroupRecommendation),
- emptyList()
- )
-
- private val safetyCenterDataOkReviewOneAlert =
- SafetyCenterData(
- safetyCenterStatusOkReviewOneAlert,
- listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)),
- listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
- )
-
- private val safetyCenterDataGeneralRecommendationOneAlert =
- SafetyCenterData(
- safetyCenterStatusGeneralRecommendationOneAlert,
- listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
- )
- ),
- emptyList()
- )
- private val safetyCenterDataGeneralRecommendationAlertWithConfirmation =
- SafetyCenterData(
- safetyCenterStatusGeneralRecommendationOneAlert,
- listOf(
- safetyCenterTestData.safetyCenterIssueRecommendation(
- SINGLE_SOURCE_ID,
- confirmationDialog = true
- )
- ),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
- )
- ),
- emptyList()
- )
-
- private val safetyCenterDataAccountRecommendationOneAlert =
- SafetyCenterData(
- safetyCenterStatusAccountRecommendationOneAlert,
- listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
- )
- ),
- emptyList()
- )
-
- private val safetyCenterDataDeviceRecommendationOneAlert =
- SafetyCenterData(
- safetyCenterStatusDeviceRecommendationOneAlert,
- listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
- listOf(
- SafetyCenterEntryOrGroup(
- safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
- )
- ),
- emptyList()
- )
-
- private val safetyCenterDataGeneralCriticalOneAlert =
- SafetyCenterData(
- safetyCenterStatusGeneralCriticalOneAlert,
- listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
- listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
- )
-
- private val safetyCenterDataAccountCriticalOneAlert =
- SafetyCenterData(
- safetyCenterStatusAccountCriticalOneAlert,
- listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
- listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
- )
+ private val safetyCenterDataDeviceRecommendationOneAlert: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusDeviceRecommendationOneAlert,
+ listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID)
+ )
+ ),
+ emptyList()
+ )
- private val safetyCenterDataDeviceCriticalOneAlert =
- SafetyCenterData(
- safetyCenterStatusDeviceCriticalOneAlert,
- listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
- listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
- )
+ private val safetyCenterDataGeneralCriticalOneAlert: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusGeneralCriticalOneAlert,
+ listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
+ listOf(safetyCenterEntryOrGroupCritical),
+ emptyList()
+ )
- private val safetyCenterDataCriticalOneAlertInFlight =
- SafetyCenterData(
- safetyCenterStatusGeneralCriticalOneAlert,
- listOf(
- safetyCenterTestData.safetyCenterIssueCritical(
- SINGLE_SOURCE_ID,
- isActionInFlight = true
- )
- ),
- listOf(safetyCenterEntryOrGroupCritical),
- emptyList()
- )
+ private val safetyCenterDataAccountCriticalOneAlert: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusAccountCriticalOneAlert,
+ listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
+ listOf(safetyCenterEntryOrGroupCritical),
+ emptyList()
+ )
- private val safetyCenterDataOkReviewOneDismissedAlertInFlight =
- SafetyCenterData(
- safetyCenterStatusOkReview,
- emptyList(),
+ private val safetyCenterDataDeviceCriticalOneAlert: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusDeviceCriticalOneAlert,
+ listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
listOf(safetyCenterEntryOrGroupCritical),
emptyList()
)
- .withDismissedIssuesIfAtLeastU(
+
+ private val safetyCenterDataCriticalOneAlertInFlight: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusGeneralCriticalOneAlert,
listOf(
safetyCenterTestData.safetyCenterIssueCritical(
SINGLE_SOURCE_ID,
isActionInFlight = true
)
- )
+ ),
+ listOf(safetyCenterEntryOrGroupCritical),
+ emptyList()
)
- private val safetyCenterDataFromComplexConfig =
- SafetyCenterData(
- safetyCenterTestData.safetyCenterStatusUnknown,
- emptyList(),
- listOf(
- SafetyCenterEntryOrGroup(
- SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
- .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
- .setSummary(
- safetyCenterResourcesContext.getStringByName("group_unknown_summary")
+ private val safetyCenterDataOkReviewOneDismissedAlertInFlight: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterStatusOkReview,
+ emptyList(),
+ listOf(safetyCenterEntryOrGroupCritical),
+ emptyList()
+ )
+ .withDismissedIssuesIfAtLeastU(
+ listOf(
+ safetyCenterTestData.safetyCenterIssueCritical(
+ SINGLE_SOURCE_ID,
+ isActionInFlight = true
)
- .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY)
- .setEntries(
- listOf(
- safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_BAREBONE_ID),
- safetyCenterTestData
- .safetyCenterEntryDefaultBuilder(DYNAMIC_ALL_OPTIONAL_ID)
- .setEnabled(false)
- .build(),
- safetyCenterTestData
- .safetyCenterEntryDefaultBuilder(DYNAMIC_DISABLED_ID)
- .setPendingIntent(null)
- .setEnabled(false)
- .build(),
- safetyCenterTestData
- .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID)
- .setPendingIntent(null)
- .setEnabled(false)
- .build()
+ )
+ )
+
+ private val safetyCenterDataFromComplexConfig: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusUnknown,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
+ .setSummary(
+ safetyCenterResourcesApk.getStringByName("group_unknown_summary")
)
- )
- .build()
+ .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY)
+ .setEntries(
+ listOf(
+ safetyCenterTestData.safetyCenterEntryDefault(
+ DYNAMIC_BAREBONE_ID
+ ),
+ safetyCenterTestData
+ .safetyCenterEntryDefaultBuilder(DYNAMIC_ALL_OPTIONAL_ID)
+ .setEnabled(false)
+ .build(),
+ safetyCenterTestData
+ .safetyCenterEntryDefaultBuilder(DYNAMIC_DISABLED_ID)
+ .setPendingIntent(null)
+ .setEnabled(false)
+ .build(),
+ safetyCenterTestData
+ .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID)
+ .setPendingIntent(null)
+ .setEnabled(false)
+ .build()
+ )
+ )
+ .build()
+ ),
+ safetyCenterEntryGroupMixedFromComplexConfig
),
- safetyCenterEntryGroupMixedFromComplexConfig
- ),
- listOf(
- safetyCenterStaticEntryGroupFromComplexConfig,
- safetyCenterStaticEntryGroupMixedFromComplexConfig
+ listOf(
+ safetyCenterStaticEntryGroupFromComplexConfig,
+ safetyCenterStaticEntryGroupMixedFromComplexConfig
+ )
)
- )
- private val safetyCenterDataFromComplexConfigUpdated =
- SafetyCenterData(
- safetyCenterTestData.safetyCenterStatusCritical(6),
- listOf(
- safetyCenterTestData.safetyCenterIssueCritical(
- DYNAMIC_BAREBONE_ID,
- groupId = DYNAMIC_GROUP_ID
- ),
- safetyCenterTestData.safetyCenterIssueCritical(
- ISSUE_ONLY_BAREBONE_ID,
- groupId = ISSUE_ONLY_GROUP_ID
- ),
- safetyCenterTestData.safetyCenterIssueRecommendation(
- DYNAMIC_DISABLED_ID,
- groupId = DYNAMIC_GROUP_ID
- ),
- safetyCenterTestData.safetyCenterIssueRecommendation(
- ISSUE_ONLY_ALL_OPTIONAL_ID,
- groupId = ISSUE_ONLY_GROUP_ID
- ),
- safetyCenterTestData.safetyCenterIssueInformation(
- DYNAMIC_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
+ private val safetyCenterDataFromComplexConfigUpdated: SafetyCenterData
+ get() =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusCritical(6),
+ listOf(
+ safetyCenterTestData.safetyCenterIssueCritical(
+ DYNAMIC_BAREBONE_ID,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueCritical(
+ ISSUE_ONLY_BAREBONE_ID,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueRecommendation(
+ DYNAMIC_DISABLED_ID,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueRecommendation(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_IN_STATELESS_ID,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ groupId = MIXED_STATELESS_GROUP_ID
+ )
),
- safetyCenterTestData.safetyCenterIssueInformation(
- ISSUE_ONLY_IN_STATELESS_ID,
- groupId = MIXED_STATELESS_GROUP_ID
- )
- ),
- listOf(
- SafetyCenterEntryOrGroup(
- SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
- .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
- .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY)
- .setSummary("Critical summary")
- .setEntries(
- listOf(
- safetyCenterTestData.safetyCenterEntryCritical(DYNAMIC_BAREBONE_ID),
- safetyCenterTestData
- .safetyCenterEntryDefaultBuilder(DYNAMIC_ALL_OPTIONAL_ID)
- .setEnabled(false)
- .build(),
- safetyCenterTestData.safetyCenterEntryRecommendation(
- DYNAMIC_DISABLED_ID
- ),
- safetyCenterTestData.safetyCenterEntryUnspecified(
- DYNAMIC_HIDDEN_ID,
- pendingIntent = null
- ),
- safetyCenterTestData.safetyCenterEntryOk(
- DYNAMIC_HIDDEN_WITH_SEARCH_ID
- ),
- safetyCenterTestData
- .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID)
- .setPendingIntent(null)
- .setEnabled(false)
- .build()
+ listOf(
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY)
+ .setSummary("Critical summary")
+ .setEntries(
+ listOf(
+ safetyCenterTestData.safetyCenterEntryCritical(
+ DYNAMIC_BAREBONE_ID
+ ),
+ safetyCenterTestData
+ .safetyCenterEntryDefaultBuilder(DYNAMIC_ALL_OPTIONAL_ID)
+ .setEnabled(false)
+ .build(),
+ safetyCenterTestData.safetyCenterEntryRecommendation(
+ DYNAMIC_DISABLED_ID
+ ),
+ safetyCenterTestData.safetyCenterEntryUnspecified(
+ DYNAMIC_HIDDEN_ID,
+ pendingIntent = null
+ ),
+ safetyCenterTestData.safetyCenterEntryOk(
+ DYNAMIC_HIDDEN_WITH_SEARCH_ID
+ ),
+ safetyCenterTestData
+ .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID)
+ .setPendingIntent(null)
+ .setEnabled(false)
+ .build()
+ )
)
- )
- .build()
+ .build()
+ ),
+ safetyCenterEntryGroupMixedFromComplexConfig
),
- safetyCenterEntryGroupMixedFromComplexConfig
- ),
- listOf(
- safetyCenterStaticEntryGroupFromComplexConfig,
- safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig
+ listOf(
+ safetyCenterStaticEntryGroupFromComplexConfig,
+ safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig
+ )
)
- )
-
- // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
-
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
-
- @Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
- }
- @After
- fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
- }
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
@Test
fun refreshSafetySources_timeout_marksSafetySourceAsError() {
@@ -808,9 +863,9 @@ class SafetyCenterManagerTest {
val status1 = listener.receiveSafetyCenterData().status
assertThat(status1.refreshStatus).isEqualTo(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS)
assertThat(status1.title.toString())
- .isEqualTo(safetyCenterResourcesContext.getStringByName("scanning_title"))
+ .isEqualTo(safetyCenterResourcesApk.getStringByName("scanning_title"))
assertThat(status1.summary.toString())
- .isEqualTo(safetyCenterResourcesContext.getStringByName("loading_summary"))
+ .isEqualTo(safetyCenterResourcesApk.getStringByName("loading_summary"))
val status2 = listener.receiveSafetyCenterData().status
assertThat(status2.refreshStatus).isEqualTo(REFRESH_STATUS_NONE)
assertThat(status2).isEqualTo(safetyCenterStatusOk)
@@ -832,9 +887,9 @@ class SafetyCenterManagerTest {
val status1 = listener.receiveSafetyCenterData().status
assertThat(status1.refreshStatus).isEqualTo(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS)
assertThat(status1.title.toString())
- .isEqualTo(safetyCenterResourcesContext.getStringByName("scanning_title"))
+ .isEqualTo(safetyCenterResourcesApk.getStringByName("scanning_title"))
assertThat(status1.summary.toString())
- .isEqualTo(safetyCenterResourcesContext.getStringByName("loading_summary"))
+ .isEqualTo(safetyCenterResourcesApk.getStringByName("loading_summary"))
val status2 = listener.receiveSafetyCenterData().status
assertThat(status2.refreshStatus).isEqualTo(REFRESH_STATUS_NONE)
assertThat(status2).isEqualTo(safetyCenterStatusOk)
@@ -858,13 +913,78 @@ class SafetyCenterManagerTest {
assertThat(status1.refreshStatus).isEqualTo(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS)
assertThat(status1.title.toString()).isEqualTo(safetyCenterStatusOk.title.toString())
assertThat(status1.summary.toString())
- .isEqualTo(safetyCenterResourcesContext.getStringByName("loading_summary"))
+ .isEqualTo(safetyCenterResourcesApk.getStringByName("loading_summary"))
val status2 = listener.receiveSafetyCenterData().status
assertThat(status2.refreshStatus).isEqualTo(REFRESH_STATUS_NONE)
assertThat(status2).isEqualTo(safetyCenterStatusOk)
}
@Test
+ fun refreshSafetySources_reasonPageOpen_allowedByFlag_broadcastSent() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig)
+ safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
+ SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID)
+ SafetySourceReceiver.setResponse(
+ Request.Refresh(SINGLE_SOURCE_ID),
+ Response.SetData(safetySourceTestData.informationWithIssue)
+ )
+
+ safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+ REFRESH_REASON_PAGE_OPEN
+ )
+
+ val apiSafetySourceData =
+ safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
+ assertThat(apiSafetySourceData).isEqualTo(safetySourceTestData.informationWithIssue)
+ }
+
+ @Test
+ fun refreshSafetySources_reasonPageOpen_allowedByFlagLater_broadcastSentLater() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig)
+ safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
+ SafetySourceReceiver.setResponse(
+ Request.Refresh(SINGLE_SOURCE_ID),
+ Response.SetData(safetySourceTestData.informationWithIssue)
+ )
+
+ assertFailsWith(TimeoutCancellationException::class) {
+ safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+ REFRESH_REASON_PAGE_OPEN,
+ timeout = TIMEOUT_SHORT
+ )
+ }
+ val apiSafetySourceDataBeforeSettingFlag =
+ safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
+ SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID)
+ safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+ REFRESH_REASON_PAGE_OPEN
+ )
+ val apiSafetySourceDataAfterSettingFlag =
+ safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
+
+ assertThat(apiSafetySourceDataBeforeSettingFlag).isEqualTo(safetySourceTestData.information)
+ assertThat(apiSafetySourceDataAfterSettingFlag)
+ .isEqualTo(safetySourceTestData.informationWithIssue)
+ }
+
+ @Test
+ fun refreshSafetySources_reasonPageOpen_noDataForSource_broadcastSent() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig)
+ SafetySourceReceiver.setResponse(
+ Request.Refresh(SINGLE_SOURCE_ID),
+ Response.SetData(safetySourceTestData.information)
+ )
+
+ safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+ REFRESH_REASON_PAGE_OPEN
+ )
+
+ val apiSafetySourceData =
+ safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
+ assertThat(apiSafetySourceData).isEqualTo(safetySourceTestData.information)
+ }
+
+ @Test
fun getSafetyCenterData_withoutDataProvided_returnsDataFromConfig() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -874,28 +994,105 @@ class SafetyCenterManagerTest {
}
@Test
+ fun getSafetyCenterData_withoutDataExplicitIntentConfig_defaultEntryHasExplicitIntent() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ val expectedExplicitPendingIntent =
+ SafetySourceTestData.createRedirectPendingIntent(
+ context,
+ Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName)
+ )
+ val defaultEntryPendingIntent =
+ apiSafetyCenterData.entriesOrGroups.firstOrNull()?.entry?.pendingIntent
+ val defaultEntryIntentFilterEqualsToExplicitIntent =
+ callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") {
+ expectedExplicitPendingIntent.intentFilterEquals(defaultEntryPendingIntent)
+ }
+ assertThat(defaultEntryIntentFilterEqualsToExplicitIntent).isTrue()
+ }
+
+ @Test
fun getSafetyCenterData_withoutDataImplicitIntentConfig_defaultEntryHasImplicitIntent() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.implicitIntentSingleSourceConfig)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
- val implicitPendingIntentCreatedByCts =
- PendingIntent.getActivity(
+ val expectedImplicitPendingIntent =
+ SafetySourceTestData.createRedirectPendingIntent(
context,
- 0 /* requestCode */,
- Intent(ACTION_TEST_ACTIVITY_EXPORTED),
- PendingIntent.FLAG_IMMUTABLE
+ Intent(ACTION_TEST_ACTIVITY_EXPORTED)
)
val defaultEntryPendingIntent =
apiSafetyCenterData.entriesOrGroups.firstOrNull()?.entry?.pendingIntent
val defaultEntryIntentFilterEqualsToImplicitIntent =
callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") {
- implicitPendingIntentCreatedByCts.intentFilterEquals(defaultEntryPendingIntent)
+ expectedImplicitPendingIntent.intentFilterEquals(defaultEntryPendingIntent)
}
assertThat(defaultEntryIntentFilterEqualsToImplicitIntent).isTrue()
}
@Test
+ fun getSafetyCenterData_withStaticImplicitResolving_implicitStaticEntry() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.staticSourcesConfig)
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ val expectedImplicitPendingIntent =
+ SafetySourceTestData.createRedirectPendingIntent(
+ context,
+ Intent(ACTION_TEST_ACTIVITY_EXPORTED)
+ )
+ val staticEntryPendingIntent =
+ apiSafetyCenterData.staticEntryGroups
+ .firstOrNull()
+ ?.staticEntries
+ ?.firstOrNull()
+ ?.pendingIntent
+ val staticEntryIntentFilterEqualsToImplicitIntent =
+ callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") {
+ expectedImplicitPendingIntent.intentFilterEquals(staticEntryPendingIntent)
+ }
+ assertThat(staticEntryIntentFilterEqualsToImplicitIntent).isTrue()
+ }
+
+ @Test
+ fun getSafetyCenterData_withStaticImplicitNotExported_explicitStaticEntryUsingCallerPackage() {
+ safetyCenterTestHelper.setConfig(
+ safetyCenterTestConfigs.singleStaticImplicitIntentNotExportedConfig
+ )
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ val expectedExplicitPendingIntent =
+ SafetySourceTestData.createRedirectPendingIntent(
+ context,
+ Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName)
+ )
+ val staticEntryPendingIntent =
+ apiSafetyCenterData.staticEntryGroups
+ .firstOrNull()
+ ?.staticEntries
+ ?.firstOrNull()
+ ?.pendingIntent
+ val staticEntryIntentFilterEqualsToExplicitIntent =
+ callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") {
+ expectedExplicitPendingIntent.intentFilterEquals(staticEntryPendingIntent)
+ }
+ assertThat(staticEntryIntentFilterEqualsToExplicitIntent).isTrue()
+ }
+
+ @Test
+ fun getSafetyCenterData_withStaticNotResolving_noStaticEntry() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleStaticInvalidIntentConfig)
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ assertThat(apiSafetyCenterData.staticEntryGroups).isEmpty()
+ }
+
+ @Test
fun getSafetyCenterData_withComplexConfigWithoutDataProvided_returnsDataFromConfig() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexConfig)
@@ -940,7 +1137,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_attributionTitleProvidedBySource_returnsIssueWithAttributionTitle() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
@@ -956,7 +1153,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_attributionTitleNotProvided_returnsGroupTitleAsAttributionTitle() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue)
@@ -969,7 +1166,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_attributionNotSetAndGroupTitleNull_returnsNullAttributionTitle() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceNoGroupTitleConfig)
safetyCenterTestHelper.setData(
@@ -997,9 +1194,6 @@ class SafetyCenterManagerTest {
@Test
@SdkSuppress(maxSdkVersion = TIRAMISU)
fun getSafetyCenterData_attributionNotSetBySourceOnTiramisu_returnsNullAttributionTitle() {
- // TODO(b/258228790): Remove after U is no longer in pre-release
- assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
- assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream")
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue)
@@ -1074,7 +1268,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_withActionConfirmation_returnsRecommendationWithActionConfirmation() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
safetyCenterTestHelper.setData(
@@ -1154,7 +1348,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_withRecommendationDataIssue_returnsDataRecommendationStatus() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1179,7 +1373,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_withCriticalDataIssue_returnsDataCriticalStatus() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1204,7 +1398,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_withRecommendationPasswordsIssue_returnsDataRecommendationStatus() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1229,7 +1423,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_withCriticalPasswordsIssue_returnsDataCriticalStatus() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1254,7 +1448,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_withRecommendationPersonalIssue_returnsDataRecommendationStatus() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1279,7 +1473,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_withCriticalPersonalIssue_returnsDataCriticalStatus() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1304,7 +1498,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_infoStatusTipFirstIssueSingleTip_infoStatusWithTipSummary() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1324,7 +1518,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_infoStatusTipFirstIssueMultiTips_infoStatusWithTipsSummary() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1360,7 +1554,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_infoStatusActionFirstIssueSingleAction_infoStatusWithActionSummary() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1380,7 +1574,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_infoStatusActionFirstIssueMultiActions_infoStatusWithActionsSummary() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1416,7 +1610,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_infoStatusManualFirstIssueSingleManual_infoStatusWithAlertSummary() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1442,7 +1636,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_infoStatusManualFirstIssueMultiManual_infoStatusWithAlertsSummary() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig)
safetyCenterTestHelper.setData(
@@ -1480,7 +1674,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_withStaticEntryGroups_hasStaticEntriesToIdsMapping() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.staticSourcesConfig)
@@ -1560,7 +1754,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_duplicateIssuesOfSameSeverities_issueOfFirstSourceInConfigShown() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1592,7 +1786,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_duplicateIssuesInDifferentSourceGroups_topIssueRelevantForBothGroups() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1622,7 +1816,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_duplicateIssuesInSameSourceGroups_topIssueRelevantForThatGroup() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1652,7 +1846,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_noDuplicateIssues_noGroupBelongingSpecified() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1680,7 +1874,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_differentDuplicationId_bothIssuesShown() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1717,7 +1911,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_differentDuplicationGroup_bothIssuesShown() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1754,7 +1948,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_threeDuplicateIssues_onlyOneIssueShown() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1793,7 +1987,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_duplicateIssuesOfDifferentSeverities_moreSevereIssueShown() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1825,7 +2019,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_multipleDuplicationsOfIssues_correctlyDeduplicated() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1901,7 +2095,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_duplicateIssuesBothDismissed_topOneShownAsDismissed() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1942,7 +2136,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_duplicateIssuesLowerSeverityOneDismissed_topOneShown() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -1980,7 +2174,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_duplicateIssuesHigherSeverityOneDismissed_topOneShownAsDismissed() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -2018,7 +2212,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_dupIssuesLowerPrioritySameSeverityOneDismissed_topShownAsDismissed() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -2056,7 +2250,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_dupIssuesTopOneDismissedThenDisappears_bottomOneReemergesTimely() {
SafetyCenterFlags.tempHiddenIssueResurfaceDelay = Duration.ZERO
SafetyCenterFlags.resurfaceIssueMaxCounts = mapOf(SEVERITY_LEVEL_CRITICAL_WARNING to 99L)
@@ -2110,7 +2304,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_dupsOfDiffSeveritiesTopOneDismissedThenGone_bottomOneReemergesTimely() {
SafetyCenterFlags.tempHiddenIssueResurfaceDelay = Duration.ZERO
SafetyCenterFlags.resurfaceIssueMaxCounts =
@@ -2173,7 +2367,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_duplicateIssuesLowerOneResurfaces_lowerOneStillFilteredOut() {
SafetyCenterFlags.resurfaceIssueMaxCounts =
mapOf(
@@ -2236,7 +2430,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_duplicateIssuesTopOneResurfaces_topOneShown() {
SafetyCenterFlags.resurfaceIssueMaxCounts =
mapOf(
@@ -2297,7 +2491,7 @@ class SafetyCenterManagerTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun getSafetyCenterData_dupIssuesTopOneResolved_bottomOneReemergesAfterTemporaryHiddenPeriod() {
SafetyCenterFlags.tempHiddenIssueResurfaceDelay = RESURFACE_DELAY
safetyCenterTestHelper.setConfig(
@@ -2831,7 +3025,7 @@ class SafetyCenterManagerTest {
safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID)
assertThat(group.summary)
- .isEqualTo(safetyCenterResourcesContext.getStringByName("group_unknown_summary"))
+ .isEqualTo(safetyCenterResourcesApk.getStringByName("group_unknown_summary"))
assertThat(group.severityLevel).isEqualTo(ENTRY_SEVERITY_LEVEL_UNKNOWN)
}
@@ -2845,7 +3039,7 @@ class SafetyCenterManagerTest {
.getGroup(ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID)
assertThat(initialGroup.summary)
- .isEqualTo(safetyCenterResourcesContext.getStringByName("group_unknown_summary"))
+ .isEqualTo(safetyCenterResourcesApk.getStringByName("group_unknown_summary"))
assertThat(initialGroup.severityLevel).isEqualTo(ENTRY_SEVERITY_LEVEL_UNKNOWN)
safetyCenterTestHelper.setData(DYNAMIC_BAREBONE_ID, safetySourceTestData.unspecified)
@@ -3340,7 +3534,7 @@ class SafetyCenterManagerTest {
assertThat(error)
.isEqualTo(
SafetyCenterErrorDetails(
- safetyCenterResourcesContext.getStringByName("resolving_action_error")
+ safetyCenterResourcesApk.getStringByName("resolving_action_error")
)
)
}
@@ -3374,7 +3568,7 @@ class SafetyCenterManagerTest {
assertThat(error)
.isEqualTo(
SafetyCenterErrorDetails(
- safetyCenterResourcesContext.getStringByName("resolving_action_error")
+ safetyCenterResourcesApk.getStringByName("resolving_action_error")
)
)
}
@@ -3538,46 +3732,6 @@ class SafetyCenterManagerTest {
}
@Test
- fun lockScreenSource_withoutReplaceLockScreenIconActionFlag_doesntReplace() {
- // Must have a screen lock for the icon action to be set
- assumeTrue(ScreenLockHelper.isDeviceSecure(context))
- safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.settingsLockScreenSourceConfig)
- val listener = safetyCenterTestHelper.addListener()
- SafetyCenterFlags.replaceLockScreenIconAction = false
-
- safetyCenterManager.refreshSafetySourcesWithPermission(REFRESH_REASON_PAGE_OPEN)
- // Skip loading data.
- listener.receiveSafetyCenterData()
-
- val lockScreenSafetyCenterData = listener.receiveSafetyCenterData()
- val lockScreenEntry = lockScreenSafetyCenterData.entriesOrGroups.first().entry!!
- val entryPendingIntent = lockScreenEntry.pendingIntent!!
- val iconActionPendingIntent = lockScreenEntry.iconAction!!.pendingIntent
- // This test passes for now but will eventually start failing once we introduce the fix in
- // the Settings app. This will warn if the assumption is failed rather than fail, at which
- // point we can remove this test (and potentially even this magnificent hack).
- assumeTrue(iconActionPendingIntent == entryPendingIntent)
- }
-
- @Test
- fun lockScreenSource_withReplaceLockScreenIconActionFlag_replaces() {
- // Must have a screen lock for the icon action to be set
- assumeTrue(ScreenLockHelper.isDeviceSecure(context))
- safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.settingsLockScreenSourceConfig)
- val listener = safetyCenterTestHelper.addListener()
-
- safetyCenterManager.refreshSafetySourcesWithPermission(REFRESH_REASON_PAGE_OPEN)
- // Skip loading data.
- listener.receiveSafetyCenterData()
-
- val lockScreenSafetyCenterData = listener.receiveSafetyCenterData()
- val lockScreenEntry = lockScreenSafetyCenterData.entriesOrGroups.first().entry!!
- val entryPendingIntent = lockScreenEntry.pendingIntent!!
- val iconActionPendingIntent = lockScreenEntry.iconAction!!.pendingIntent
- assertThat(iconActionPendingIntent).isNotEqualTo(entryPendingIntent)
- }
-
- @Test
fun beforeAnyDataSet_noLastUpdatedTimestamps() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
@@ -3629,6 +3783,17 @@ class SafetyCenterManagerTest {
assertThat(lastUpdated[key]).isNotNull()
}
+ @Test
+ fun setSafetySourceData_dynamicHiddenWithIssueOnlyData_allowed() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.hiddenSourceConfig)
+ val expectedData = SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+
+ safetyCenterTestHelper.setData(DYNAMIC_HIDDEN_ID, expectedData)
+
+ val actualData = safetyCenterManager.getSafetySourceDataWithPermission(DYNAMIC_HIDDEN_ID)
+ assertThat(actualData).isEqualTo(expectedData)
+ }
+
private fun dumpLastUpdated(): Map<String, String> {
val dump = SystemUtil.runShellCommand("dumpsys safety_center data")
return dump
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 70d6468fa..1678ccced 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt
+++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt
@@ -28,41 +28,46 @@ import android.safetycenter.SafetyCenterStatus
import android.safetycenter.SafetyEvent
import android.safetycenter.SafetySourceErrorDetails
import android.safetycenter.SafetySourceIssue
-import android.safetycenter.functional.testing.NotificationCharacteristics
-import android.safetycenter.functional.testing.TestNotificationListener
+import android.service.notification.StatusBarNotification
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
import com.android.safetycenter.pendingintents.PendingIntentSender
+import com.android.safetycenter.testing.Coroutines
import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT
+import com.android.safetycenter.testing.NotificationCharacteristics
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.executeBlockAndExit
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.clearAllSafetySourceDataForTestsWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.dismissSafetyCenterIssueWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.reportSafetySourceErrorWithPermission
import com.android.safetycenter.testing.SafetyCenterFlags
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestConfigs
-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
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_5
import com.android.safetycenter.testing.SafetyCenterTestData
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceIntentHandler.Request
import com.android.safetycenter.testing.SafetySourceIntentHandler.Response
import com.android.safetycenter.testing.SafetySourceReceiver
import com.android.safetycenter.testing.SafetySourceTestData
import com.android.safetycenter.testing.SafetySourceTestData.Companion.ISSUE_TYPE_ID
import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
+import com.android.safetycenter.testing.StatusBarNotificationWithChannel
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
+import com.android.safetycenter.testing.TestNotificationListener
import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueDisplayed
import com.google.common.truth.Truth.assertThat
import java.time.Duration
import kotlin.test.assertFailsWith
import kotlinx.coroutines.TimeoutCancellationException
-import org.junit.After
-import org.junit.Assume.assumeTrue
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
+import org.junit.rules.TestName
import org.junit.runner.RunWith
/** Notification-related tests for [SafetyCenterManager]. */
@@ -74,44 +79,33 @@ class SafetyCenterNotificationTest {
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
private val safetyCenterManager =
requireNotNull(context.getSystemService(SafetyCenterManager::class.java)) {
- "Could not get system service"
+ "Could not get SafetyCenterManager"
}
+ private var uniqueSafetySourceId: String = ""
- // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2)
+ val safetyCenterTestRule =
+ SafetyCenterTestRule(safetyCenterTestHelper, withNotifications = true)
+ @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule()
+ @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule()
+ @get:Rule(order = 5) val testNameRule = TestName()
@Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
-
- @Before
- fun setUp() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
- TestNotificationListener.setup()
+ fun enableNotificationsForTestSourceBeforeTest() {
SafetyCenterFlags.notificationsEnabled = true
- setFlagsForImmediateNotifications(SINGLE_SOURCE_ID)
- safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
- }
-
- @After
- fun tearDown() {
- if (!shouldRunTests) {
- return
- }
- // It is important to reset the notification listener last because it waits/ensures that
- // all notifications have been removed before returning.
- safetyCenterTestHelper.reset()
- TestNotificationListener.reset()
+ uniqueSafetySourceId = testNameRule.methodName
+ setFlagsForImmediateNotifications(uniqueSafetySourceId)
+ safetyCenterTestHelper.setConfig(
+ safetyCenterTestConfigs.singleSourceConfig(
+ safetyCenterTestConfigs.dynamicSafetySourceBuilder(uniqueSafetySourceId).build()
+ )
+ )
}
@Test
fun setSafetySourceData_withNoIssue_noNotification() {
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, safetySourceTestData.information)
TestNotificationListener.waitForZeroNotifications()
}
@@ -121,7 +115,7 @@ class SafetyCenterNotificationTest {
SafetyCenterFlags.immediateNotificationBehaviorIssues = emptySet()
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
+ uniqueSafetySourceId,
safetySourceTestData.recommendationWithAccountIssue
)
@@ -133,7 +127,7 @@ class SafetyCenterNotificationTest {
SafetyCenterFlags.notificationsAllowedSources = emptySet()
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
+ uniqueSafetySourceId,
safetySourceTestData.recommendationWithAccountIssue
)
@@ -145,7 +139,7 @@ class SafetyCenterNotificationTest {
SafetyCenterFlags.notificationsEnabled = false
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
+ uniqueSafetySourceId,
safetySourceTestData.recommendationWithAccountIssue
)
@@ -153,7 +147,7 @@ class SafetyCenterNotificationTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_withNotificationBehaviorNever_noNotification() {
val data =
safetySourceTestData
@@ -166,13 +160,13 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
TestNotificationListener.waitForZeroNotifications()
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_withNotificationBehaviorDelay_noImmediateNotification() {
SafetyCenterFlags.notificationsMinDelay = Duration.ofDays(1)
val data =
@@ -186,13 +180,13 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
TestNotificationListener.waitForZeroNotifications()
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_withNotificationBehaviorDelay_sendsNotificationAfterDelay() {
SafetyCenterFlags.notificationsMinDelay = Duration.ofDays(1)
val delayedNotificationIssue =
@@ -217,7 +211,7 @@ class SafetyCenterNotificationTest {
.addIssue(neverNotifyIssue)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data1)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data1)
TestNotificationListener.waitForZeroNotifications()
SafetyCenterFlags.notificationsMinDelay = TIMEOUT_SHORT
@@ -225,19 +219,20 @@ class SafetyCenterNotificationTest {
// Sending new data causes Safety Center to recompute which issues to send notifications
// about and this should now include the delayed issue sent in data1 above.
Thread.sleep(TIMEOUT_SHORT.toMillis())
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data2)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data2)
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Notify later",
text = "This is not urgent.",
- actions = listOf("See issue")
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_withNotificationBehaviorDelayOfZero_sendsNotificationImmediately() {
SafetyCenterFlags.immediateNotificationBehaviorIssues = emptySet()
SafetyCenterFlags.notificationsMinDelay = Duration.ofSeconds(0)
@@ -252,19 +247,20 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Notify immediately",
text = "This is urgent!",
- actions = listOf("See issue")
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_withNotificationBehaviorImmediately_sendsNotification() {
SafetyCenterFlags.immediateNotificationBehaviorIssues = emptySet()
val data =
@@ -280,37 +276,39 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Notify immediately",
text = "This is urgent!",
- actions = listOf("See issue")
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@Test
fun setSafetySourceData_withNotificationsAllowedForSourceByFlag_sendsNotification() {
- SafetyCenterFlags.notificationsAllowedSources = setOf(SINGLE_SOURCE_ID)
+ SafetyCenterFlags.notificationsAllowedSources = setOf(uniqueSafetySourceId)
val data = safetySourceTestData.recommendationWithAccountIssue
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Recommendation issue title",
text = "Recommendation issue summary",
- actions = listOf("See issue")
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@Test
fun setSafetySourceData_issueWithTwoActions_notificationWithTwoActions() {
- val intent1 = safetySourceTestData.testActivityRedirectPendingIntent(identifier = "1")
- val intent2 = safetySourceTestData.testActivityRedirectPendingIntent(identifier = "2")
+ val intent1 = safetySourceTestData.createTestActivityRedirectPendingIntent(identifier = "1")
+ val intent2 = safetySourceTestData.createTestActivityRedirectPendingIntent(identifier = "2")
val data =
safetySourceTestData
@@ -329,19 +327,20 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Recommendation issue title",
text = "Recommendation issue summary",
- actions = listOf("Action 1", "Action 2")
+ actions = listOf("Action 1", "Action 2"),
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_withNotificationsAllowedForSourceByConfig_sendsNotification() {
SafetyCenterFlags.notificationsAllowedSources = emptySet()
SafetyCenterFlags.immediateNotificationBehaviorIssues = emptySet()
@@ -368,14 +367,19 @@ class SafetyCenterNotificationTest {
safetyCenterTestHelper.setData("MyNotifiableSource", data)
- TestNotificationListener.waitForSingleNotification()
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = "MyNotifiableSource",
+ )
+ )
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_withCustomNotification_usesCustomValues() {
- val intent1 = safetySourceTestData.testActivityRedirectPendingIntent(identifier = "1")
- val intent2 = safetySourceTestData.testActivityRedirectPendingIntent(identifier = "2")
+ val intent1 = safetySourceTestData.createTestActivityRedirectPendingIntent(identifier = "1")
+ val intent2 = safetySourceTestData.createTestActivityRedirectPendingIntent(identifier = "2")
val notification =
SafetySourceIssue.Notification.Builder("Custom title", "Custom text")
@@ -397,7 +401,7 @@ class SafetyCenterNotificationTest {
SafetySourceIssue.Action.Builder(
"default_action",
"Default action",
- safetySourceTestData.testActivityRedirectPendingIntent
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
)
.build()
)
@@ -406,19 +410,20 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Custom title",
text = "Custom text",
- actions = listOf("Custom action 1", "Custom action 2")
+ actions = listOf("Custom action 1", "Custom action 2"),
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_withEmptyCustomActions_notificationHasNoActions() {
val notification =
SafetySourceIssue.Notification.Builder("Custom title", "Custom text")
@@ -435,7 +440,7 @@ class SafetyCenterNotificationTest {
SafetySourceIssue.Action.Builder(
"default_action",
"Default action",
- safetySourceTestData.testActivityRedirectPendingIntent
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
)
.build()
)
@@ -444,13 +449,14 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Custom title",
text = "Custom text",
- actions = emptyList()
+ actions = emptyList(),
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@@ -467,14 +473,15 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data1)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data1)
val initialNotification =
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Initial",
text = "Blah",
- actions = listOf("See issue")
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
)
)
@@ -488,7 +495,7 @@ class SafetyCenterNotificationTest {
SafetySourceIssue.Action.Builder(
"new_action",
"New action",
- safetySourceTestData.testActivityRedirectPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent(
identifier = "new_action"
)
)
@@ -498,14 +505,15 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data2)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data2)
val revisedNotification =
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Revised",
text = "Different",
- actions = listOf("See issue", "New action")
+ actions = listOf("See issue", "New action"),
+ safetySourceId = uniqueSafetySourceId,
)
)
assertThat(initialNotification.statusBarNotification.key)
@@ -516,11 +524,16 @@ class SafetyCenterNotificationTest {
fun setSafetySourceData_twiceWithExactSameIssue_doNotNotifyTwice() {
val data = safetySourceTestData.recommendationWithAccountIssue
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
- TestNotificationListener.waitForSingleNotification()
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
TestNotificationListener.waitForZeroNotificationEvents()
}
@@ -530,15 +543,41 @@ class SafetyCenterNotificationTest {
val data1 = safetySourceTestData.recommendationWithAccountIssue
val data2 = safetySourceTestData.information
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data1)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data1)
- TestNotificationListener.waitForSingleNotification()
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data2)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data2)
TestNotificationListener.waitForZeroNotifications()
}
+ // TODO(b/284271124): Decide what to do with existing notifications when flag flipped off
+ @Test
+ fun setSafetySourceData_removingAnIssue_afterFlagTurnedOff_noNotificationChanges() {
+ val data1 = safetySourceTestData.recommendationWithAccountIssue
+ val data2 = safetySourceTestData.information
+
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data1)
+
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
+
+ SafetyCenterFlags.notificationsEnabled = false
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data2)
+
+ TestNotificationListener.waitForZeroNotificationEvents()
+ }
+
@Test
fun reportSafetySourceError_sourceWithNotification_cancelsNotification() {
val data = safetySourceTestData.recommendationWithAccountIssue
@@ -547,11 +586,16 @@ class SafetyCenterNotificationTest {
SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
)
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
- TestNotificationListener.waitForSingleNotification()
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
- safetyCenterManager.reportSafetySourceErrorWithPermission(SINGLE_SOURCE_ID, error)
+ safetyCenterManager.reportSafetySourceErrorWithPermission(uniqueSafetySourceId, error)
TestNotificationListener.waitForZeroNotifications()
}
@@ -581,20 +625,21 @@ class SafetyCenterNotificationTest {
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data1)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data1)
val notificationWithChannel =
TestNotificationListener.waitForSingleNotificationMatching(
NotificationCharacteristics(
title = "Initial",
text = "Blah",
- actions = listOf("See issue")
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
)
)
TestNotificationListener.cancelAndWait(notificationWithChannel.statusBarNotification.key)
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data2)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data2)
TestNotificationListener.waitForZeroNotifications()
}
@@ -620,17 +665,28 @@ class SafetyCenterNotificationTest {
.build()
)
.build()
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data1)
- val notificationWithChannel = TestNotificationListener.waitForSingleNotification()
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data1)
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
TestNotificationListener.cancelAndWait(notificationWithChannel.statusBarNotification.key)
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data2)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data2)
- TestNotificationListener.waitForSingleNotification()
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_duplicateIssues_sendsOneNotification() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -655,12 +711,13 @@ class SafetyCenterNotificationTest {
"Critical issue title",
"Critical issue summary",
actions = listOf("Solve issue"),
+ safetySourceId = SOURCE_ID_5,
)
)
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun setSafetySourceData_duplicateIssueOfLowerSeverityDismissed_sendsNotification() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -674,7 +731,13 @@ class SafetyCenterNotificationTest {
)
)
- val notificationWithChannel = TestNotificationListener.waitForSingleNotification()
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = SOURCE_ID_1,
+ )
+ )
TestNotificationListener.cancelAndWait(notificationWithChannel.statusBarNotification.key)
@@ -690,13 +753,17 @@ class SafetyCenterNotificationTest {
"Critical issue title",
"Critical issue summary",
actions = listOf("Solve issue"),
+ safetySourceId = SOURCE_ID_5,
)
)
}
@Test
fun setSafetySourceData_withInformationIssue_lowImportanceBlockableNotification() {
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue)
+ safetyCenterTestHelper.setData(
+ uniqueSafetySourceId,
+ safetySourceTestData.informationWithIssue
+ )
TestNotificationListener.waitForNotificationsMatching(
NotificationCharacteristics(
@@ -704,7 +771,8 @@ class SafetyCenterNotificationTest {
"Information issue summary",
actions = listOf("Review"),
importance = NotificationManager.IMPORTANCE_LOW,
- blockable = true
+ blockable = true,
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@@ -712,7 +780,7 @@ class SafetyCenterNotificationTest {
@Test
fun setSafetySourceData_withRecommendationIssue_defaultImportanceUnblockableNotification() {
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
+ uniqueSafetySourceId,
safetySourceTestData.recommendationWithAccountIssue
)
@@ -722,7 +790,8 @@ class SafetyCenterNotificationTest {
"Recommendation issue summary",
importance = NotificationManager.IMPORTANCE_DEFAULT,
actions = listOf("See issue"),
- blockable = false
+ blockable = false,
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@@ -730,8 +799,8 @@ class SafetyCenterNotificationTest {
@Test
fun setSafetySourceData_withCriticalIssue_highImportanceUnblockableNotification() {
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingDeviceIssue
+ uniqueSafetySourceId,
+ safetySourceTestData.criticalWithResolvingDeviceIssue(sourceId = uniqueSafetySourceId)
)
TestNotificationListener.waitForNotificationsMatching(
@@ -740,7 +809,8 @@ class SafetyCenterNotificationTest {
"Critical issue summary",
actions = listOf("Solve issue"),
importance = NotificationManager.IMPORTANCE_HIGH,
- blockable = false
+ blockable = false,
+ safetySourceId = uniqueSafetySourceId,
)
)
}
@@ -748,15 +818,20 @@ class SafetyCenterNotificationTest {
@Test
fun dismissSafetyCenterIssue_dismissesNotification() {
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
+ uniqueSafetySourceId,
safetySourceTestData.recommendationWithAccountIssue
)
- TestNotificationListener.waitForSingleNotification()
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterTestData.issueId(
- SINGLE_SOURCE_ID,
+ uniqueSafetySourceId,
SafetySourceTestData.RECOMMENDATION_ISSUE_ID
)
)
@@ -767,14 +842,20 @@ class SafetyCenterNotificationTest {
@Test
fun dismissingNotification_doesNotUpdateSafetyCenterData() {
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ 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
val listener = safetyCenterTestHelper.addListener()
- val notificationWithChannel = TestNotificationListener.waitForSingleNotification()
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
TestNotificationListener.cancelAndWait(notificationWithChannel.statusBarNotification.key)
@@ -784,7 +865,7 @@ class SafetyCenterNotificationTest {
}
@Test
- @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
fun dismissingNotification_withDuplicateIssues_allDismissed() {
safetyCenterTestHelper.setConfig(
safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig
@@ -810,6 +891,7 @@ class SafetyCenterNotificationTest {
"Critical issue title",
"Critical issue summary",
actions = listOf("Solve issue"),
+ safetySourceId = SOURCE_ID_1,
)
)
@@ -826,9 +908,14 @@ class SafetyCenterNotificationTest {
fun clearSafetySourceData_cancelsAllNotifications() {
val data = safetySourceTestData.recommendationWithAccountIssue
- safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data)
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
- TestNotificationListener.waitForSingleNotification()
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
safetyCenterManager.clearAllSafetySourceDataForTestsWithPermission()
@@ -838,15 +925,21 @@ class SafetyCenterNotificationTest {
@Test
fun sendActionPendingIntent_successful_updatesListener() {
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ uniqueSafetySourceId,
+ safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId)
)
- val notificationWithChannel = TestNotificationListener.waitForSingleNotification()
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
val action =
notificationWithChannel.statusBarNotification.notification.actions.firstOrNull()
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
- Request.ResolveAction(SINGLE_SOURCE_ID),
+ Request.ResolveAction(uniqueSafetySourceId),
Response.SetData(safetySourceTestData.information)
)
val listener = safetyCenterTestHelper.addListener()
@@ -864,15 +957,21 @@ class SafetyCenterNotificationTest {
@Test
fun sendActionPendingIntent_successfulNoSuccessMessage_removesNotification() {
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ uniqueSafetySourceId,
+ safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId)
)
- val notificationWithChannel = TestNotificationListener.waitForSingleNotification()
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
val action =
notificationWithChannel.statusBarNotification.notification.actions.firstOrNull()
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
- Request.ResolveAction(SINGLE_SOURCE_ID),
+ Request.ResolveAction(uniqueSafetySourceId),
Response.SetData(safetySourceTestData.information)
)
@@ -884,27 +983,200 @@ class SafetyCenterNotificationTest {
@Test
fun sendActionPendingIntent_successfulWithSuccessMessage_successNotification() {
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage
+ uniqueSafetySourceId,
+ safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage(
+ sourceId = uniqueSafetySourceId
+ )
)
- val notificationWithChannel = TestNotificationListener.waitForSingleNotification()
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
val action =
notificationWithChannel.statusBarNotification.notification.actions.firstOrNull()
checkNotNull(action) { "Notification action unexpectedly null" }
SafetySourceReceiver.setResponse(
- Request.ResolveAction(SINGLE_SOURCE_ID),
+ Request.ResolveAction(uniqueSafetySourceId),
Response.SetData(safetySourceTestData.information)
)
sendActionPendingIntentAndWaitWithPermission(action)
- TestNotificationListener.waitForSingleNotificationMatching(
- NotificationCharacteristics(
- "Issue solved",
- "",
- actions = emptyList(),
+ TestNotificationListener.waitForSuccessNotification("Issue solved") {
+ assertThat(NotificationCharacteristics.safetySourceIdMatches(it, uniqueSafetySourceId))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun successNotification_notificationHasAutoCancel() {
+ safetyCenterTestHelper.setData(
+ uniqueSafetySourceId,
+ safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage(
+ sourceId = uniqueSafetySourceId
+ )
+ )
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
+ val action =
+ notificationWithChannel.statusBarNotification.notification.actions.firstOrNull()
+ checkNotNull(action) { "Notification action unexpectedly null" }
+ SafetySourceReceiver.setResponse(
+ Request.ResolveAction(uniqueSafetySourceId),
+ Response.SetData(safetySourceTestData.information)
+ )
+ sendActionPendingIntentAndWaitWithPermission(action)
+
+ TestNotificationListener.waitForSuccessNotification("Issue solved") {
+ assertThat(NotificationCharacteristics.safetySourceIdMatches(it, uniqueSafetySourceId))
+ .isTrue()
+ assertThat(it.hasAutoCancel()).isTrue()
+ }
+ }
+
+ // TODO(b/284271124): Decide what to do with existing notifications when flag flipped off
+ @Test
+ fun sendActionPendingIntent_flagDisabled_pendingIntentNotSentToSource() {
+ safetyCenterTestHelper.setData(
+ uniqueSafetySourceId,
+ safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage(
+ sourceId = uniqueSafetySourceId
+ )
+ )
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
+ val action =
+ notificationWithChannel.statusBarNotification.notification.actions.firstOrNull()
+ checkNotNull(action) { "Notification action unexpectedly null" }
+ SafetySourceReceiver.setResponse(
+ Request.ResolveAction(uniqueSafetySourceId),
+ Response.SetData(safetySourceTestData.information)
+ )
+ SafetyCenterFlags.notificationsEnabled = false
+
+ assertFailsWith(TimeoutCancellationException::class) {
+ sendActionPendingIntentAndWaitWithPermission(action, timeout = TIMEOUT_SHORT)
+ }
+ }
+
+ @Test
+ fun sendActionPendingIntent_sourceStateChangedSafetyEvent_successNotification() {
+ safetyCenterTestHelper.setData(
+ uniqueSafetySourceId,
+ safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage(
+ sourceId = uniqueSafetySourceId
)
)
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
+ val action =
+ notificationWithChannel.statusBarNotification.notification.actions.firstOrNull()
+ checkNotNull(action) { "Notification action unexpectedly null" }
+ SafetySourceReceiver.setResponse(
+ Request.ResolveAction(uniqueSafetySourceId),
+ Response.SetData(
+ safetySourceTestData.information,
+ overrideSafetyEvent =
+ SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
+ )
+ )
+
+ sendActionPendingIntentAndWaitWithPermission(action)
+
+ TestNotificationListener.waitForSuccessNotification("Issue solved") {
+ assertThat(NotificationCharacteristics.safetySourceIdMatches(it, uniqueSafetySourceId))
+ .isTrue()
+ }
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
+ fun sendActionPendingIntent_actionIdDiffersFromIssueActionId_successNotification() {
+ val notification =
+ SafetySourceIssue.Notification.Builder("Custom title", "Custom text")
+ .addAction(
+ SafetySourceIssue.Action.Builder(
+ "notification_action_id",
+ "Solve now!",
+ safetySourceTestData.resolvingActionPendingIntent(
+ sourceId = uniqueSafetySourceId,
+ sourceIssueActionId = "notification_action_id"
+ )
+ )
+ .setWillResolve(true)
+ .setSuccessMessage("Solved via notification action :)")
+ .build()
+ )
+ .build()
+ val data =
+ safetySourceTestData
+ .defaultCriticalDataBuilder()
+ .clearIssues()
+ .addIssue(
+ safetySourceTestData
+ .defaultCriticalResolvingIssueBuilder()
+ .clearActions()
+ .addAction(
+ SafetySourceIssue.Action.Builder(
+ "issue_action_id",
+ "Default action",
+ safetySourceTestData.resolvingActionPendingIntent(
+ sourceId = uniqueSafetySourceId,
+ sourceIssueActionId = "issue_action_id"
+ )
+ )
+ .setWillResolve(true)
+ .setSuccessMessage("Solved via issue action :(")
+ .build()
+ )
+ .setCustomNotification(notification)
+ .setNotificationBehavior(
+ SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY
+ )
+ .build()
+ )
+ .build()
+
+ safetyCenterTestHelper.setData(uniqueSafetySourceId, data)
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve now!"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
+ val action =
+ notificationWithChannel.statusBarNotification.notification.actions.firstOrNull()
+ checkNotNull(action) { "Notification action unexpectedly null" }
+ SafetySourceReceiver.setResponse(
+ Request.ResolveAction(uniqueSafetySourceId),
+ Response.SetData(safetySourceTestData.information)
+ )
+
+ sendActionPendingIntentAndWaitWithPermission(action)
+
+ TestNotificationListener.waitForSuccessNotification("Solved via notification action :)") {
+ assertThat(NotificationCharacteristics.safetySourceIdMatches(it, uniqueSafetySourceId))
+ .isTrue()
+ }
}
@Test
@@ -912,14 +1184,23 @@ class SafetyCenterNotificationTest {
// Here we cause a notification with an action to be posted and prepare the fake receiver
// to resolve that action successfully.
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ uniqueSafetySourceId,
+ safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId)
)
- val notificationWithChannel = TestNotificationListener.waitForSingleNotification()
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
val action =
notificationWithChannel.statusBarNotification.notification.actions.firstOrNull()
checkNotNull(action) { "Notification action unexpectedly null" }
- SafetySourceReceiver.setResponse(Request.ResolveAction(SINGLE_SOURCE_ID), Response.Error)
+ SafetySourceReceiver.setResponse(
+ Request.ResolveAction(uniqueSafetySourceId),
+ Response.Error
+ )
val listener = safetyCenterTestHelper.addListener()
sendActionPendingIntentAndWaitWithPermission(action)
@@ -931,22 +1212,31 @@ class SafetyCenterNotificationTest {
assertThat(listenerData2.inFlightActions).isEmpty()
assertThat(listenerData2.status.severityLevel)
.isEqualTo(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
- TestNotificationListener.waitForSingleNotification()
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Solve issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
}
@Test
fun sendContentPendingIntent_singleIssue_opensSafetyCenterWithIssueVisible() {
safetyCenterTestHelper.setData(
- SINGLE_SOURCE_ID,
+ uniqueSafetySourceId,
safetySourceTestData.recommendationWithDeviceIssue
)
- val notificationWithChannel = TestNotificationListener.waitForSingleNotification()
- val contentIntent = notificationWithChannel.statusBarNotification.notification.contentIntent
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
- executeBlockAndExit(
- launchActivity = { PendingIntentSender.send(contentIntent) },
- block = { waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue) }
- )
+ sendContentPendingIntent(notificationWithChannel) {
+ waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue)
+ }
}
@Test
@@ -959,37 +1249,93 @@ class SafetyCenterNotificationTest {
)
safetyCenterTestHelper.setData(
SOURCE_ID_2,
- safetySourceTestData.criticalWithResolvingGeneralIssue
+ safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = SOURCE_ID_2)
)
- val notificationWithChannel = TestNotificationListener.waitForSingleNotification()
- val contentIntent = notificationWithChannel.statusBarNotification.notification.contentIntent
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = SOURCE_ID_1,
+ )
+ )
- executeBlockAndExit(
- launchActivity = { PendingIntentSender.send(contentIntent) },
- block = {
- waitSourceIssueDisplayed(safetySourceTestData.criticalResolvingGeneralIssue)
- waitSourceIssueDisplayed(safetySourceTestData.recommendationGeneralIssue)
- }
+ sendContentPendingIntent(notificationWithChannel) {
+ waitSourceIssueDisplayed(safetySourceTestData.criticalResolvingGeneralIssue)
+ waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue)
+ }
+ }
+
+ @Test
+ fun whenGreenIssue_notificationHasAutoCancel() {
+ safetyCenterTestHelper.setData(
+ uniqueSafetySourceId,
+ safetySourceTestData.informationWithIssue
)
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("Review"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
+
+ assertThat(notificationWithChannel.statusBarNotification.hasAutoCancel()).isTrue()
}
- companion object {
- private val SafetyCenterData.inFlightActions: List<SafetyCenterIssue.Action>
+ @Test
+ fun whenNotGreenIssue_notificationDoesntHaveAutoCancel() {
+ safetyCenterTestHelper.setData(
+ uniqueSafetySourceId,
+ safetySourceTestData.recommendationWithDeviceIssue
+ )
+ val notificationWithChannel =
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = uniqueSafetySourceId,
+ )
+ )
+
+ assertThat(notificationWithChannel.statusBarNotification.hasAutoCancel()).isFalse()
+ }
+
+ private companion object {
+ val SafetyCenterData.inFlightActions: List<SafetyCenterIssue.Action>
get() = issues.flatMap { it.actions }.filter { it.isInFlight }
- private fun sendActionPendingIntentAndWaitWithPermission(action: Notification.Action) {
+ fun sendActionPendingIntentAndWaitWithPermission(
+ action: Notification.Action,
+ timeout: Duration = Coroutines.TIMEOUT_LONG
+ ) {
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
PendingIntentSender.send(action.actionIntent)
// Sending the action's PendingIntent above is asynchronous and we need to wait for
// it to be received by the fake receiver below.
- SafetySourceReceiver.receiveResolveAction()
+ SafetySourceReceiver.receiveResolveAction(timeout)
}
}
- private fun setFlagsForImmediateNotifications(vararg sourceIds: String) {
+ fun setFlagsForImmediateNotifications(vararg sourceIds: String) {
SafetyCenterFlags.notificationsAllowedSources = sourceIds.toSet()
SafetyCenterFlags.immediateNotificationBehaviorIssues =
sourceIds.map { "$it/$ISSUE_TYPE_ID" }.toSet()
}
+
+ fun StatusBarNotification.hasAutoCancel(): Boolean {
+ val autoCancelMask = notification.flags and Notification.FLAG_AUTO_CANCEL
+ return autoCancelMask != 0
+ }
+
+ fun sendContentPendingIntent(
+ statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
+ andExecuteBlock: () -> Unit = {}
+ ) {
+ val contentIntent =
+ statusBarNotificationWithChannel.statusBarNotification.notification.contentIntent
+ executeBlockAndExit(
+ launchActivity = { PendingIntentSender.send(contentIntent) },
+ block = andExecuteBlock
+ )
+ }
}
}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterShellCommandsTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterShellCommandsTest.kt
index b0860d751..7c5b41944 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterShellCommandsTest.kt
+++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterShellCommandsTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,20 +14,23 @@
* limitations under the License.
*/
-package android.safetycenter.cts
+package android.safetycenter.functional
+import android.Manifest.permission.INTERACT_ACROSS_USERS
+import android.app.ActivityManager
import android.content.Context
import android.safetycenter.SafetyCenterManager
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compatibility.common.util.SystemUtil
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.isSafetyCenterEnabledWithPermission
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
+import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
+import com.android.safetycenter.testing.deviceSupportsSafetyCenter
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-/** CTS tests for Safety Center's shell commands. */
+/** Tests for Safety Center's shell commands. */
@RunWith(AndroidJUnit4::class)
class SafetyCenterShellCommandsTest {
private val context: Context = getApplicationContext()
@@ -54,6 +57,39 @@ class SafetyCenterShellCommandsTest {
assertThat(packageName).isEqualTo(context.packageManager.permissionControllerPackageName)
}
+ @Test
+ fun clearData_executesSuccessfully() {
+ executeShellCommand("cmd safety_center clear-data")
+ }
+
+ @Test
+ fun refresh_executesSuccessfully() {
+ val currentUser =
+ callWithShellPermissionIdentity(INTERACT_ACROSS_USERS) {
+ ActivityManager.getCurrentUser()
+ }
+ executeShellCommand("cmd safety_center refresh --reason OTHER --user $currentUser")
+ }
+
+ @Test
+ fun help_containsAllCommands() {
+ val help = executeShellCommand("cmd safety_center help")
+
+ assertThat(help).contains("help")
+ assertThat(help).contains("enabled")
+ assertThat(help).contains("supported")
+ assertThat(help).contains("package-name")
+ assertThat(help).contains("clear-data")
+ assertThat(help).contains("refresh")
+ }
+
+ @Test
+ fun dump_containsSafetyCenterService() {
+ val dump = executeShellCommand("dumpsys safety_center")
+
+ assertThat(dump).contains("SafetyCenterService")
+ }
+
private fun executeShellCommand(command: String): String =
- SystemUtil.runShellCommand(command).trim()
+ SystemUtil.runShellCommandOrThrow(command).trim()
}
diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt
new file mode 100644
index 000000000..4ba293eb9
--- /dev/null
+++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt
@@ -0,0 +1,298 @@
+package android.safetycenter.functional
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+import android.safetycenter.SafetyCenterManager
+import android.safetycenter.SafetySourceData
+import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION
+import android.safetycenter.SafetySourceIssue
+import android.safetycenter.SafetySourceStatus
+import androidx.annotation.RequiresApi
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.android.compatibility.common.preconditions.ScreenLockHelper
+import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetySourceDataWithPermission
+import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.refreshSafetySourcesWithPermission
+import com.android.safetycenter.testing.SafetyCenterFlags
+import com.android.safetycenter.testing.SafetyCenterTestConfigs
+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
+import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
+import com.android.safetycenter.testing.SafetySourceTestData
+import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeTrue
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Functional tests for "fixes" applied to Safety Source data received by [SafetyCenterManager]. */
+@RunWith(AndroidJUnit4::class)
+class SafetySourceDataFixesTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
+ private val safetySourceTestData = SafetySourceTestData(context)
+ private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
+ private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
+
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
+
+ @Test
+ fun lockScreenSource_withoutReplaceLockScreenIconActionFlag_doesntReplace() {
+ // Must have a screen lock for the icon action to be set
+ assumeTrue(ScreenLockHelper.isDeviceSecure(context))
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.settingsLockScreenSourceConfig)
+ val listener = safetyCenterTestHelper.addListener()
+ SafetyCenterFlags.replaceLockScreenIconAction = false
+
+ safetyCenterManager.refreshSafetySourcesWithPermission(
+ SafetyCenterManager.REFRESH_REASON_PAGE_OPEN
+ )
+ // Skip loading data.
+ listener.receiveSafetyCenterData()
+
+ val lockScreenSafetyCenterData = listener.receiveSafetyCenterData()
+ val lockScreenEntry = lockScreenSafetyCenterData.entriesOrGroups.first().entry!!
+ val entryPendingIntent = lockScreenEntry.pendingIntent!!
+ val iconActionPendingIntent = lockScreenEntry.iconAction!!.pendingIntent
+ // This test passes for now but will eventually start failing once we introduce the fix in
+ // the Settings app. This will warn if the assumption is failed rather than fail, at which
+ // point we can remove this test (and potentially even this magnificent hack).
+ assumeTrue(iconActionPendingIntent == entryPendingIntent)
+ }
+
+ @Test
+ fun lockScreenSource_withReplaceLockScreenIconActionFlag_replaces() {
+ // Must have a screen lock for the icon action to be set
+ assumeTrue(ScreenLockHelper.isDeviceSecure(context))
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.settingsLockScreenSourceConfig)
+ val listener = safetyCenterTestHelper.addListener()
+
+ safetyCenterManager.refreshSafetySourcesWithPermission(
+ SafetyCenterManager.REFRESH_REASON_PAGE_OPEN
+ )
+ // Skip loading data.
+ listener.receiveSafetyCenterData()
+
+ val lockScreenSafetyCenterData = listener.receiveSafetyCenterData()
+ val lockScreenEntry = lockScreenSafetyCenterData.entriesOrGroups.first().entry!!
+ val entryPendingIntent = lockScreenEntry.pendingIntent!!
+ val iconActionPendingIntent = lockScreenEntry.iconAction!!.pendingIntent
+ assertThat(iconActionPendingIntent).isNotEqualTo(entryPendingIntent)
+ }
+
+ @Test
+ fun defaultActionOverride_issue_overridesMatchingActions() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ val targetActionId = "TargetActionId"
+ SafetyCenterFlags.actionsToOverrideWithDefaultIntent =
+ mapOf(SINGLE_SOURCE_ID to setOf(targetActionId, "AdditionalActionId"))
+
+ val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT"))
+ val dataWithActionToOverride =
+ sourceDataBuilder()
+ .addIssue(
+ issueBuilder()
+ .clearActions()
+ .addAction(
+ safetySourceTestData.action(
+ id = targetActionId,
+ pendingIntent = originalPendingIntent
+ )
+ )
+ .build()
+ )
+ .build()
+
+ safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, dataWithActionToOverride)
+
+ val overriddenPendingIntent =
+ safetyCenterManager
+ .getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)!!
+ .issues[0]
+ .actions[0]
+ .pendingIntent
+ val expectedPendingIntent =
+ pendingIntent(
+ Intent(SafetyCenterTestConfigs.ACTION_TEST_ACTIVITY).setPackage(context.packageName)
+ )
+ assertThat(intentsFilterEqual(overriddenPendingIntent, expectedPendingIntent)).isTrue()
+ }
+ @Test
+ @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE)
+ fun defaultActionOverride_notification_overridesMatchingActions() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ val targetActionId = "TargetActionId"
+ SafetyCenterFlags.actionsToOverrideWithDefaultIntent =
+ mapOf(SINGLE_SOURCE_ID to setOf(targetActionId, "AdditionalActionId"))
+
+ val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT"))
+ val dataWithNotificationActionToOverride =
+ sourceDataBuilder()
+ .addIssue(
+ issueBuilder()
+ .setCustomNotification(
+ notification(
+ safetySourceTestData.action(
+ id = targetActionId,
+ pendingIntent = originalPendingIntent
+ )
+ )
+ )
+ .build()
+ )
+ .build()
+
+ safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, dataWithNotificationActionToOverride)
+
+ val overriddenPendingIntent =
+ safetyCenterManager
+ .getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)!!
+ .issues[0]
+ .customNotification!!
+ .actions[0]
+ .pendingIntent
+ val expectedPendingIntent =
+ pendingIntent(
+ Intent(SafetyCenterTestConfigs.ACTION_TEST_ACTIVITY).setPackage(context.packageName)
+ )
+ assertThat(intentsFilterEqual(overriddenPendingIntent, expectedPendingIntent)).isTrue()
+ }
+
+ @Test
+ fun defaultActionOverride_sameActionIdDifferentSource_doesNotOverride() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
+ val targetActionId = "TargetActionId"
+ SafetyCenterFlags.actionsToOverrideWithDefaultIntent =
+ mapOf(SOURCE_ID_1 to setOf(targetActionId, "AdditionalActionId"))
+
+ val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT"))
+ val dataWithoutActionToOverride =
+ sourceDataBuilder()
+ .addIssue(
+ issueBuilder()
+ .clearActions()
+ .addAction(
+ safetySourceTestData.action(
+ id = targetActionId,
+ pendingIntent = originalPendingIntent
+ )
+ )
+ .build()
+ )
+ .build()
+
+ safetyCenterTestHelper.setData(
+ SOURCE_ID_2, // Different source ID
+ dataWithoutActionToOverride
+ )
+
+ val actualPendingIntent =
+ safetyCenterManager
+ .getSafetySourceDataWithPermission(SOURCE_ID_2)!!
+ .issues[0]
+ .actions[0]
+ .pendingIntent
+ assertThat(intentsFilterEqual(actualPendingIntent, originalPendingIntent)).isTrue()
+ }
+
+ @Test
+ fun defaultActionOverride_sameSourceDifferentActionId_doesNotOverride() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
+ SafetyCenterFlags.actionsToOverrideWithDefaultIntent =
+ mapOf(SOURCE_ID_1 to setOf("TargetActionId"))
+
+ val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT"))
+ val dataWithoutActionToOverride =
+ sourceDataBuilder()
+ .addIssue(
+ issueBuilder()
+ .clearActions()
+ .addAction(
+ safetySourceTestData.action(
+ id = "DifferentActionId",
+ pendingIntent = originalPendingIntent
+ )
+ )
+ .build()
+ )
+ .build()
+
+ safetyCenterTestHelper.setData(SOURCE_ID_1, dataWithoutActionToOverride)
+
+ val actualPendingIntent =
+ safetyCenterManager
+ .getSafetySourceDataWithPermission(SOURCE_ID_1)!!
+ .issues[0]
+ .actions[0]
+ .pendingIntent
+ assertThat(intentsFilterEqual(actualPendingIntent, originalPendingIntent)).isTrue()
+ }
+
+ @Test
+ fun defaultActionOverride_noDefaultIntent_doesNotOverride() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceInvalidIntentConfig)
+ val targetActionId = "TargetActionId"
+ SafetyCenterFlags.actionsToOverrideWithDefaultIntent =
+ mapOf(SINGLE_SOURCE_ID to setOf(targetActionId, "AdditionalActionId"))
+
+ val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT"))
+ val dataWithActionToOverride =
+ sourceDataBuilder()
+ .addIssue(
+ issueBuilder()
+ .clearActions()
+ .addAction(
+ safetySourceTestData.action(
+ id = targetActionId,
+ pendingIntent = originalPendingIntent
+ )
+ )
+ .build()
+ )
+ .build()
+
+ safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, dataWithActionToOverride)
+
+ val actualPendingIntent =
+ safetyCenterManager
+ .getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)!!
+ .issues[0]
+ .actions[0]
+ .pendingIntent
+ assertThat(intentsFilterEqual(actualPendingIntent, originalPendingIntent)).isTrue()
+ }
+
+ private fun issueBuilder() = safetySourceTestData.defaultInformationIssueBuilder()
+
+ private fun pendingIntent(intent: Intent) =
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+
+ companion object {
+ private fun sourceDataBuilder() =
+ SafetySourceData.Builder()
+ .setStatus(
+ SafetySourceStatus.Builder("OK", "Blah", SEVERITY_LEVEL_INFORMATION).build()
+ )
+
+ @RequiresApi(UPSIDE_DOWN_CAKE)
+ private fun notification(action: SafetySourceIssue.Action) =
+ SafetySourceIssue.Notification.Builder("Blah", "Bleh").addAction(action).build()
+
+ private fun intentsFilterEqual(
+ actualPendingIntent: 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/SafetyCenterQsActivityTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterQsActivityTest.kt
index 9c4d720d3..73d6a0737 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterQsActivityTest.kt
+++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterQsActivityTest.kt
@@ -16,29 +16,23 @@
package android.safetycenter.functional.ui
-import android.Manifest.permission.MANAGE_SENSOR_PRIVACY
-import android.Manifest.permission.OBSERVE_SENSOR_PRIVACY
import android.content.Context
-import android.hardware.SensorPrivacyManager
import android.hardware.SensorPrivacyManager.Sensors.CAMERA
import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
-import android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE
-import android.platform.test.rule.ScreenRecordRule
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import com.android.compatibility.common.util.DisableAnimationRule
import com.android.compatibility.common.util.FreezeRotationRule
+import com.android.safetycenter.testing.EnableSensorRule
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterQsActivity
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestConfigs
import com.android.safetycenter.testing.SafetyCenterTestHelper
-import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
+import com.android.safetycenter.testing.SafetyCenterTestRule
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitPageTitleDisplayed
-import org.junit.After
-import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -48,66 +42,22 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SafetyCenterQsActivityTest {
- @get:Rule val disableAnimationRule = DisableAnimationRule()
-
- @get:Rule val freezeRotationRule = FreezeRotationRule()
-
- @get:Rule val screenRecordRule = ScreenRecordRule()
-
private val context: Context = getApplicationContext()
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
- private val sensorPrivacyManager = context.getSystemService(SensorPrivacyManager::class.java)!!
- private var shouldRunTests =
- context.deviceSupportsSafetyCenter() &&
- deviceSupportsSensorToggle(CAMERA) &&
- deviceSupportsSensorToggle(MICROPHONE)
- private var oldCameraState: Boolean = false
- private var oldMicrophoneState: Boolean = false
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val enableCameraRule = EnableSensorRule(context, CAMERA)
+ @get:Rule(order = 3) val enableMicrophoneRule = EnableSensorRule(context, MICROPHONE)
+ @get:Rule(order = 4) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
+ @get:Rule(order = 5) val disableAnimationRule = DisableAnimationRule()
+ @get:Rule(order = 6) val freezeRotationRule = FreezeRotationRule()
@Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
+ fun setTestConfigBeforeTest() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
}
- @After
- fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
- }
-
- @Before
- fun enablePrivacyControlsBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- oldCameraState = isSensorEnabled(CAMERA)
- setSensorState(CAMERA, true)
-
- oldMicrophoneState = isSensorEnabled(MICROPHONE)
- setSensorState(MICROPHONE, true)
- }
-
- @After
- fun restorePrivacyControlsAfterTest() {
- if (!shouldRunTests) {
- return
- }
- setSensorState(CAMERA, oldCameraState)
- setSensorState(MICROPHONE, oldMicrophoneState)
- }
-
@Test
fun launchActivity_fromQuickSettings_hasContentDescriptions() {
context.launchSafetyCenterQsActivity {
@@ -122,7 +72,6 @@ class SafetyCenterQsActivityTest {
}
@Test
- @ScreenRecordRule.ScreenRecord
fun launchActivity_togglePrivacyControls_hasUpdatedDescriptions() {
context.launchSafetyCenterQsActivity {
// Toggle privacy controls
@@ -134,25 +83,4 @@ class SafetyCenterQsActivityTest {
waitDisplayed(By.desc("Switch. Mic access. Blocked"))
}
}
-
- private fun deviceSupportsSensorToggle(sensor: Int): Boolean {
- return sensorPrivacyManager.supportsSensorToggle(sensor) &&
- sensorPrivacyManager.supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor)
- }
-
- private fun isSensorEnabled(sensor: Int): Boolean {
- val isSensorDisabled =
- callWithShellPermissionIdentity(OBSERVE_SENSOR_PRIVACY) {
- sensorPrivacyManager.isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor)
- }
- return !isSensorDisabled
- }
-
- private fun setSensorState(sensor: Int, enabled: Boolean) {
- val disableSensor = !enabled
- // The sensor is enabled iff the privacy control is disabled.
- callWithShellPermissionIdentity(MANAGE_SENSOR_PRIVACY, OBSERVE_SENSOR_PRIVACY) {
- sensorPrivacyManager.setSensorPrivacy(sensor, disableSensor)
- }
- }
}
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 f4ed328ae..f76a52256 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
@@ -17,31 +17,31 @@
package android.safetycenter.functional.ui
import android.content.Context
+import android.os.Build
+import android.safetycenter.SafetySourceData
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import com.android.compatibility.common.util.DisableAnimationRule
import com.android.compatibility.common.util.FreezeRotationRule
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestConfigs
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID
import com.android.safetycenter.testing.SafetyCenterTestData
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceIntentHandler.Request
import com.android.safetycenter.testing.SafetySourceIntentHandler.Response
import com.android.safetycenter.testing.SafetySourceReceiver
import com.android.safetycenter.testing.SafetySourceTestData
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.android.safetycenter.testing.UiTestHelper.RESCAN_BUTTON_LABEL
import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitButtonDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitButtonNotDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitNotDisplayed
-import org.junit.After
-import org.junit.Assume.assumeTrue
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -50,42 +50,17 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SafetyCenterStatusCardTest {
- @get:Rule val disableAnimationRule = DisableAnimationRule()
-
- @get:Rule val freezeRotationRule = FreezeRotationRule()
-
private val context: Context = getApplicationContext()
-
- private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context)
+ private val safetyCenterResourcesApk = SafetyCenterResourcesApk.forTests(context)
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetySourceTestData = SafetySourceTestData(context)
private val safetyCenterTestData = SafetyCenterTestData(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
- // JUnit's Assume is not supported in @BeforeClass by the tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
-
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
-
- @Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
- }
-
- @After
- fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
- }
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
+ @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule()
+ @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule()
@Test
fun withUnknownStatus_displaysScanningOnLoad() {
@@ -93,8 +68,8 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity {
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName("scanning_title"),
- safetyCenterResourcesContext.getStringByName("loading_summary")
+ safetyCenterResourcesApk.getStringByName("scanning_title"),
+ safetyCenterResourcesApk.getStringByName("loading_summary")
)
}
}
@@ -109,8 +84,8 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity {
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesContext.getStringByName("loading_summary")
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
+ safetyCenterResourcesApk.getStringByName("loading_summary")
)
}
}
@@ -122,12 +97,8 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_ok_review_title"
- ),
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_ok_review_summary"
- )
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_title"),
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_summary")
)
waitButtonDisplayed(RESCAN_BUTTON_LABEL)
}
@@ -136,6 +107,7 @@ class SafetyCenterStatusCardTest {
@Test
fun withInformationAndNoIssues_hasRescanButton() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
Response.SetData(safetySourceTestData.information)
@@ -143,8 +115,8 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_summary")
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary")
)
waitButtonDisplayed(RESCAN_BUTTON_LABEL)
}
@@ -153,6 +125,7 @@ class SafetyCenterStatusCardTest {
@Test
fun withInformationAndNoIssues_hasContentDescriptions() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
Response.SetData(safetySourceTestData.information)
@@ -167,6 +140,7 @@ class SafetyCenterStatusCardTest {
@Test
fun withInformationIssue_doesNotHaveRescanButton() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
Response.SetData(safetySourceTestData.informationWithIssue)
@@ -174,7 +148,7 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
safetyCenterTestData.getAlertString(1)
)
waitButtonNotDisplayed(RESCAN_BUTTON_LABEL)
@@ -184,6 +158,10 @@ class SafetyCenterStatusCardTest {
@Test
fun withRecommendationIssue_doesNotHaveRescanButton() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ safetyCenterTestHelper.setData(
+ SINGLE_SOURCE_ID,
+ safetySourceTestData.recommendationWithGeneralIssue
+ )
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
Response.SetData(safetySourceTestData.recommendationWithGeneralIssue)
@@ -191,7 +169,7 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName(
+ safetyCenterResourcesApk.getStringByName(
"overall_severity_level_safety_recommendation_title"
),
safetyCenterTestData.getAlertString(1)
@@ -203,6 +181,10 @@ class SafetyCenterStatusCardTest {
@Test
fun withCriticalWarningIssue_doesNotHaveRescanButton() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ safetyCenterTestHelper.setData(
+ SINGLE_SOURCE_ID,
+ safetySourceTestData.criticalWithResolvingGeneralIssue
+ )
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue)
@@ -210,7 +192,7 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName(
+ safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_safety_warning_title"
),
safetyCenterTestData.getAlertString(1)
@@ -222,6 +204,7 @@ class SafetyCenterStatusCardTest {
@Test
fun withKnownStatus_displaysScanningOnRescan() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
Response.SetData(safetySourceTestData.information)
@@ -229,15 +212,15 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_summary")
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary")
)
waitButtonDisplayed(RESCAN_BUTTON_LABEL) { it.click() }
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName("scanning_title"),
- safetyCenterResourcesContext.getStringByName("loading_summary")
+ safetyCenterResourcesApk.getStringByName("scanning_title"),
+ safetyCenterResourcesApk.getStringByName("loading_summary")
)
}
}
@@ -245,6 +228,7 @@ class SafetyCenterStatusCardTest {
@Test
fun rescan_updatesDataAfterScanCompletes() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information)
SafetySourceReceiver.setResponse(
Request.Refresh(SINGLE_SOURCE_ID),
Response.SetData(safetySourceTestData.information)
@@ -256,18 +240,28 @@ class SafetyCenterStatusCardTest {
context.launchSafetyCenterActivity(withReceiverPermission = true) {
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_summary")
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary")
)
waitButtonDisplayed(RESCAN_BUTTON_LABEL) { it.click() }
waitAllTextDisplayed(
- safetyCenterResourcesContext.getStringByName(
+ safetyCenterResourcesApk.getStringByName(
"overall_severity_level_safety_recommendation_title"
),
safetyCenterTestData.getAlertString(1)
)
}
}
+
+ /**
+ * Sets the given data for the given source ID if this test is running on T builds. This is a
+ * mitigation for b/301234118 which seems to only fail consistently on T.
+ */
+ private fun preSetDataOnT(sourceId: String, safetySourceData: SafetySourceData) {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
+ safetyCenterTestHelper.setData(sourceId, safetySourceData)
+ }
+ }
}
diff --git a/tests/functional/safetycenter/subpages/Android.bp b/tests/functional/safetycenter/subpages/Android.bp
new file mode 100644
index 000000000..4279ca26f
--- /dev/null
+++ b/tests/functional/safetycenter/subpages/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "SafetyCenterSubpagesTestCases",
+ defaults: ["mts-target-sdk-version-current"],
+ sdk_version: "test_current",
+ min_sdk_version: "30",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "androidx.test.monitor",
+ "compatibility-device-preconditions",
+ "kotlin-test",
+ "platform-test-rules",
+ "safety-center-test-util-lib",
+ ],
+ test_suites: [
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/functional/safetycenter/subpages/AndroidManifest.xml b/tests/functional/safetycenter/subpages/AndroidManifest.xml
new file mode 100644
index 000000000..ea59a99c6
--- /dev/null
+++ b/tests/functional/safetycenter/subpages/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.safetycenter.functional.subpages">
+ <application>
+
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Functional tests for SafetyCenter Subpages"
+ android:targetPackage="android.safetycenter.functional.subpages"/>
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+</manifest>
diff --git a/tests/functional/safetycenter/subpages/AndroidTest.xml b/tests/functional/safetycenter/subpages/AndroidTest.xml
new file mode 100644
index 000000000..c3245e9d7
--- /dev/null
+++ b/tests/functional/safetycenter/subpages/AndroidTest.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<configuration description="Config for SafetyCenter subpages testcases">
+
+ <object
+ class="com.android.tradefed.testtype.suite.module.Sdk34ModuleController"
+ type="module_controller"/>
+
+ <option name="config-descriptor:metadata" key="component" value="framework"/>
+ <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="secondary_user"/>
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" />
+ <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
+
+ <option name="test-suite-tag" value="functional"/>
+
+ <target_preparer
+ class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="SafetyCenterSubpagesTestCases.apk"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- Ensure all broadcasts are dispatched prior to running our tests, to make sure they
+ aren't polluted by `BOOT_COMPLETED` or similar broadcasts still being delivered, which
+ causes our `ActivityManager#waitForBroadcastIdle()` calls to timeout. -->
+ <option name="run-command" value="am wait-for-broadcast-idle" />
+ <option name="run-command" value="am wait-for-broadcast-barrier" />
+ <!-- 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" />
+ <!-- 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>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/user/0/android.safetycenter.functional.subpages/files" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.safetycenter.functional.subpages"/>
+ <option name="exclude-annotation" value="org.junit.Ignore"/>
+ <option name="runtime-hint" value="10m"/>
+ </test>
+</configuration>
diff --git a/tests/functional/safetycenter/subpages/TEST_MAPPING b/tests/functional/safetycenter/subpages/TEST_MAPPING
new file mode 100644
index 000000000..455ad21d0
--- /dev/null
+++ b/tests/functional/safetycenter/subpages/TEST_MAPPING
@@ -0,0 +1,17 @@
+{
+ "presubmit": [
+ {
+ "name": "SafetyCenterSubpagesTestCases"
+ }
+ ],
+ "mainline-presubmit": [
+ {
+ "name": "SafetyCenterSubpagesTestCases[com.google.android.permission.apex]",
+ "options": [
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt
index 0c90029e0..7ad83b949 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt
+++ b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt
@@ -17,13 +17,15 @@
package android.safetycenter.functional.ui
import android.content.Context
-import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+import android.content.pm.PackageManager
+import android.hardware.SensorPrivacyManager
+import android.hardware.SensorPrivacyManager.Sensors.CAMERA
+import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
import android.os.Bundle
import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID
import android.safetycenter.config.SafetySource
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SdkSuppress
import androidx.test.uiautomator.By
import com.android.compatibility.common.util.DisableAnimationRule
import com.android.compatibility.common.util.FreezeRotationRule
@@ -31,23 +33,24 @@ import com.android.compatibility.common.util.UiAutomatorUtils2
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.openPageAndExit
import com.android.safetycenter.testing.SafetyCenterFlags
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestConfigs
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.PRIVACY_SOURCE_ID_1
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_1
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceTestData
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.android.safetycenter.testing.UiTestHelper.MORE_ISSUES_LABEL
import com.android.safetycenter.testing.UiTestHelper.clickMoreIssuesCard
import com.android.safetycenter.testing.UiTestHelper.resetRotation
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 org.junit.After
-import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -55,42 +58,27 @@ import org.junit.runner.RunWith
/** Functional tests for the Privacy subpage in Safety Center. */
@RunWith(AndroidJUnit4::class)
-@SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
class PrivacySubpageTest {
- @get:Rule val disableAnimationRule = DisableAnimationRule()
-
- @get:Rule val freezeRotationRule = FreezeRotationRule()
-
private val context: Context = getApplicationContext()
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetySourceTestData = SafetySourceTestData(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
+ private val sensorPrivacyManager: SensorPrivacyManager =
+ context.getSystemService(SensorPrivacyManager::class.java)!!
- // JUnit's Assume is not supported in @BeforeClass by the tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
-
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
+ @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule()
+ @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule()
@Before
fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
SafetyCenterFlags.showSubpages = true
}
@After
fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
UiAutomatorUtils2.getUiDevice().resetRotation()
}
@@ -166,13 +154,20 @@ class PrivacySubpageTest {
extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, config.safetySourcesGroups.first().id)
context.launchSafetyCenterActivity(extras) {
- waitAllTextDisplayed(
- "Camera access",
- "Microphone access",
- "Show clipboard access",
- "Show passwords",
- "Location access"
+ waitAllText(
+ displayed = sensorPrivacyManager.supportsSensorToggle(CAMERA),
+ text = "Camera access"
+ )
+ waitAllText(
+ displayed = sensorPrivacyManager.supportsSensorToggle(MICROPHONE),
+ text = "Microphone access"
+ )
+ waitAllTextDisplayed("Show clipboard access")
+ waitAllText(
+ displayed = getPermissionControllerBool("config_display_show_password_toggle"),
+ text = "Show passwords"
)
+ waitAllTextDisplayed("Location access")
}
}
@@ -216,6 +211,34 @@ class PrivacySubpageTest {
}
}
+ private fun waitAllText(displayed: Boolean, text: String) {
+ if (displayed) {
+ waitAllTextDisplayed(text)
+ } else {
+ waitAllTextNotDisplayed(text)
+ }
+ }
+
+ private fun getPermissionControllerBool(resourceName: String): Boolean {
+ val permissionControllerContext = getPermissionControllerContext()
+ val resourceId =
+ permissionControllerContext.resources.getIdentifier(
+ resourceName,
+ "bool",
+ "com.android.permissioncontroller"
+ )
+ return permissionControllerContext.resources.getBoolean(resourceId)
+ }
+
+ private fun getPermissionControllerContext(): Context {
+ val permissionControllerPkg = context.packageManager.permissionControllerPackageName
+ try {
+ return context.createPackageContext(permissionControllerPkg, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ throw RuntimeException(e)
+ }
+ }
+
companion object {
private const val EXTRA_SETTINGS_FRAGMENT_ARGS_KEY = ":settings:fragment_args_key"
}
diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt
index eeb512037..236beb34e 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt
+++ b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt
@@ -17,7 +17,6 @@
package android.safetycenter.functional.ui
import android.content.Context
-import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.os.Bundle
import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID
import android.safetycenter.SafetySourceData
@@ -26,21 +25,16 @@ import android.safetycenter.config.SafetySource
import android.safetycenter.config.SafetySourcesGroup
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SdkSuppress
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import com.android.compatibility.common.util.DisableAnimationRule
import com.android.compatibility.common.util.FreezeRotationRule
-import com.android.compatibility.common.util.RetryRule
import com.android.compatibility.common.util.UiAutomatorUtils2
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG
import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.openPageAndExit
-import com.android.safetycenter.testing.SafetyCenterActivityLauncher.openPageAndExitAllowingRetries
import com.android.safetycenter.testing.SafetyCenterFlags
-import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import com.android.safetycenter.testing.SafetyCenterTestConfigs
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.MULTIPLE_SOURCES_GROUP_ID_1
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID
@@ -51,10 +45,12 @@ import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_5
import com.android.safetycenter.testing.SafetyCenterTestData
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceIntentHandler.Request
import com.android.safetycenter.testing.SafetySourceIntentHandler.Response
import com.android.safetycenter.testing.SafetySourceReceiver
import com.android.safetycenter.testing.SafetySourceTestData
+import com.android.safetycenter.testing.SupportsSafetyCenterRule
import com.android.safetycenter.testing.UiTestHelper.MORE_ISSUES_LABEL
import com.android.safetycenter.testing.UiTestHelper.clickConfirmDismissal
import com.android.safetycenter.testing.UiTestHelper.clickDismissIssueCard
@@ -75,64 +71,34 @@ import com.android.safetycenter.testing.UiTestHelper.waitPageTitleDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitPageTitleNotDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueDisplayed
import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueNotDisplayed
-import java.util.concurrent.TimeUnit
import org.junit.After
-import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
-import org.junit.rules.Timeout
import org.junit.runner.RunWith
/** Functional tests for generic subpages in Safety Center. */
@RunWith(AndroidJUnit4::class)
-@SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
class SafetyCenterSubpagesTest {
- @get:Rule val disableAnimationRule = DisableAnimationRule()
-
- @get:Rule val freezeRotationRule = FreezeRotationRule()
-
- // It is necessery to couple RetryRule and Timeout to ensure that all the retries together are
- // restricted with the test timeout
- @get:Rule val retryRule = RetryRule(/* retries= */ 3)
- @get:Rule
- val timeoutRule =
- Timeout(
- InstrumentationRegistry.getArguments().getString("timeout_msec", "60000").toLong(),
- TimeUnit.MILLISECONDS
- )
-
private val context: Context = getApplicationContext()
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetySourceTestData = SafetySourceTestData(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
- private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context)
+ private val safetyCenterResourcesApk = SafetyCenterResourcesApk.forTests(context)
- // JUnit's Assume is not supported in @BeforeClass by the tests runner, so this is used to
- // manually skip the setup and teardown methods.
- private val shouldRunTests = context.deviceSupportsSafetyCenter()
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
+ @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule()
+ @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule()
@Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
-
- @Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
+ fun enableSubpagesBeforeTest() {
SafetyCenterFlags.showSubpages = true
}
@After
- fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
+ fun resetRotationAfterTest() {
UiAutomatorUtils2.getUiDevice().resetRotation()
}
@@ -527,7 +493,7 @@ class SafetyCenterSubpagesTest {
)
context.launchSafetyCenterActivity(withReceiverPermission = true) {
- openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) {
+ openPageAndExit(context.getString(sourcesGroup.titleResId)) {
waitSourceIssueDisplayed(issue)
waitButtonDisplayed(action.label) { it.click() }
@@ -547,7 +513,7 @@ class SafetyCenterSubpagesTest {
val issue = sourceData.issues[0]
context.launchSafetyCenterActivity {
- openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) {
+ openPageAndExit(context.getString(sourcesGroup.titleResId)) {
waitSourceIssueDisplayed(issue)
clickDismissIssueCard()
@@ -568,7 +534,7 @@ class SafetyCenterSubpagesTest {
val issue = sourceData.issues[0]
context.launchSafetyCenterActivity {
- openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) {
+ openPageAndExit(context.getString(sourcesGroup.titleResId)) {
waitSourceIssueDisplayed(issue)
clickDismissIssueCard()
waitAllTextDisplayed("Dismiss this alert?")
@@ -611,7 +577,7 @@ class SafetyCenterSubpagesTest {
val issue = sourceData.issues[0]
context.launchSafetyCenterActivity {
- openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) {
+ openPageAndExit(context.getString(sourcesGroup.titleResId)) {
waitSourceIssueDisplayed(issue)
clickDismissIssueCard()
waitAllTextDisplayed("Dismiss this alert?")
@@ -635,7 +601,7 @@ class SafetyCenterSubpagesTest {
val issue = sourceData.issues[0]
context.launchSafetyCenterActivity {
- openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) {
+ openPageAndExit(context.getString(sourcesGroup.titleResId)) {
waitSourceIssueDisplayed(issue)
clickDismissIssueCard()
waitAllTextDisplayed("Dismiss this alert?")
@@ -822,6 +788,24 @@ class SafetyCenterSubpagesTest {
}
@Test
+ fun dismissedIssuesCard_doesntShowGreenCards() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ val (sourcesGroup, issue) =
+ prepareSingleSourceGroupWithIssue(
+ safetySourceTestData.informationWithIssueWithAttributionTitle
+ )
+ val safetyCenterIssueId = SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, issue.id)
+ safetyCenterTestHelper.dismissSafetyCenterIssue(safetyCenterIssueId)
+
+ context.launchSafetyCenterActivity {
+ openPageAndExit(context.getString(sourcesGroup.titleResId)) {
+ waitAllTextNotDisplayed("Dismissed alerts")
+ waitSourceIssueNotDisplayed(issue)
+ }
+ }
+ }
+
+ @Test
fun moreIssuesCard_expandWithDismissedIssues_showsAdditionalCards() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesInSingleGroupConfig)
@@ -968,9 +952,7 @@ class SafetyCenterSubpagesTest {
waitAllTextDisplayed(
context.getString(source.titleResId),
context.getString(source.summaryResId),
- safetyCenterResourcesContext.getStringByName(
- "test_single_source_group_id_footer"
- )
+ safetyCenterResourcesApk.getStringByName("test_single_source_group_id_footer")
)
}
}
diff --git a/tests/hostside/safetycenter/Android.bp b/tests/hostside/safetycenter/Android.bp
index 499e44f62..c66cae23a 100644
--- a/tests/hostside/safetycenter/Android.bp
+++ b/tests/hostside/safetycenter/Android.bp
@@ -27,6 +27,7 @@ java_test_host {
libs: [
"tradefed",
"junit",
+ "compatibility-host-util",
],
static_libs: [
"cts-statsd-atom-host-test-utils",
diff --git a/tests/hostside/safetycenter/AndroidTest.xml b/tests/hostside/safetycenter/AndroidTest.xml
index 09ddf9d84..a28b70c3c 100644
--- a/tests/hostside/safetycenter/AndroidTest.xml
+++ b/tests/hostside/safetycenter/AndroidTest.xml
@@ -28,9 +28,12 @@
aren't polluted by `BOOT_COMPLETED` or similar broadcasts still being delivered, which
causes our `ActivityManager#waitForBroadcastIdle()` calls to timeout. -->
<option name="run-command" value="am wait-for-broadcast-idle" />
+ <option name="run-command" value="am wait-for-broadcast-barrier" />
<!-- 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" />
+ <!-- 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>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.StayAwakePreparer" />
diff --git a/tests/hostside/safetycenter/helper-app/Android.bp b/tests/hostside/safetycenter/helper-app/Android.bp
index cf4372d99..a05f8d2f3 100644
--- a/tests/hostside/safetycenter/helper-app/Android.bp
+++ b/tests/hostside/safetycenter/helper-app/Android.bp
@@ -30,6 +30,7 @@ android_test_helper_app {
static_libs: [
"androidx.test.rules",
"androidx.test.ext.junit",
+ "safety-center-pending-intents",
"safety-center-test-util-lib",
],
} \ No newline at end of file
diff --git a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt
index c72166c65..6afcff85a 100644
--- a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt
+++ b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt
@@ -19,15 +19,25 @@ package android.safetycenter.hostside.device
import android.content.Context
import android.os.Bundle
import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID
+import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID
+import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compatibility.common.util.UiAutomatorUtils2
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
+import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterQsActivity
import com.android.safetycenter.testing.SafetyCenterActivityLauncher.openPageAndExit
import com.android.safetycenter.testing.SafetyCenterFlags
import com.android.safetycenter.testing.SafetyCenterTestConfigs
+import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
+import com.android.safetycenter.testing.SafetySourceTestData
+import com.android.safetycenter.testing.SafetySourceTestData.Companion.INFORMATION_ISSUE_ID
+import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed
import org.junit.After
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,16 +56,21 @@ class SafetyCenterInteractionLoggingHelperTests {
private val context: Context = ApplicationProvider.getApplicationContext()
private val safetyCenterTestHelper = SafetyCenterTestHelper(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
+ private val safetySourceTestData = SafetySourceTestData(context)
+
+ @get:Rule val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
@Before
fun setUp() {
- safetyCenterTestHelper.setup()
SafetyCenterFlags.showSubpages = true
}
@After
fun tearDown() {
- safetyCenterTestHelper.reset()
+ // When an assertion fails, it will end up leaving the previous view open, which screws
+ // with the logging assertions made by this test (polluting with view events from whatever
+ // view was left open). Here we preemptively clear whatever's open to get back to home
+ UiAutomatorUtils2.getUiDevice().pressHome()
}
@Test
@@ -64,6 +79,34 @@ class SafetyCenterInteractionLoggingHelperTests {
}
@Test
+ fun openSafetyCenterFullFromQs() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue)
+
+ context.launchSafetyCenterQsActivity {
+ openPageAndExit("Settings") { waitAllTextDisplayed(safetySourceTestData.informationIssue.title) }
+ }
+ }
+
+ @Test
+ fun openSafetyCenterWithIssueIntent() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+
+ safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue)
+
+ val extras = Bundle()
+ extras.putString(EXTRA_SAFETY_SOURCE_ID, SINGLE_SOURCE_ID)
+ extras.putString(EXTRA_SAFETY_SOURCE_ISSUE_ID, INFORMATION_ISSUE_ID)
+
+ context.launchSafetyCenterActivity(extras) {}
+ }
+
+ @Test
+ fun openSafetyCenterQs() {
+ context.launchSafetyCenterQsActivity {}
+ }
+
+ @Test
fun openSubpageFromIntentExtra() {
val config = safetyCenterTestConfigs.singleSourceConfig
safetyCenterTestHelper.setConfig(config)
diff --git a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterNotificationLoggingHelperTests.kt b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterNotificationLoggingHelperTests.kt
index f4677cfed..60e6e41ec 100644
--- a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterNotificationLoggingHelperTests.kt
+++ b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterNotificationLoggingHelperTests.kt
@@ -21,13 +21,19 @@ import android.safetycenter.SafetySourceData
import android.safetycenter.SafetySourceIssue
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.safetycenter.pendingintents.PendingIntentSender
+import com.android.safetycenter.testing.NotificationCharacteristics
+import com.android.safetycenter.testing.SafetyCenterActivityLauncher
import com.android.safetycenter.testing.SafetyCenterFlags
import com.android.safetycenter.testing.SafetyCenterTestConfigs
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID
import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceTestData
-import org.junit.After
+import com.android.safetycenter.testing.StatusBarNotificationWithChannel
+import com.android.safetycenter.testing.TestNotificationListener
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,23 +54,35 @@ class SafetyCenterNotificationLoggingHelperTests {
private val safetySourceTestData = SafetySourceTestData(context)
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
+ @get:Rule
+ val safetyCenterTestRule =
+ SafetyCenterTestRule(safetyCenterTestHelper, withNotifications = true)
+
@Before
fun setUp() {
- safetyCenterTestHelper.setup()
SafetyCenterFlags.notificationsEnabled = true
SafetyCenterFlags.notificationsAllowedSources = setOf(SINGLE_SOURCE_ID)
SafetyCenterFlags.allowStatsdLogging = true
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
}
- @After
- fun tearDown() {
- safetyCenterTestHelper.reset()
+ @Test
+ fun sendNotification() {
+ safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, newTestDataWithNotifiableIssue())
}
@Test
- fun sendNotification() {
+ fun openSafetyCenterFromNotification() {
safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, newTestDataWithNotifiableIssue())
+
+ sendContentPendingIntent(
+ TestNotificationListener.waitForSingleNotificationMatching(
+ NotificationCharacteristics(
+ actions = listOf("See issue"),
+ safetySourceId = SINGLE_SOURCE_ID,
+ )
+ )
+ )
}
private fun newTestDataWithNotifiableIssue(): SafetySourceData =
@@ -77,4 +95,17 @@ class SafetyCenterNotificationLoggingHelperTests {
.build()
)
.build()
+
+ companion object {
+ private fun sendContentPendingIntent(
+ statusBarNotificationWithChannel: StatusBarNotificationWithChannel
+ ) {
+ val contentIntent =
+ statusBarNotificationWithChannel.statusBarNotification.notification.contentIntent
+ SafetyCenterActivityLauncher.executeBlockAndExit(
+ launchActivity = { PendingIntentSender.send(contentIntent) },
+ block = {} // No action required
+ )
+ }
+ }
}
diff --git a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt
index d0e6dd430..4d945aad7 100644
--- a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt
+++ b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt
@@ -23,16 +23,27 @@ import android.safetycenter.SafetySourceErrorDetails
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compatibility.common.util.SystemUtil
-import com.android.safetycenter.testing.*
+import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.reportSafetySourceErrorWithPermission
+import com.android.safetycenter.testing.SafetyCenterFlags
+import com.android.safetycenter.testing.SafetyCenterTestConfigs
+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
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_3
+import com.android.safetycenter.testing.SafetyCenterTestData
+import com.android.safetycenter.testing.SafetyCenterTestHelper
+import com.android.safetycenter.testing.SafetyCenterTestRule
import com.android.safetycenter.testing.SafetySourceIntentHandler.Request
import com.android.safetycenter.testing.SafetySourceIntentHandler.Response
+import com.android.safetycenter.testing.SafetySourceReceiver
+import com.android.safetycenter.testing.SafetySourceReceiver.Companion.executeSafetyCenterIssueActionWithPermissionAndWait
import com.android.safetycenter.testing.SafetySourceReceiver.Companion.refreshSafetySourcesWithReceiverPermissionAndWait
-import org.junit.After
+import com.android.safetycenter.testing.SafetySourceTestData
+import com.android.safetycenter.testing.SafetySourceTestData.Companion.CRITICAL_ISSUE_ACTION_ID
+import com.android.safetycenter.testing.SafetySourceTestData.Companion.CRITICAL_ISSUE_ID
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,18 +55,14 @@ class SafetySourceStateCollectedLoggingHelperTests {
private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context)
private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
+ @get:Rule val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
+
@Before
fun setUp() {
- safetyCenterTestHelper.setup()
SafetyCenterFlags.allowStatsdLogging = true
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig)
}
- @After
- fun tearDown() {
- safetyCenterTestHelper.reset()
- }
-
@Test
fun triggerStatsPull() {
val label = 1 // Arbitrary label in [0, 16)
@@ -124,13 +131,11 @@ class SafetySourceStateCollectedLoggingHelperTests {
@Test
fun refreshAllSources_reasonPageOpen_oneSuccessOneErrorOneTimeout() {
- SafetyCenterFlags.setAllRefreshTimeoutsTo(Coroutines.TIMEOUT_SHORT)
simulateRefresh(Response.SetData(safetySourceTestData.information), Response.Error, null)
}
@Test
fun refreshAllSources_reasonButtonClick_oneSuccessOneErrorOneTimeout() {
- SafetyCenterFlags.setAllRefreshTimeoutsTo(Coroutines.TIMEOUT_SHORT)
simulateRefresh(
Response.SetData(safetySourceTestData.information),
Response.Error,
@@ -139,6 +144,16 @@ class SafetySourceStateCollectedLoggingHelperTests {
)
}
+ @Test
+ fun resolvingAction_success() {
+ simulateResolvingActionWith(Response.SetData(safetySourceTestData.information))
+ }
+
+ @Test
+ fun resolvingAction_error() {
+ simulateResolvingActionWith(Response.Error)
+ }
+
private fun simulateRefresh(
source1Response: Response?,
source2Response: Response?,
@@ -155,8 +170,34 @@ class SafetySourceStateCollectedLoggingHelperTests {
SafetySourceReceiver.setResponse(Request.Refresh(SOURCE_ID_3), source3Response)
}
+ val atLeastOneTimeout =
+ source1Response == null || source2Response == null || source3Response == null
+ if (atLeastOneTimeout) {
+ SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_SHORT)
+ }
+
+ // Refresh sources and wait until the refresh has fully completed / timed out to ensure that
+ // things are logged.
val listener = safetyCenterTestHelper.addListener()
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(refreshReason)
listener.waitForSafetyCenterRefresh()
}
+
+ private fun simulateResolvingActionWith(response: Response) {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig)
+ safetyCenterTestHelper.setData(
+ SINGLE_SOURCE_ID,
+ safetySourceTestData.criticalWithResolvingGeneralIssue
+ )
+ SafetySourceReceiver.setResponse(Request.ResolveAction(SINGLE_SOURCE_ID), response)
+
+ safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
+ SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID),
+ SafetyCenterTestData.issueActionId(
+ SINGLE_SOURCE_ID,
+ CRITICAL_ISSUE_ID,
+ CRITICAL_ISSUE_ACTION_ID
+ )
+ )
+ }
}
diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt
index 5ef8ed84a..42a2a8a89 100644
--- a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt
+++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt
@@ -20,31 +20,32 @@ import android.cts.statsdatom.lib.ConfigUtils
import android.cts.statsdatom.lib.ReportUtils
import android.safetycenter.hostside.rules.HelperAppRule
import android.safetycenter.hostside.rules.RequireSafetyCenterRule
+import com.android.compatibility.common.util.ApiLevelUtil
import com.android.os.AtomsProto.Atom
import com.android.os.AtomsProto.SafetyCenterInteractionReported
import com.android.os.AtomsProto.SafetyCenterInteractionReported.Action
+import com.android.os.AtomsProto.SafetyCenterInteractionReported.NavigationSource
import com.android.os.AtomsProto.SafetyCenterInteractionReported.ViewType
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
import com.google.common.truth.Truth.assertThat
+import java.math.BigInteger
+import java.security.MessageDigest
import org.junit.After
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.rules.RuleChain
import org.junit.runner.RunWith
/** Host-side tests for Safety Center statsd logging. */
@RunWith(DeviceJUnit4ClassRunner::class)
class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() {
- private val safetyCenterRule = RequireSafetyCenterRule(this)
- private val helperAppRule = HelperAppRule(this, HelperApp.APK_NAME, HelperApp.PACKAGE_NAME)
-
- @Rule
- @JvmField
- val rules: RuleChain = RuleChain.outerRule(safetyCenterRule).around(helperAppRule)
+ @get:Rule(order = 1) val safetyCenterRule = RequireSafetyCenterRule(this)
+ @get:Rule(order = 2)
+ val helperAppRule = HelperAppRule(this, HelperApp.APK_NAME, HelperApp.PACKAGE_NAME)
@Before
fun setUp() {
@@ -70,11 +71,75 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() {
val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED)
- assertThat(safetyCenterViewedAtoms).isNotEmpty()
+ assertThat(safetyCenterViewedAtoms).hasSize(1)
+ with(safetyCenterViewedAtoms.first()) {
+ assertThat(navigationSource).isEqualTo(NavigationSource.SOURCE_UNKNOWN)
+ assertThat(viewType).isEqualTo(ViewType.FULL)
+ }
+ }
+
+ @Test
+ fun openSafetyCenterQs_recordsSafetyCenterViewedEvent() {
+ helperAppRule.runTest(TEST_CLASS_NAME, "openSafetyCenterQs")
+
+ val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED)
+
+ assertThat(safetyCenterViewedAtoms).hasSize(1)
+ with(safetyCenterViewedAtoms.first()) {
+ assertThat(navigationSource).isEqualTo(NavigationSource.QUICK_SETTINGS_TILE)
+ assertThat(viewType).isEqualTo(ViewType.QUICK_SETTINGS)
+ }
+ }
+
+ @Ignore // TODO: b/323269529 - Deflake this test
+ @Test
+ fun openSafetyCenterFullFromQs_recordsViewEventWithCorrectSource() {
+ helperAppRule.runTest(TEST_CLASS_NAME, "openSafetyCenterFullFromQs")
+
+ val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED)
+
+ val viewTypesToNavSources =
+ safetyCenterViewedAtoms.associate { Pair(it.viewType, it.navigationSource) }
+ assertThat(viewTypesToNavSources)
+ .containsEntry(ViewType.FULL, NavigationSource.QUICK_SETTINGS_TILE)
+ }
+
+ @Test
+ fun openSafetyCenterWithIssueIntent_recordsViewEventWithAssociatedIssueMetadata() {
+ helperAppRule.runTest(TEST_CLASS_NAME, testMethodName = "openSafetyCenterWithIssueIntent")
+
+ val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED)
+
+ assertThat(safetyCenterViewedAtoms).hasSize(1)
+ with(safetyCenterViewedAtoms.first()) {
+ assertThat(navigationSource).isEqualTo(NavigationSource.NOTIFICATION)
+ assertThat(encodedSafetySourceId).isEqualTo(ENCODED_SINGLE_SOURCE_ID)
+ assertThat(encodedIssueTypeId).isEqualTo(ENCODED_ISSUE_TYPE_ID)
+ }
+ }
+
+ @Test
+ fun openSafetyCenterWithNotification_recordsViewEventWithAssociatedIssueMetadata() {
+ assumeAtLeastUpsideDownCake("Safety Center notification APIs require Android U+")
+
+ helperAppRule.runTest(
+ testClassName = ".SafetyCenterNotificationLoggingHelperTests",
+ testMethodName = "openSafetyCenterFromNotification"
+ )
+
+ val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED)
+
+ assertThat(safetyCenterViewedAtoms).hasSize(1)
+ with(safetyCenterViewedAtoms.first()) {
+ assertThat(navigationSource).isEqualTo(NavigationSource.NOTIFICATION)
+ assertThat(encodedSafetySourceId).isEqualTo(ENCODED_SINGLE_SOURCE_ID)
+ assertThat(encodedIssueTypeId).isEqualTo(ENCODED_ISSUE_TYPE_ID)
+ }
}
@Test
fun sendNotification_recordsNotificationPostedEvent() {
+ assumeAtLeastUpsideDownCake("Safety Center notification APIs require Android U+")
helperAppRule.runTest(
testClassName = ".SafetyCenterNotificationLoggingHelperTests",
testMethodName = "sendNotification"
@@ -89,6 +154,8 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() {
@Test
fun openSubpageFromIntentExtra_recordsEventWithUnknownNavigationSource() {
+ assumeAtLeastUpsideDownCake("Safety Center subpages require Android U+")
+
helperAppRule.runTest(TEST_CLASS_NAME, testMethodName = "openSubpageFromIntentExtra")
val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED)
@@ -96,32 +163,29 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() {
assertThat(safetyCenterViewedAtoms).hasSize(1)
with(safetyCenterViewedAtoms.first()) {
assertThat(viewType).isEqualTo(ViewType.SUBPAGE)
- assertThat(navigationSource)
- .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SOURCE_UNKNOWN)
+ assertThat(navigationSource).isEqualTo(NavigationSource.SOURCE_UNKNOWN)
assertThat(sessionId).isNotNull()
}
}
@Test
- @Ignore
- // TODO(b/278202773): Fix/de-flake this test
fun openSubpageFromHomepage_recordsEventWithSafetyCenterNavigationSource() {
+ assumeAtLeastUpsideDownCake("Safety Center subpages require Android U+")
+
helperAppRule.runTest(TEST_CLASS_NAME, testMethodName = "openSubpageFromHomepage")
val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED)
+ val subpageViewedEvent = safetyCenterViewedAtoms.find { it.viewType == ViewType.SUBPAGE }
- assertThat(safetyCenterViewedAtoms.map { it.viewType })
- .containsExactly(ViewType.FULL, ViewType.SUBPAGE, ViewType.FULL)
- .inOrder()
- assertThat(safetyCenterViewedAtoms[1].navigationSource)
- .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SAFETY_CENTER)
+ assertThat(subpageViewedEvent).isNotNull()
+ assertThat(subpageViewedEvent!!.navigationSource).isEqualTo(NavigationSource.SAFETY_CENTER)
assertThat(safetyCenterViewedAtoms.map { it.sessionId }.distinct()).hasSize(1)
}
@Test
- @Ignore
- // TODO(b/278202773): Fix/de-flake this test
fun openSubpageFromSettingsSearch_recordsEventWithSettingsNavigationSource() {
+ assumeAtLeastUpsideDownCake("Safety Center subpages require Android U+")
+
helperAppRule.runTest(TEST_CLASS_NAME, testMethodName = "openSubpageFromSettingsSearch")
val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED)
@@ -129,8 +193,7 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() {
assertThat(safetyCenterViewedAtoms).hasSize(1)
with(safetyCenterViewedAtoms.first()) {
assertThat(viewType).isEqualTo(ViewType.SUBPAGE)
- assertThat(navigationSource)
- .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SETTINGS)
+ assertThat(navigationSource).isEqualTo(NavigationSource.SETTINGS)
assertThat(sessionId).isNotNull()
}
}
@@ -142,7 +205,33 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() {
.mapNotNull { it.atom.safetyCenterInteractionReported }
.filter { it.action == action }
+ private fun assumeAtLeastUpsideDownCake(message: String) {
+ assumeTrue(message, ApiLevelUtil.isAtLeast(device, 34))
+ }
+
private companion object {
const val TEST_CLASS_NAME = ".SafetyCenterInteractionLoggingHelperTests"
+
+ // LINT.IfChange(single_source_id)
+ val ENCODED_SINGLE_SOURCE_ID = encodeId("test_single_source_id")
+ // LINT.ThenChange(/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt:issue_type_id)
+
+ // LINT.IfChange(issue_type_id)
+ val ENCODED_ISSUE_TYPE_ID = encodeId("issue_type_id")
+ // LINT.ThenChange(/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt:issue_type_id)
+
+ /**
+ * Encodes a string into an long ID. The ID is a SHA-256 of the string, truncated to 64
+ * bits.
+ */
+ fun encodeId(id: String?): Long {
+ if (id == null) return 0
+
+ val digest = MessageDigest.getInstance("MD5")
+ digest.update(id.toByteArray())
+
+ // Truncate to the size of a long
+ return BigInteger(digest.digest()).toLong()
+ }
}
}
diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterSystemEventReportedLoggingHostTest.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterSystemEventReportedLoggingHostTest.kt
index 65e47fa91..2589c7a47 100644
--- a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterSystemEventReportedLoggingHostTest.kt
+++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterSystemEventReportedLoggingHostTest.kt
@@ -31,18 +31,14 @@ import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
-import org.junit.rules.RuleChain
import org.junit.runner.RunWith
@RunWith(DeviceJUnit4ClassRunner::class)
class SafetyCenterSystemEventReportedLoggingHostTest : BaseHostJUnit4Test() {
- private val safetyCenterRule = RequireSafetyCenterRule(this)
- private val helperAppRule = HelperAppRule(this, HelperApp.APK_NAME, HelperApp.PACKAGE_NAME)
-
- @Rule
- @JvmField
- val rules: RuleChain = RuleChain.outerRule(safetyCenterRule).around(helperAppRule)
+ @get:Rule(order = 1) val safetyCenterRule = RequireSafetyCenterRule(this)
+ @get:Rule(order = 2)
+ val helperAppRule = HelperAppRule(this, HelperApp.APK_NAME, HelperApp.PACKAGE_NAME)
@Before
fun setUp() {
@@ -143,6 +139,7 @@ class SafetyCenterSystemEventReportedLoggingHostTest : BaseHostJUnit4Test() {
.that(systemEventAtoms.count { it.refreshReason == REFRESH_REASON_BUTTON_CLICK })
}
+ @Test
fun refreshAllSources_firstTime_allSourcesSuccessful_dataChangedTrueForAll() {
helperAppRule.runTest(
".SafetySourceStateCollectedLoggingHelperTests",
@@ -183,18 +180,50 @@ class SafetyCenterSystemEventReportedLoggingHostTest : BaseHostJUnit4Test() {
@Test
fun refreshAllSources_secondTime_someSourcesChanged_dataChangedCorrect() {
helperAppRule.runTest(
- ".SafetySourceStateCollectedLoggingHelperTests",
- "refreshAllSources_twiceDifferentData_onlySource1Unchanged"
+ ".SafetySourceStateCollectedLoggingHelperTests",
+ "refreshAllSources_twiceDifferentData_onlySource1Unchanged"
)
val systemEventAtoms =
- ReportUtils.getEventMetricDataList(device).mapNotNull {
- it.atom.safetyCenterSystemEventReported
- }
+ ReportUtils.getEventMetricDataList(device).mapNotNull {
+ it.atom.safetyCenterSystemEventReported
+ }
assertWithMessage("the number of atoms with dataChanged=false")
- .that(systemEventAtoms.count { !it.dataChanged })
- .isEqualTo(1) // Only source 1
+ .that(systemEventAtoms.count { !it.dataChanged })
+ .isEqualTo(1) // Only source 1
+ }
+
+ @Test
+ fun resolveAction_success_resolvingActionSuccessEvent() {
+ helperAppRule.runTest(
+ ".SafetySourceStateCollectedLoggingHelperTests",
+ "resolvingAction_success"
+ )
+
+ val resolvingActionEvent =
+ ReportUtils.getEventMetricDataList(device)
+ .mapNotNull { it.atom.safetyCenterSystemEventReported }
+ .single { it.eventType == EventType.INLINE_ACTION }
+
+ assertThat(resolvingActionEvent.result).isEqualTo(Result.SUCCESS)
+ assertThat(resolvingActionEvent.encodedIssueTypeId).isNotEqualTo(0)
+ }
+
+ @Test
+ fun resolveAction_error_resolvingActionErrorEvent() {
+ helperAppRule.runTest(
+ ".SafetySourceStateCollectedLoggingHelperTests",
+ "resolvingAction_error"
+ )
+
+ val resolvingActionEvent =
+ ReportUtils.getEventMetricDataList(device)
+ .mapNotNull { it.atom.safetyCenterSystemEventReported }
+ .single { it.eventType == EventType.INLINE_ACTION }
+
+ assertThat(resolvingActionEvent.result).isEqualTo(Result.ERROR)
+ assertThat(resolvingActionEvent.encodedIssueTypeId).isNotEqualTo(0)
}
companion object {
diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetySourceStateCollectedLoggingHostTest.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetySourceStateCollectedLoggingHostTest.kt
index 22a380e12..a86041b5b 100644
--- a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetySourceStateCollectedLoggingHostTest.kt
+++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetySourceStateCollectedLoggingHostTest.kt
@@ -29,19 +29,15 @@ import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
-import org.junit.rules.RuleChain
import org.junit.runner.RunWith
/** Host-side tests for Safety Center statsd logging. */
@RunWith(DeviceJUnit4ClassRunner::class)
class SafetySourceStateCollectedLoggingHostTest : BaseHostJUnit4Test() {
- private val safetyCenterRule = RequireSafetyCenterRule(this)
- private val helperAppRule = HelperAppRule(this, HelperApp.APK_NAME, HelperApp.PACKAGE_NAME)
-
- @Rule
- @JvmField
- val rules: RuleChain = RuleChain.outerRule(safetyCenterRule).around(helperAppRule)
+ @get:Rule(order = 1) val safetyCenterRule = RequireSafetyCenterRule(this)
+ @get:Rule(order = 2)
+ val helperAppRule = HelperAppRule(this, HelperApp.APK_NAME, HelperApp.PACKAGE_NAME)
@Before
fun setUp() {
@@ -66,8 +62,10 @@ class SafetySourceStateCollectedLoggingHostTest : BaseHostJUnit4Test() {
val sourceStateAtoms = getSafetySourceStateCollectedAtoms()
+ // This assertion purposefully uses containsAtLeast and not containsExact because on test
+ // devices with multiple primary users there will be multiple atoms per source.
assertThat(sourceStateAtoms.map { it.encodedSafetySourceId })
- .containsExactly(
+ .containsAtLeast(
SOURCE_1_ENCODED_SOURCE_ID,
SOURCE_2_ENCODED_SOURCE_ID,
SOURCE_3_ENCODED_SOURCE_ID
diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/rules/RequireSafetyCenterRule.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/rules/RequireSafetyCenterRule.kt
index 809fe5f0f..fe75a05a2 100644
--- a/tests/hostside/safetycenter/src/android/safetycenter/hostside/rules/RequireSafetyCenterRule.kt
+++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/rules/RequireSafetyCenterRule.kt
@@ -24,14 +24,23 @@ import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
+/** toBooleanString() doesn't seem available on all Kotlin versions we need to support. */
+private fun String.toBooleanStrictInt(): Boolean =
+ when (this) {
+ "true" -> true
+ "false" -> false
+ else ->
+ throw IllegalArgumentException("The string doesn't represent a boolean value: $this")
+ }
+
/** JUnit rule for host side tests that requires Safety Center to be supported and enabled. */
class RequireSafetyCenterRule(private val hostTestClass: BaseHostJUnit4Test) : TestRule {
private val safetyCenterSupported: Boolean by lazy {
- executeShellCommandOrThrow("cmd safety_center supported").toBoolean()
+ shellCommandStdoutOrThrow("cmd safety_center supported").toBooleanStrictInt()
}
private val safetyCenterEnabled: Boolean by lazy {
- executeShellCommandOrThrow("cmd safety_center enabled").toBoolean()
+ shellCommandStdoutOrThrow("cmd safety_center enabled").toBooleanStrictInt()
}
override fun apply(base: Statement, description: Description): Statement {
@@ -45,13 +54,20 @@ class RequireSafetyCenterRule(private val hostTestClass: BaseHostJUnit4Test) : T
}
/** Returns the package name of Safety Center on the test device. */
- fun getSafetyCenterPackageName(): String =
- executeShellCommandOrThrow("cmd safety_center package-name")
+ fun getSafetyCenterPackageName(): String {
+ return shellCommandStdoutOrThrow("cmd safety_center package-name")
+ }
- private fun executeShellCommandOrThrow(command: String): String {
+ private fun shellCommandStdoutOrThrow(command: String): String {
val result = hostTestClass.device.executeShellV2Command(command)
if (result.status != CommandStatus.SUCCESS) {
- throw IOException("$command exited with status ${result.exitCode}")
+ throw IOException(
+ """Host-side test failed to execute adb shell command on test device.
+ |Command '$command' exited with status code ${result.exitCode}.
+ |This probably means the test device does not have a compatible version of
+ |the Permission Mainline module. Please check the test configuration."""
+ .trimMargin("|")
+ )
}
return result.stdout.trim()
}
diff --git a/tests/utils/safetycenter/AndroidManifest.xml b/tests/utils/safetycenter/AndroidManifest.xml
index 9d7c52e62..ce3724318 100644
--- a/tests/utils/safetycenter/AndroidManifest.xml
+++ b/tests/utils/safetycenter/AndroidManifest.xml
@@ -40,11 +40,25 @@
<activity android:name=".TestActivity"
android:exported="false">
- <intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="com.android.safetycenter.testing.action.TEST_ACTIVITY"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <!-- Create an alias at higher priority, disabled. We have seen flakes where implicit
+ intents for TEST_ACTIVITY fail owing to multiple receivers, perhaps due to an older
+ CTS APK hanging around. We turn this component on (and off in tidyup) in tests, in
+ the hope of only resolving to the actively running test in these cases. -->
+ <activity-alias android:name=".TestActivityPriority"
+ android:targetActivity=".TestActivity"
+ android:enabled="false"
+ android:exported="false">
+ <intent-filter android:priority="0">
<action android:name="com.android.safetycenter.testing.action.TEST_ACTIVITY"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
- </activity>
+ </activity-alias>
<activity-alias android:name=".TestActivityExported"
android:targetActivity=".TestActivity"
@@ -54,5 +68,14 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity-alias>
+
+ <service android:name=".TestNotificationListener"
+ android:label="TestNotificationListener"
+ android:exported="false"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService" />
+ </intent-filter>
+ </service>
</application>
</manifest>
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 29d1c1f09..a7009b19e 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt
@@ -29,6 +29,7 @@ import kotlinx.coroutines.withTimeoutOrNull
/** A class that facilitates interacting with coroutines. */
object Coroutines {
+
/**
* The timeout of a test case, typically varies depending on whether the test is running
* locally, on pre-submit or post-submit.
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt
new file mode 100644
index 000000000..1ed0ecbc3
--- /dev/null
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.safetycenter.testing
+
+import android.Manifest.permission.MANAGE_SENSOR_PRIVACY
+import android.Manifest.permission.OBSERVE_SENSOR_PRIVACY
+import android.content.Context
+import android.hardware.SensorPrivacyManager
+import android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE
+import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
+import org.junit.Assume.assumeTrue
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A JUnit [TestRule] to ensure a given [sensor] is enabled.
+ *
+ * This rule disables sensor privacy before a test and restores the prior state afterwards.
+ */
+class EnableSensorRule(context: Context, val sensor: Int) : TestRule {
+
+ private val sensorPrivacyManager: SensorPrivacyManager =
+ context.getSystemService(SensorPrivacyManager::class.java)!!
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return object : Statement() {
+ override fun evaluate() {
+ assumeTrue(
+ "Test device does not support toggling sensor $sensor",
+ supportsSensorToggle()
+ )
+ val oldSensorPrivacy = isSensorPrivacyEnabled()
+ setSensorPrivacy(false)
+ try {
+ base.evaluate()
+ } finally {
+ setSensorPrivacy(oldSensorPrivacy)
+ }
+ }
+ }
+ }
+
+ private fun supportsSensorToggle(): Boolean =
+ sensorPrivacyManager.supportsSensorToggle(sensor) &&
+ sensorPrivacyManager.supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor)
+
+ private fun isSensorPrivacyEnabled(): Boolean =
+ callWithShellPermissionIdentity(OBSERVE_SENSOR_PRIVACY) {
+ sensorPrivacyManager.isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor)
+ }
+
+ private fun setSensorPrivacy(enabled: Boolean) {
+ callWithShellPermissionIdentity(MANAGE_SENSOR_PRIVACY, OBSERVE_SENSOR_PRIVACY) {
+ sensorPrivacyManager.setSensorPrivacy(sensor, enabled)
+ }
+ }
+}
diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/NotificationCharacteristics.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt
index b91c72422..81b752bca 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/NotificationCharacteristics.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,21 +14,28 @@
* limitations under the License.
*/
-package android.safetycenter.functional.testing
+package com.android.safetycenter.testing
import android.app.Notification
+import android.service.notification.StatusBarNotification
+import com.android.safetycenter.internaldata.SafetyCenterIds
/** The characteristic properties of a notification. */
data class NotificationCharacteristics(
- val title: String,
- val text: String,
+ val title: String? = null,
+ val text: String? = null,
val actions: List<CharSequence> = emptyList(),
val importance: Int = IMPORTANCE_ANY,
- val blockable: Boolean? = null
+ val blockable: Boolean? = null,
+ val safetySourceId: String? = null,
) {
companion object {
const val IMPORTANCE_ANY = -1
+ private fun stringMatches(actual: String?, expected: String?): Boolean {
+ return expected == null || actual == expected
+ }
+
private fun importanceMatches(
statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
characteristicImportance: Int
@@ -45,17 +52,31 @@ data class NotificationCharacteristics(
statusBarNotificationWithChannel.channel.isBlockable == characteristicBlockable
}
+ fun safetySourceIdMatches(
+ statusBarNotification: StatusBarNotification,
+ safetySourceId: String?
+ ): Boolean {
+ return safetySourceId == null ||
+ SafetyCenterIds.issueKeyFromString(statusBarNotification.tag).safetySourceId ==
+ safetySourceId
+ }
+
private fun isMatch(
statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
characteristic: NotificationCharacteristics
): Boolean {
val notif = statusBarNotificationWithChannel.statusBarNotification.notification
+ val extras = notif.extras
return notif != null &&
- notif.extras.getString(Notification.EXTRA_TITLE) == characteristic.title &&
- notif.extras.getString(Notification.EXTRA_TEXT).orEmpty() == characteristic.text &&
+ stringMatches(extras.getString(Notification.EXTRA_TITLE), characteristic.title) &&
+ stringMatches(extras.getString(Notification.EXTRA_TEXT), characteristic.text) &&
notif.actions.orEmpty().map { it.title } == characteristic.actions &&
importanceMatches(statusBarNotificationWithChannel, characteristic.importance) &&
- blockableMatches(statusBarNotificationWithChannel, characteristic.blockable)
+ blockableMatches(statusBarNotificationWithChannel, characteristic.blockable) &&
+ safetySourceIdMatches(
+ statusBarNotificationWithChannel.statusBarNotification,
+ characteristic.safetySourceId
+ )
}
fun areMatching(
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 537eb7ead..40515fa33 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt
@@ -28,7 +28,6 @@ import android.os.Build.VERSION_CODES.TIRAMISU
import android.os.Bundle
import androidx.annotation.RequiresApi
import androidx.test.uiautomator.By
-import com.android.compatibility.common.util.RetryableException
import com.android.compatibility.common.util.UiAutomatorUtils2.getUiDevice
import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
import com.android.safetycenter.testing.UiTestHelper.waitDisplayed
@@ -46,13 +45,14 @@ object SafetyCenterActivityLauncher {
*/
fun Context.launchSafetyCenterActivity(
intentExtras: Bundle? = null,
+ intentAction: String = ACTION_SAFETY_CENTER,
withReceiverPermission: Boolean = false,
preventTrampolineToSettings: Boolean = true,
block: () -> Unit
) {
val launchSafetyCenterIntent =
createIntent(
- ACTION_SAFETY_CENTER,
+ intentAction,
intentExtras,
preventTrampolineToSettings = preventTrampolineToSettings
)
@@ -80,19 +80,6 @@ object SafetyCenterActivityLauncher {
executeBlockAndExit(block) { waitDisplayed(By.text(entryPoint)) { it.click() } }
}
- /**
- * Launches a page in Safety Center and exits it once [block] completes, throwing a
- * [RetryableException] for any [RuntimeException] thrown by [block] to allow [RetryRule] to
- * retry the test invocation.
- */
- fun openPageAndExitAllowingRetries(entryPoint: String, block: () -> Unit) {
- try {
- openPageAndExit(entryPoint, block)
- } catch (e: Throwable) {
- throw RetryableException(e, "Exception occurred when checking a Safety Center page")
- }
- }
-
private fun createIntent(
intentAction: String,
intentExtras: Bundle?,
@@ -107,6 +94,7 @@ object SafetyCenterActivityLauncher {
return launchIntent
}
+ /** Executes the given [block] and presses the back button to exit. */
fun executeBlockAndExit(block: () -> Unit, launchActivity: () -> Unit) {
val uiDevice = getUiDevice()
uiDevice.waitForIdle()
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 b948dc52c..f8926caac 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt
@@ -24,8 +24,8 @@ import android.content.IntentFilter
import android.os.Build.VERSION_CODES.TIRAMISU
import android.safetycenter.SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED
import androidx.annotation.RequiresApi
-import com.android.compatibility.common.util.SystemUtil
import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG
+import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT
import com.android.safetycenter.testing.Coroutines.runBlockingWithTimeout
import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
import java.time.Duration
@@ -55,20 +55,18 @@ class SafetyCenterEnabledChangedReceiver(private val context: Context) : Broadca
fun setSafetyCenterEnabledWithReceiverPermissionAndWait(
value: Boolean,
timeout: Duration = TIMEOUT_LONG
- ) =
+ ): Boolean =
callWithShellPermissionIdentity(READ_SAFETY_CENTER_STATUS) {
- setSafetyCenterEnabledWithoutReceiverPermissionAndWait(value, timeout)
+ SafetyCenterFlags.isEnabled = value
+ receiveSafetyCenterEnabledChanged(timeout)
}
fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait(
value: Boolean,
- timeout: Duration = TIMEOUT_LONG
- ): Boolean {
+ ) {
SafetyCenterFlags.isEnabled = value
- if (timeout < TIMEOUT_LONG) {
- SystemUtil.waitForBroadcasts()
- }
- return receiveSafetyCenterEnabledChanged(timeout)
+ WaitForBroadcasts.waitForBroadcasts()
+ receiveSafetyCenterEnabledChanged(TIMEOUT_SHORT)
}
fun unregister() {
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 f7b5f486d..912ea44ad 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt
@@ -20,9 +20,7 @@ import android.Manifest.permission.READ_DEVICE_CONFIG
import android.Manifest.permission.WRITE_DEVICE_CONFIG
import android.annotation.TargetApi
import android.app.job.JobInfo
-import android.content.Context
import android.content.pm.PackageManager
-import android.content.res.Resources
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_PRIVACY
@@ -35,6 +33,7 @@ import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC
import android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK
import android.safetycenter.SafetyCenterManager.REFRESH_REASON_SAFETY_CENTER_ENABLED
import android.safetycenter.SafetySourceData
+import com.android.modules.utils.build.SdkLevel
import com.android.safetycenter.testing.Coroutines.TEST_TIMEOUT
import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG
import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity
@@ -46,7 +45,7 @@ object SafetyCenterFlags {
/** Flag that determines whether Safety Center is enabled. */
private val isEnabledFlag =
- Flag("safety_center_is_enabled", defaultValue = false, BooleanParser())
+ Flag("safety_center_is_enabled", defaultValue = SdkLevel.isAtLeastU(), BooleanParser())
/** Flag that determines whether Safety Center can send notifications. */
private val notificationsFlag =
@@ -102,13 +101,6 @@ object SafetyCenterFlags {
DurationParser()
)
- /**
- * Flag that determines whether we should show error entries for sources that timeout when
- * refreshing them.
- */
- private val showErrorEntriesOnTimeoutFlag =
- Flag("safety_center_show_error_entries_on_timeout", defaultValue = true, BooleanParser())
-
/** Flag that determines whether we should replace the IconAction of the lock screen source. */
private val replaceLockScreenIconActionFlag =
Flag("safety_center_replace_lock_screen_icon_action", defaultValue = true, BooleanParser())
@@ -211,6 +203,18 @@ object SafetyCenterFlags {
)
/**
+ * Flag containing a map (a comma separated list of colon separated pairs) where the key is a
+ * Safety Source ID and the value is a vertical-bar-delimited list of Action IDs that should
+ * have their PendingIntent replaced with the source's default PendingIntent.
+ */
+ private val actionsToOverrideWithDefaultIntentFlag =
+ Flag(
+ "safety_center_actions_to_override_with_default_intent",
+ defaultValue = emptyMap(),
+ MapParser(StringParser(), SetParser(StringParser(), delimiter = "|"))
+ )
+
+ /**
* Flag that represents a comma delimited list of IDs of sources that should only be refreshed
* when Safety Center is on screen. We will refresh these sources only on page open and when the
* scan button is clicked.
@@ -293,13 +297,6 @@ object SafetyCenterFlags {
MapParser(StringParser(), SetParser(StringParser(), delimiter = "|"))
)
- /**
- * Flag that determines whether background refreshes require charging in
- * [SafetyCenterBackgroundRefreshJobService]. See [JobInfo.setRequiresCharging] for details.
- */
- private val backgroundRefreshRequiresChargingFlag =
- Flag("safety_center_background_requires_charging", defaultValue = false, BooleanParser())
-
/** Every Safety Center flag. */
private val FLAGS: List<Flag<*>> =
listOf(
@@ -309,7 +306,6 @@ object SafetyCenterFlags {
notificationsMinDelayFlag,
immediateNotificationBehaviorIssuesFlag,
notificationResurfaceIntervalFlag,
- showErrorEntriesOnTimeoutFlag,
replaceLockScreenIconActionFlag,
refreshSourceTimeoutsFlag,
resolveActionTimeoutFlag,
@@ -319,6 +315,7 @@ object SafetyCenterFlags {
resurfaceIssueMaxCountsFlag,
resurfaceIssueDelaysFlag,
issueCategoryAllowlistsFlag,
+ actionsToOverrideWithDefaultIntentFlag,
allowedAdditionalPackageCertsFlag,
backgroundRefreshDeniedSourcesFlag,
allowStatsdLoggingFlag,
@@ -326,14 +323,7 @@ object SafetyCenterFlags {
showSubpagesFlag,
overrideRefreshOnPageOpenSourcesFlag,
backgroundRefreshIsEnabledFlag,
- periodicBackgroundRefreshIntervalFlag,
- backgroundRefreshRequiresChargingFlag
- )
-
- /** Returns whether the device supports Safety Center. */
- fun Context.deviceSupportsSafetyCenter() =
- resources.getBoolean(
- Resources.getSystem().getIdentifier("config_enableSafetyCenter", "bool", "android")
+ periodicBackgroundRefreshIntervalFlag
)
/** A property that allows getting and setting the [isEnabledFlag]. */
@@ -354,9 +344,6 @@ object SafetyCenterFlags {
/** A property that allows getting and setting the [notificationResurfaceIntervalFlag]. */
var notificationResurfaceInterval: Duration by notificationResurfaceIntervalFlag
- /** A property that allows getting and setting the [showErrorEntriesOnTimeoutFlag]. */
- var showErrorEntriesOnTimeout: Boolean by showErrorEntriesOnTimeoutFlag
-
/** A property that allows getting and setting the [replaceLockScreenIconActionFlag]. */
var replaceLockScreenIconAction: Boolean by replaceLockScreenIconActionFlag
@@ -384,6 +371,10 @@ object SafetyCenterFlags {
/** A property that allows getting and setting the [issueCategoryAllowlistsFlag]. */
var issueCategoryAllowlists: Map<Int, Set<String>> by issueCategoryAllowlistsFlag
+ /** A property that allows getting and setting the [actionsToOverrideWithDefaultIntentFlag]. */
+ var actionsToOverrideWithDefaultIntent: Map<String, Set<String>> by
+ actionsToOverrideWithDefaultIntentFlag
+
var allowedAdditionalPackageCerts: Map<String, Set<String>> by allowedAdditionalPackageCertsFlag
/** A property that allows getting and setting the [backgroundRefreshDeniedSourcesFlag]. */
@@ -398,15 +389,6 @@ object SafetyCenterFlags {
/** A property that allows getting and setting the [overrideRefreshOnPageOpenSourcesFlag]. */
var overrideRefreshOnPageOpenSources: Set<String> by overrideRefreshOnPageOpenSourcesFlag
- /** A property that allows getting and settings the [backgroundRefreshIsEnabledFlag]. */
- var backgroundRefreshIsEnabled: Boolean by backgroundRefreshIsEnabledFlag
-
- /** A property that allows getting and settings the [periodicBackgroundRefreshIntervalFlag]. */
- var periodicBackgroundRefreshInterval: Duration by periodicBackgroundRefreshIntervalFlag
-
- /** A property that allows getting and settings the [backgroundRefreshRequiresChargingFlag]. */
- var backgroundRefreshRequiresCharging: Boolean by backgroundRefreshRequiresChargingFlag
-
/**
* Returns a snapshot of all the Safety Center flags.
*
@@ -460,7 +442,7 @@ object SafetyCenterFlags {
/** Returns the [isEnabledFlag] value of the Safety Center flags snapshot. */
fun Properties.isSafetyCenterEnabled() =
- getBoolean(isEnabledFlag.name, /* defaultValue */ false)
+ getBoolean(isEnabledFlag.name, isEnabledFlag.defaultValue)
@TargetApi(UPSIDE_DOWN_CAKE)
private fun getAllRefreshTimeoutsMap(refreshTimeout: Duration): Map<Int, Duration> =
@@ -533,11 +515,7 @@ object SafetyCenterFlags {
.joinToString(entriesDelimiter)
}
- private class Flag<T>(
- val name: String,
- private val defaultValue: T,
- private val parser: Parser<T>
- ) {
+ private class Flag<T>(val name: String, val defaultValue: T, private val parser: Parser<T>) {
val defaultStringValue = parser.toString(defaultValue)
operator fun getValue(thisRef: Any?, property: KProperty<*>): T =
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 de4cf7094..b0d209fcc 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt
@@ -64,7 +64,9 @@ class SafetyCenterTestConfigs(private val context: Context) {
*/
val singleSourceInvalidIntentConfig =
singleSourceConfig(
- dynamicSafetySourceBuilder(SINGLE_SOURCE_ID).setIntentAction("stub").build()
+ dynamicSafetySourceBuilder(SINGLE_SOURCE_ID)
+ .setIntentAction(INTENT_ACTION_NOT_RESOLVING)
+ .build()
)
/**
@@ -157,6 +159,14 @@ class SafetyCenterTestConfigs(private val context: Context) {
/** A [SafetyCenterConfig] with a dynamic source in a different, missing package. */
val singleSourceOtherPackageConfig = singleSourceConfig(dynamicOtherPackageSafetySource)
+ /** A [SafetyCenterConfig] with a dynamic hidden-by-default source. */
+ val hiddenSourceConfig =
+ singleSourceConfig(
+ dynamicSafetySourceBuilder(DYNAMIC_HIDDEN_ID)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
+ .build()
+ )
+
/** A simple [SafetyCenterConfig] with a source supporting all profiles. */
val singleSourceAllProfileConfig =
singleSourceConfig(
@@ -348,7 +358,9 @@ class SafetyCenterTestConfigs(private val context: Context) {
.addSafetySourcesGroup(
safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_1)
.addSafetySource(
- dynamicSafetySourceBuilder(SOURCE_ID_1).setIntentAction("stub").build()
+ dynamicSafetySourceBuilder(SOURCE_ID_1)
+ .setIntentAction(INTENT_ACTION_NOT_RESOLVING)
+ .build()
)
.addSafetySource(dynamicSafetySource(SOURCE_ID_2))
.build()
@@ -373,9 +385,7 @@ class SafetyCenterTestConfigs(private val context: Context) {
* Source group provided by [staticSourcesConfig] containing a single source [staticSource1].
*/
val staticSourceGroup1 =
- SafetySourcesGroup.Builder()
- .setId("test_static_sources_group_id_1")
- .setTitleResId(android.R.string.paste)
+ staticSafetySourcesGroupBuilder("test_static_sources_group_id_1")
.addSafetySource(staticSource1)
.build()
@@ -383,8 +393,7 @@ class SafetyCenterTestConfigs(private val context: Context) {
* Source group provided by [staticSourcesConfig] containing a single source [staticSource2].
*/
val staticSourceGroup2 =
- SafetySourcesGroup.Builder()
- .setId("test_static_sources_group_id_2")
+ staticSafetySourcesGroupBuilder("test_static_sources_group_id_2")
.setTitleResId(android.R.string.copy)
.addSafetySource(staticSource2)
.build()
@@ -402,7 +411,44 @@ class SafetyCenterTestConfigs(private val context: Context) {
* The particular source ID is configured in the same way as sources hosted by the Settings app,
* to launch as if it is part of the Settings app UI.
*/
- val singleStaticSettingsSource = singleSourceConfig(staticSafetySource("TestSource"))
+ val singleStaticSettingsSourceConfig =
+ SafetyCenterConfig.Builder()
+ .addSafetySourcesGroup(
+ staticSafetySourcesGroupBuilder("single_static_source_group")
+ .addSafetySource(staticSafetySource("TestSource"))
+ .build()
+ )
+ .build()
+
+ /** A [SafetyCenterConfig] with a single static source and an intent that doesn't resolve */
+ val singleStaticInvalidIntentConfig =
+ SafetyCenterConfig.Builder()
+ .addSafetySourcesGroup(
+ staticSafetySourcesGroupBuilder("single_static_source_group")
+ .addSafetySource(
+ staticSafetySourceBuilder(SINGLE_SOURCE_ID)
+ .setIntentAction(INTENT_ACTION_NOT_RESOLVING)
+ .build()
+ )
+ .build()
+ )
+ .build()
+
+ /**
+ * A [SafetyCenterConfig] with a single static source and an implicit intent that isn't exported
+ */
+ val singleStaticImplicitIntentNotExportedConfig =
+ SafetyCenterConfig.Builder()
+ .addSafetySourcesGroup(
+ staticSafetySourcesGroupBuilder("single_static_source_group")
+ .addSafetySource(
+ staticSafetySourceBuilder(SINGLE_SOURCE_ID)
+ .setIntentAction(ACTION_TEST_ACTIVITY)
+ .build()
+ )
+ .build()
+ )
+ .build()
/** [SafetyCenterConfig] used in tests for Your Work Policy Info source. */
val workPolicyInfoConfig =
@@ -750,7 +796,7 @@ class SafetyCenterTestConfigs(private val context: Context) {
.setId(id)
.setTitleResId(android.R.string.ok)
.setSummaryResId(android.R.string.ok)
- .setIntentAction(ACTION_TEST_ACTIVITY)
+ .setIntentAction(ACTION_TEST_ACTIVITY_EXPORTED)
.setProfile(SafetySource.PROFILE_PRIMARY)
private fun staticAllProfileSafetySourceBuilder(id: String) =
@@ -780,6 +826,9 @@ class SafetyCenterTestConfigs(private val context: Context) {
.setTitleResId(android.R.string.ok)
.setSummaryResId(android.R.string.ok)
+ private fun staticSafetySourcesGroupBuilder(id: String) =
+ SafetySourcesGroup.Builder().setId(id).setTitleResId(android.R.string.paste)
+
fun singleSourceConfig(safetySource: SafetySource) =
SafetyCenterConfig.Builder()
.addSafetySourcesGroup(
@@ -807,7 +856,9 @@ class SafetyCenterTestConfigs(private val context: Context) {
* ID of the only source provided in [singleSourceConfig], [severityZeroConfig] and
* [noPageOpenConfig].
*/
+ // LINT.IfChange(single_source_id)
const val SINGLE_SOURCE_ID = "test_single_source_id"
+ // LINT.ThenChange(/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt:single_source_id)
/** ID of the only source provided in [singleSourceAllProfileConfig]. */
const val SINGLE_SOURCE_ALL_PROFILE_ID = "test_single_source_all_profile_id"
@@ -1033,5 +1084,7 @@ class SafetyCenterTestConfigs(private val context: Context) {
* [privacySubpageWithoutDataSourcesConfig], to replicate the privacy sources group.
*/
const val ANDROID_PRIVACY_SOURCES_GROUP_ID = "AndroidPrivacySources"
+
+ private const val INTENT_ACTION_NOT_RESOLVING = "there.is.no.way.this.resolves"
}
}
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 5ff42e23c..289bc32a8 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt
@@ -48,7 +48,7 @@ import com.android.safetycenter.internaldata.SafetyCenterIds
import com.android.safetycenter.internaldata.SafetyCenterIssueActionId
import com.android.safetycenter.internaldata.SafetyCenterIssueId
import com.android.safetycenter.internaldata.SafetyCenterIssueKey
-import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.android.safetycenter.resources.SafetyCenterResourcesApk
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_GROUP_ID
import com.android.safetycenter.testing.SafetySourceTestData.Companion.CRITICAL_ISSUE_ACTION_ID
import com.android.safetycenter.testing.SafetySourceTestData.Companion.CRITICAL_ISSUE_ID
@@ -66,23 +66,24 @@ import java.util.Locale
@RequiresApi(TIRAMISU)
class SafetyCenterTestData(context: Context) {
- private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context)
+ private val safetyCenterResourcesApk = SafetyCenterResourcesApk.forTests(context)
private val safetySourceTestData = SafetySourceTestData(context)
/**
* The [SafetyCenterStatus] used when the overall status is unknown and no scan is in progress.
*/
- val safetyCenterStatusUnknown =
- SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_ok_review_title"
- ),
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_ok_review_summary"
+ val safetyCenterStatusUnknown: SafetyCenterStatus
+ get() =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_ok_review_title"
+ ),
+ safetyCenterResourcesApk.getStringByName(
+ "overall_severity_level_ok_review_summary"
+ )
)
- )
- .setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN)
- .build()
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN)
+ .build()
/**
* Returns a [SafetyCenterStatus] with one alert and the given [statusResource] and
@@ -103,7 +104,7 @@ class SafetyCenterTestData(context: Context) {
numAlerts: Int,
): SafetyCenterStatus =
SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(statusResource),
+ safetyCenterResourcesApk.getStringByName(statusResource),
getAlertString(numAlerts)
)
.setSeverityLevel(overallSeverityLevel)
@@ -117,11 +118,8 @@ class SafetyCenterTestData(context: Context) {
numTipIssues: Int,
): SafetyCenterStatus =
SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_tip_summary",
- numTipIssues
- )
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
+ getIcuPluralsString("overall_severity_level_tip_summary", numTipIssues)
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
.build()
@@ -134,8 +132,8 @@ class SafetyCenterTestData(context: Context) {
numAutomaticIssues: Int,
): SafetyCenterStatus =
SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
- safetyCenterResourcesContext.getStringByName(
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
+ getIcuPluralsString(
"overall_severity_level_action_taken_summary",
numAutomaticIssues
)
@@ -149,7 +147,7 @@ class SafetyCenterTestData(context: Context) {
*/
fun safetyCenterStatusCritical(numAlerts: Int) =
SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName(
+ safetyCenterResourcesApk.getStringByName(
"overall_severity_level_critical_safety_warning_title"
),
getAlertString(numAlerts)
@@ -166,7 +164,8 @@ class SafetyCenterTestData(context: Context) {
sourceId: String,
userId: Int = UserHandle.myUserId(),
title: CharSequence = "OK",
- pendingIntent: PendingIntent? = safetySourceTestData.testActivityRedirectPendingIntent
+ pendingIntent: PendingIntent? =
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
) =
SafetyCenterEntry.Builder(entryId(sourceId, userId), title)
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
@@ -183,7 +182,8 @@ class SafetyCenterTestData(context: Context) {
sourceId: String,
userId: Int = UserHandle.myUserId(),
title: CharSequence = "OK",
- pendingIntent: PendingIntent? = safetySourceTestData.testActivityRedirectPendingIntent
+ pendingIntent: PendingIntent? =
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
) = safetyCenterEntryDefaultBuilder(sourceId, userId, title, pendingIntent).build()
/**
@@ -199,7 +199,9 @@ class SafetyCenterTestData(context: Context) {
SafetyCenterEntry.Builder(entryId(sourceId, userId), title)
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
.setSummary("OK")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent(explicit = false)
+ )
.setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON)
/**
@@ -216,7 +218,8 @@ class SafetyCenterTestData(context: Context) {
*/
fun safetyCenterEntryUnspecified(
sourceId: String,
- pendingIntent: PendingIntent? = safetySourceTestData.testActivityRedirectPendingIntent
+ pendingIntent: PendingIntent? =
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
) =
SafetyCenterEntry.Builder(entryId(sourceId), "Unspecified title")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
@@ -239,7 +242,7 @@ class SafetyCenterTestData(context: Context) {
SafetyCenterEntry.Builder(entryId(sourceId, userId), title)
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_OK)
.setSummary("Ok summary")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
+ .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
.setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION)
/**
@@ -264,7 +267,7 @@ class SafetyCenterTestData(context: Context) {
SafetyCenterEntry.Builder(entryId(sourceId), "Recommendation title")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
.setSummary(summary)
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
+ .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
.setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION)
.build()
@@ -276,7 +279,7 @@ class SafetyCenterTestData(context: Context) {
SafetyCenterEntry.Builder(entryId(sourceId), "Critical title")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
.setSummary("Critical summary")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
+ .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
.setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION)
.build()
@@ -307,7 +310,7 @@ class SafetyCenterTestData(context: Context) {
userId
),
"Review",
- safetySourceTestData.testActivityRedirectPendingIntent
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
)
.build()
)
@@ -347,7 +350,7 @@ class SafetyCenterTestData(context: Context) {
userId
),
"See issue",
- safetySourceTestData.testActivityRedirectPendingIntent
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
)
.apply {
if (confirmationDialog && SdkLevel.isAtLeastU()) {
@@ -428,7 +431,7 @@ class SafetyCenterTestData(context: Context) {
private fun getIcuPluralsString(name: String, count: Int, vararg formatArgs: Any): String {
val messageFormat =
MessageFormat(
- safetyCenterResourcesContext.getStringByName(name, formatArgs),
+ safetyCenterResourcesApk.getStringByName(name, formatArgs),
Locale.getDefault()
)
val arguments = ArrayMap<String, Any>()
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 82f7326fd..2902cdd6a 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt
@@ -27,6 +27,7 @@ import android.safetycenter.SafetyEvent
import android.safetycenter.SafetySourceData
import android.safetycenter.config.SafetyCenterConfig
import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_STATIC
+import android.util.Log
import androidx.annotation.RequiresApi
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.addOnSafetyCenterDataChangedListenerWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.clearAllSafetySourceDataForTestsWithPermission
@@ -44,7 +45,7 @@ import com.google.common.util.concurrent.MoreExecutors.directExecutor
/** A class that facilitates settings up Safety Center in tests. */
@RequiresApi(TIRAMISU)
-class SafetyCenterTestHelper(private val context: Context) {
+class SafetyCenterTestHelper(val context: Context) {
private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
private val userManager = context.getSystemService(UserManager::class.java)!!
@@ -55,14 +56,17 @@ class SafetyCenterTestHelper(private val context: Context) {
* values. To be called before each test.
*/
fun setup() {
- SafetySourceReceiver.setup()
+ Log.d(TAG, "setup")
Coroutines.enableDebugging()
+ SafetySourceReceiver.setup()
+ TestActivity.enableHighPriorityAlias()
SafetyCenterFlags.setup()
setEnabled(true)
}
/** Resets the state of Safety Center. To be called after each test. */
fun reset() {
+ Log.d(TAG, "reset")
setEnabled(true)
listeners.forEach {
safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(it)
@@ -72,12 +76,14 @@ class SafetyCenterTestHelper(private val context: Context) {
safetyCenterManager.clearAllSafetySourceDataForTestsWithPermission()
safetyCenterManager.clearSafetyCenterConfigForTestsWithPermission()
resetFlags()
+ TestActivity.disableHighPriorityAlias()
SafetySourceReceiver.reset()
Coroutines.resetDebugging()
}
/** Enables or disables SafetyCenter based on [value]. */
fun setEnabled(value: Boolean) {
+ Log.d(TAG, "setEnabled to $value")
val safetyCenterConfig = safetyCenterManager.getSafetyCenterConfigWithPermission()
if (safetyCenterConfig == null) {
// No broadcasts are dispatched when toggling the flag when SafetyCenter is not
@@ -87,8 +93,8 @@ class SafetyCenterTestHelper(private val context: Context) {
SafetyCenterFlags.isEnabled = value
return
}
- val currentValue = safetyCenterManager.isSafetyCenterEnabledWithPermission()
- if (currentValue == value) {
+ if (value == isEnabled()) {
+ Log.d(TAG, "isEnabled is already $value")
return
}
setEnabledWaitingForSafetyCenterBroadcastIdle(value, safetyCenterConfig)
@@ -96,6 +102,7 @@ class SafetyCenterTestHelper(private val context: Context) {
/** Sets the given [SafetyCenterConfig]. */
fun setConfig(config: SafetyCenterConfig) {
+ Log.d(TAG, "setConfig")
require(isEnabled())
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(config)
}
@@ -107,6 +114,7 @@ class SafetyCenterTestHelper(private val context: Context) {
* initial SafetyCenter update
*/
fun addListener(skipInitialData: Boolean = true): SafetyCenterTestListener {
+ Log.d(TAG, "addListener")
require(isEnabled())
val listener = SafetyCenterTestListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
@@ -126,6 +134,7 @@ class SafetyCenterTestHelper(private val context: Context) {
safetySourceData: SafetySourceData?,
safetyEvent: SafetyEvent = EVENT_SOURCE_STATE_CHANGED
) {
+ Log.d(TAG, "setData for $safetySourceId")
require(isEnabled())
safetyCenterManager.setSafetySourceDataWithPermission(
safetySourceId,
@@ -137,6 +146,8 @@ class SafetyCenterTestHelper(private val context: Context) {
/** Dismisses the [SafetyCenterIssue] for the given [safetyCenterIssueId]. */
@RequiresApi(UPSIDE_DOWN_CAKE)
fun dismissSafetyCenterIssue(safetyCenterIssueId: String) {
+ Log.d(TAG, "dismissSafetyCenterIssue")
+ require(isEnabled())
safetyCenterManager.dismissSafetyCenterIssueWithPermission(safetyCenterIssueId)
}
@@ -155,6 +166,7 @@ class SafetyCenterTestHelper(private val context: Context) {
// Wait for all ACTION_SAFETY_CENTER_ENABLED_CHANGED broadcasts to be dispatched to
// avoid them leaking onto other tests.
if (safetyCenterConfig.containsTestSource()) {
+ Log.d(TAG, "Waiting for test source enabled changed broadcast")
SafetySourceReceiver.receiveSafetyCenterEnabledChanged()
// The explicit ACTION_SAFETY_CENTER_ENABLED_CHANGED broadcast is also sent to the
// dynamically registered receivers.
@@ -166,6 +178,7 @@ class SafetyCenterTestHelper(private val context: Context) {
// 2: test finishes, 3: new test starts, 4: a test config is set, 5: broadcast from 1
// dispatched).
if (userManager.isSystemUser) {
+ Log.d(TAG, "Waiting for system enabled changed broadcast")
// The implicit broadcast is only sent to the system user.
enabledChangedReceiver.receiveSafetyCenterEnabledChanged()
}
@@ -188,4 +201,8 @@ class SafetyCenterTestHelper(private val context: Context) {
.any { it.packageName == context.packageName }
private fun isEnabled() = safetyCenterManager.isSafetyCenterEnabledWithPermission()
+
+ private companion object {
+ const val TAG: String = "SafetyCenterTestHelper"
+ }
}
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt
new file mode 100644
index 000000000..dcbc4ebe9
--- /dev/null
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.safetycenter.testing
+
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+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
+) : TestRule {
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return object : Statement() {
+ override fun evaluate() {
+ setup()
+ try {
+ base.evaluate()
+ } finally {
+ reset()
+ }
+ }
+ }
+ }
+
+ private fun setup() {
+ safetyCenterTestHelper.setup()
+ if (withNotifications) {
+ TestNotificationListener.setup(safetyCenterTestHelper.context)
+ }
+ }
+
+ private fun reset() {
+ safetyCenterTestHelper.reset()
+ if (withNotifications) {
+ // It is important to reset the notification listener last because it waits/ensures that
+ // all notifications have been removed before returning.
+ TestNotificationListener.reset(safetyCenterTestHelper.context)
+ }
+ }
+}
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 2bd662ee8..8386228b8 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt
@@ -221,7 +221,7 @@ class SafetySourceIntentHandler {
safetyEventForResponse: (Response) -> SafetyEvent
) {
val response = mutex.withLock { requestsToResponses[request] } ?: return
- val safetyEvent = safetyEventForResponse(response)
+ val safetyEvent = response.overrideSafetyEvent ?: safetyEventForResponse(response)
when (response) {
is Response.Error ->
reportSafetySourceError(request.sourceId, SafetySourceErrorDetails(safetyEvent))
@@ -270,6 +270,13 @@ class SafetySourceIntentHandler {
*/
sealed interface Response {
+ /**
+ * If non-null, the [SafetyEvent] to use when calling any applicable [SafetyCenterManager]
+ * methods.
+ */
+ val overrideSafetyEvent: SafetyEvent?
+ get() = null
+
/** Creates an error [Response]. */
object Error : Response
@@ -282,10 +289,13 @@ class SafetySourceIntentHandler {
* @param overrideBroadcastId an optional override of the broadcast id to use in the
* [SafetyEvent] sent to the [SafetyCenterManager], in case of [Request.Refresh] or
* [Request.Rescan]. This is used to simulate a misuse of the [SafetyCenterManager] APIs
+ * @param overrideSafetyEvent like [overrideBroadcastId] but allows the whole [SafetyEvent]
+ * to be override to send different types of [SafetyEvent].
*/
data class SetData(
val safetySourceData: SafetySourceData,
- val overrideBroadcastId: String? = null
+ val overrideBroadcastId: String? = 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 2ba87040a..29072c989 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt
@@ -35,8 +35,8 @@ import android.safetycenter.SafetyCenterManager
import android.safetycenter.SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED
import androidx.annotation.RequiresApi
import androidx.test.core.app.ApplicationProvider
-import com.android.compatibility.common.util.SystemUtil
import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG
+import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT
import com.android.safetycenter.testing.Coroutines.runBlockingWithTimeout
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.dismissSafetyCenterIssueWithPermission
import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.executeSafetyCenterIssueActionWithPermission
@@ -164,46 +164,38 @@ class SafetySourceReceiver : BroadcastReceiver() {
fun SafetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
refreshReason: Int,
- timeout: Duration = TIMEOUT_LONG,
- safetySourceIds: List<String>? = null
- ) =
+ safetySourceIds: List<String>? = null,
+ timeout: Duration = TIMEOUT_LONG
+ ): String =
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
- refreshSafetySourcesWithoutReceiverPermissionAndWait(
- refreshReason,
- timeout,
- safetySourceIds
- )
+ refreshSafetySourcesWithPermission(refreshReason, safetySourceIds)
+ receiveRefreshSafetySources(timeout)
}
fun SafetyCenterManager.refreshSafetySourcesWithoutReceiverPermissionAndWait(
refreshReason: Int,
- timeout: Duration,
safetySourceIds: List<String>? = null
- ): String {
+ ) {
refreshSafetySourcesWithPermission(refreshReason, safetySourceIds)
- if (timeout < TIMEOUT_LONG) {
- SystemUtil.waitForBroadcasts()
- }
- return receiveRefreshSafetySources(timeout)
+ WaitForBroadcasts.waitForBroadcasts()
+ receiveRefreshSafetySources(TIMEOUT_SHORT)
}
fun setSafetyCenterEnabledWithReceiverPermissionAndWait(
value: Boolean,
timeout: Duration = TIMEOUT_LONG
- ) =
+ ): Boolean =
callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
- setSafetyCenterEnabledWithoutReceiverPermissionAndWait(value, timeout)
+ SafetyCenterFlags.isEnabled = value
+ receiveSafetyCenterEnabledChanged(timeout)
}
fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait(
value: Boolean,
- timeout: Duration = TIMEOUT_LONG
- ): Boolean {
+ ) {
SafetyCenterFlags.isEnabled = value
- if (timeout < TIMEOUT_LONG) {
- SystemUtil.waitForBroadcasts()
- }
- return receiveSafetyCenterEnabledChanged(timeout)
+ WaitForBroadcasts.waitForBroadcasts()
+ receiveSafetyCenterEnabledChanged(TIMEOUT_SHORT)
}
fun SafetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
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 97e2078e0..c35d02a52 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt
@@ -20,7 +20,6 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_RECEIVER_FOREGROUND
-import android.content.pm.PackageManager.ResolveInfoFlags
import android.os.Build.VERSION_CODES.TIRAMISU
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.safetycenter.SafetyEvent
@@ -39,13 +38,13 @@ import android.safetycenter.SafetySourceStatus.IconAction.ICON_TYPE_INFO
import androidx.annotation.RequiresApi
import com.android.modules.utils.build.SdkLevel
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY
+import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY_EXPORTED
import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID
import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.ACTION_DISMISS_ISSUE
import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.ACTION_RESOLVE_ACTION
import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ID
import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ISSUE_ACTION_ID
import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ISSUE_ID
-import java.lang.IllegalStateException
import kotlin.math.max
/**
@@ -55,16 +54,21 @@ import kotlin.math.max
@RequiresApi(TIRAMISU)
class SafetySourceTestData(private val context: Context) {
- /** A [PendingIntent] that redirects to the [TestActivity] page. */
- val testActivityRedirectPendingIntent =
- createRedirectPendingIntent(context, Intent(ACTION_TEST_ACTIVITY))
-
/**
- * A [PendingIntent] that redirects to the [TestActivity] page, the [Intent] is constructed with
- * the given [identifier].
+ * A [PendingIntent] that redirects to the [TestActivity] page.
+ *
+ * @param explicit whether the returned [PendingIntent] should use an explicit [Intent] (default
+ * [true])
+ * @param identifier the [Intent] identifier (default [null])
*/
- fun testActivityRedirectPendingIntent(identifier: String? = null) =
- createRedirectPendingIntent(context, Intent(ACTION_TEST_ACTIVITY).setIdentifier(identifier))
+ fun createTestActivityRedirectPendingIntent(
+ explicit: Boolean = true,
+ identifier: String? = null
+ ) =
+ createRedirectPendingIntent(
+ context,
+ createTestActivityIntent(context, explicit).setIdentifier(identifier)
+ )
/** A [SafetySourceData] with a [SEVERITY_LEVEL_UNSPECIFIED] [SafetySourceStatus]. */
val unspecified =
@@ -93,7 +97,7 @@ class SafetySourceTestData(private val context: Context) {
SEVERITY_LEVEL_UNSPECIFIED
)
.setEnabled(false)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.build()
@@ -110,14 +114,14 @@ class SafetySourceTestData(private val context: Context) {
summary: String = "Information issue summary"
) =
SafetySourceIssue.Builder(id, title, summary, SEVERITY_LEVEL_INFORMATION, ISSUE_TYPE_ID)
- .addAction(
- Action.Builder(
- INFORMATION_ISSUE_ACTION_ID,
- "Review",
- testActivityRedirectPendingIntent
- )
- .build()
- )
+ .addAction(action())
+
+ /** Creates an action with some defaults set. */
+ fun action(
+ id: String = INFORMATION_ISSUE_ACTION_ID,
+ label: String = "Review",
+ pendingIntent: PendingIntent = createTestActivityRedirectPendingIntent()
+ ) = Action.Builder(id, label, pendingIntent).build()
/**
* A [SafetySourceIssue] with a [SEVERITY_LEVEL_INFORMATION] and a redirecting [Action]. With
@@ -132,14 +136,7 @@ class SafetySourceTestData(private val context: Context) {
ISSUE_TYPE_ID
)
.setSubtitle("Information issue subtitle")
- .addAction(
- Action.Builder(
- INFORMATION_ISSUE_ACTION_ID,
- "Review",
- testActivityRedirectPendingIntent
- )
- .build()
- )
+ .addAction(action())
.build()
/**
@@ -154,7 +151,7 @@ class SafetySourceTestData(private val context: Context) {
"Unspecified summary",
SEVERITY_LEVEL_UNSPECIFIED
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(informationIssue)
@@ -172,7 +169,7 @@ class SafetySourceTestData(private val context: Context) {
"Unspecified summary",
SEVERITY_LEVEL_UNSPECIFIED
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(informationIssue)
@@ -183,7 +180,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.build()
@@ -209,8 +206,10 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
- .setPendingIntent(testActivityRedirectPendingIntent)
- .setIconAction(IconAction(ICON_TYPE_INFO, testActivityRedirectPendingIntent))
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
+ .setIconAction(
+ IconAction(ICON_TYPE_INFO, createTestActivityRedirectPendingIntent())
+ )
.build()
)
.build()
@@ -223,8 +222,10 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
- .setPendingIntent(testActivityRedirectPendingIntent)
- .setIconAction(IconAction(ICON_TYPE_GEAR, testActivityRedirectPendingIntent))
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
+ .setIconAction(
+ IconAction(ICON_TYPE_GEAR, createTestActivityRedirectPendingIntent())
+ )
.build()
)
.build()
@@ -237,7 +238,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(informationIssue)
@@ -253,7 +254,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(
@@ -275,7 +276,7 @@ class SafetySourceTestData(private val context: Context) {
"Ok summary",
SEVERITY_LEVEL_INFORMATION
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(informationIssue)
@@ -289,7 +290,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(informationIssueWithSubtitle)
@@ -315,7 +316,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
RECOMMENDATION_ISSUE_ACTION_ID,
"See issue",
- testActivityRedirectPendingIntent
+ createTestActivityRedirectPendingIntent()
)
.apply {
if (confirmationDialog && SdkLevel.isAtLeastU()) {
@@ -383,7 +384,7 @@ class SafetySourceTestData(private val context: Context) {
"Recommendation summary",
SEVERITY_LEVEL_RECOMMENDATION
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
@@ -425,12 +426,29 @@ class SafetySourceTestData(private val context: Context) {
.build()
/** A [PendingIntent] used by the resolving [Action] in [criticalResolvingGeneralIssue]. */
- val criticalIssueActionPendingIntent =
+ val criticalIssueActionPendingIntent = resolvingActionPendingIntent()
+
+ /** A [PendingIntent] used by the resolving [Action] in [criticalResolvingGeneralIssue]. */
+ fun criticalIssueActionPendingIntent(sourceId: String) =
+ resolvingActionPendingIntent(sourceId = sourceId)
+
+ /**
+ * Returns a [PendingIntent] for a resolving [Action] with the given [sourceId], [sourceIssueId]
+ * and [sourceIssueActionId]. Default values are the same as those used by
+ * [criticalIssueActionPendingIntent]. *
+ */
+ fun resolvingActionPendingIntent(
+ sourceId: String = SINGLE_SOURCE_ID,
+ sourceIssueId: String = CRITICAL_ISSUE_ID,
+ sourceIssueActionId: String = CRITICAL_ISSUE_ACTION_ID
+ ) =
broadcastPendingIntent(
Intent(ACTION_RESOLVE_ACTION)
- .putExtra(EXTRA_SOURCE_ID, SINGLE_SOURCE_ID)
- .putExtra(EXTRA_SOURCE_ISSUE_ID, CRITICAL_ISSUE_ID)
- .putExtra(EXTRA_SOURCE_ISSUE_ACTION_ID, CRITICAL_ISSUE_ACTION_ID)
+ .putExtra(EXTRA_SOURCE_ID, sourceId)
+ .putExtra(EXTRA_SOURCE_ISSUE_ID, sourceIssueId)
+ .putExtra(EXTRA_SOURCE_ISSUE_ACTION_ID, sourceIssueActionId)
+ // Identifier is set because intent extras do not disambiguate PendingIntents
+ .setIdentifier(sourceId + sourceIssueId + sourceIssueActionId)
)
/** A resolving Critical [Action] */
@@ -439,6 +457,16 @@ class SafetySourceTestData(private val context: Context) {
.setWillResolve(true)
.build()
+ /** A resolving Critical [Action] */
+ private fun criticalResolvingAction(sourceId: String) =
+ Action.Builder(
+ CRITICAL_ISSUE_ACTION_ID,
+ "Solve issue",
+ criticalIssueActionPendingIntent(sourceId = sourceId)
+ )
+ .setWillResolve(true)
+ .build()
+
/** A resolving Critical [Action] with confirmation */
val criticalResolvingActionWithConfirmation: SafetySourceIssue.Action
@RequiresApi(UPSIDE_DOWN_CAKE)
@@ -454,7 +482,11 @@ class SafetySourceTestData(private val context: Context) {
/** An action that redirects to [TestActivity] */
val testActivityRedirectAction =
- Action.Builder(CRITICAL_ISSUE_ACTION_ID, "Redirect", testActivityRedirectPendingIntent)
+ Action.Builder(
+ CRITICAL_ISSUE_ACTION_ID,
+ "Redirect",
+ createTestActivityRedirectPendingIntent()
+ )
.build()
/** A resolving Critical [Action] that declares a success message */
@@ -464,6 +496,17 @@ class SafetySourceTestData(private val context: Context) {
.setSuccessMessage("Issue solved")
.build()
+ /** A resolving Critical [Action] that declares a success message */
+ private fun criticalResolvingActionWithSuccessMessage(sourceId: String) =
+ Action.Builder(
+ CRITICAL_ISSUE_ACTION_ID,
+ "Solve issue",
+ criticalIssueActionPendingIntent(sourceId = sourceId)
+ )
+ .setWillResolve(true)
+ .setSuccessMessage("Issue solved")
+ .build()
+
/** A [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving [Action]. */
val criticalResolvingIssueWithSuccessMessage =
SafetySourceIssue.Builder(
@@ -476,6 +519,18 @@ class SafetySourceTestData(private val context: Context) {
.addAction(criticalResolvingActionWithSuccessMessage)
.build()
+ /** A [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving [Action]. */
+ private fun criticalResolvingIssueWithSuccessMessage(sourceId: String) =
+ SafetySourceIssue.Builder(
+ CRITICAL_ISSUE_ID,
+ "Critical issue title",
+ "Critical issue summary",
+ SEVERITY_LEVEL_CRITICAL_WARNING,
+ ISSUE_TYPE_ID
+ )
+ .addAction(criticalResolvingActionWithSuccessMessage(sourceId = sourceId))
+ .build()
+
/**
* Another [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a redirecting
* [Action].
@@ -492,7 +547,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
CRITICAL_ISSUE_ACTION_ID,
"Go solve issue",
- testActivityRedirectPendingIntent
+ createTestActivityRedirectPendingIntent()
)
.build()
)
@@ -520,7 +575,10 @@ class SafetySourceTestData(private val context: Context) {
* [SafetySourceIssue.Builder] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving [Action]
* .
*/
- fun defaultCriticalResolvingIssueBuilder(issueId: String = CRITICAL_ISSUE_ID) =
+ fun defaultCriticalResolvingIssueBuilder(
+ issueId: String = CRITICAL_ISSUE_ID,
+ sourceId: String = SINGLE_SOURCE_ID,
+ ) =
SafetySourceIssue.Builder(
issueId,
"Critical issue title",
@@ -528,7 +586,7 @@ class SafetySourceTestData(private val context: Context) {
SEVERITY_LEVEL_CRITICAL_WARNING,
ISSUE_TYPE_ID
)
- .addAction(criticalResolvingAction)
+ .addAction(criticalResolvingAction(sourceId))
/**
* General [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving [Action]
@@ -537,6 +595,13 @@ class SafetySourceTestData(private val context: Context) {
val criticalResolvingGeneralIssue = defaultCriticalResolvingIssueBuilder().build()
/**
+ * General [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving [Action]
+ * .
+ */
+ private fun criticalResolvingGeneralIssue(sourceId: String) =
+ defaultCriticalResolvingIssueBuilder(sourceId = sourceId).build()
+
+ /**
* General [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and with deduplication
* info and a resolving [Action].
*/
@@ -562,6 +627,15 @@ class SafetySourceTestData(private val context: Context) {
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
.build()
+ /**
+ * Device related [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving
+ * [Action].
+ */
+ private fun criticalResolvingDeviceIssue(sourceId: String) =
+ defaultCriticalResolvingIssueBuilder(sourceId = sourceId)
+ .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
+ .build()
+
/** A [SafetySourceData.Builder] with a [SEVERITY_LEVEL_CRITICAL_WARNING] status. */
fun defaultCriticalDataBuilder() =
SafetySourceData.Builder()
@@ -571,7 +645,7 @@ class SafetySourceTestData(private val context: Context) {
"Critical summary",
SEVERITY_LEVEL_CRITICAL_WARNING
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
@@ -635,6 +709,15 @@ class SafetySourceTestData(private val context: Context) {
/**
* A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving general
+ * [SafetySourceIssue] and [SafetySourceStatus].
+ */
+ fun criticalWithResolvingGeneralIssue(sourceId: String) =
+ defaultCriticalDataBuilder()
+ .addIssue(criticalResolvingGeneralIssue(sourceId = sourceId))
+ .build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving general
* [SafetySourceIssue] and [SafetySourceStatus], with confirmation dialog.
*/
val criticalWithResolvingGeneralIssueWithConfirmation: SafetySourceData
@@ -665,6 +748,15 @@ class SafetySourceTestData(private val context: Context) {
/**
* A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving device related
+ * [SafetySourceIssue] and [SafetySourceStatus].
+ */
+ fun criticalWithResolvingDeviceIssue(sourceId: String) =
+ defaultCriticalDataBuilder()
+ .addIssue(criticalResolvingDeviceIssue(sourceId = sourceId))
+ .build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving device related
* [SafetySourceIssue] and [SafetySourceStatus] and a recommendation issue.
*/
val criticalWithResolvingDeviceIssueAndRecommendationIssue =
@@ -685,13 +777,31 @@ class SafetySourceTestData(private val context: Context) {
"Critical summary",
SEVERITY_LEVEL_CRITICAL_WARNING
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(criticalResolvingIssueWithSuccessMessage)
.build()
/**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving [SafetySourceIssue]
+ * and [SafetySourceStatus].
+ */
+ fun criticalWithResolvingIssueWithSuccessMessage(sourceId: String) =
+ SafetySourceData.Builder()
+ .setStatus(
+ SafetySourceStatus.Builder(
+ "Critical title",
+ "Critical summary",
+ SEVERITY_LEVEL_CRITICAL_WARNING
+ )
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
+ .build()
+ )
+ .addIssue(criticalResolvingIssueWithSuccessMessage(sourceId = sourceId))
+ .build()
+
+ /**
* A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and
* [SEVERITY_LEVEL_CRITICAL_WARNING] [SafetySourceStatus].
*/
@@ -710,7 +820,7 @@ class SafetySourceTestData(private val context: Context) {
"Critical summary 2",
SEVERITY_LEVEL_CRITICAL_WARNING
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(criticalRedirectingIssue)
@@ -729,7 +839,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder(entryTitle, entrySummary, severityLevel)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.apply {
@@ -746,7 +856,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
"action_id",
"Action",
- testActivityRedirectPendingIntent
+ createTestActivityRedirectPendingIntent()
)
.build()
)
@@ -784,7 +894,9 @@ class SafetySourceTestData(private val context: Context) {
const val CRITICAL_ISSUE_ACTION_ID = "critical_issue_action_id"
/** Issue type ID for all the issues in this file */
+ // LINT.IfChange(issue_type_id)
const val ISSUE_TYPE_ID = "issue_type_id"
+ // LINT.ThenChange(/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt:issue_type_id)
const val CONFIRMATION_TITLE = "Confirmation title"
const val CONFIRMATION_TEXT = "Confirmation text"
@@ -811,28 +923,28 @@ class SafetySourceTestData(private val context: Context) {
return builder.build()
}
- /** Returns a [PendingIntent] that redirects to [intent]. */
+ /** Returns an [Intent] that redirects to the [TestActivity] page. */
+ fun createTestActivityIntent(context: Context, explicit: Boolean = true): Intent =
+ if (explicit) {
+ Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName)
+ } else {
+ val intent = Intent(ACTION_TEST_ACTIVITY_EXPORTED)
+ // We have seen some flakiness where implicit intents find multiple receivers
+ // and the ResolveActivity pops up. A test cannot handle this, so crash. Most
+ // likely the cause is other test's APKs being left hanging around by flaky
+ // test infrastructure.
+ intent.flags = intent.flags or Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT
+ intent
+ }
+
+ /** Returns a [PendingIntent] that redirects to the given [Intent]. */
fun createRedirectPendingIntent(context: Context, intent: Intent): PendingIntent {
- val explicitIntent = Intent(intent).setPackage(context.packageName)
- val redirectIntent =
- if (intentResolves(context, explicitIntent)) {
- explicitIntent
- } else if (intentResolves(context, intent)) {
- intent
- } else {
- throw IllegalStateException("Intent doesn't resolve")
- }
return PendingIntent.getActivity(
context,
0 /* requestCode */,
- redirectIntent,
+ intent,
PendingIntent.FLAG_IMMUTABLE
)
}
-
- private fun intentResolves(context: Context, intent: Intent): Boolean =
- context.packageManager
- .queryIntentActivities(intent, ResolveInfoFlags.of(0))
- .isNotEmpty()
}
}
diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/StatusBarNotificationWithChannel.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt
index d9a998c3e..53ea34362 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/StatusBarNotificationWithChannel.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.safetycenter.functional.testing
+package com.android.safetycenter.testing
import android.app.NotificationChannel
import android.service.notification.StatusBarNotification
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SupportsSafetyCenter.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SupportsSafetyCenter.kt
new file mode 100644
index 000000000..bfb5c4bd7
--- /dev/null
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SupportsSafetyCenter.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.safetycenter.testing
+
+import android.content.Context
+import android.content.res.Resources
+
+/**
+ * Returns whether the device supports Safety Center according to the `config_enableSafetyCenter`
+ * boolean system resource.
+ */
+fun Context.deviceSupportsSafetyCenter(): Boolean {
+ val resId = Resources.getSystem().getIdentifier("config_enableSafetyCenter", "bool", "android")
+ return resources.getBoolean(resId)
+}
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SupportsSafetyCenterRule.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SupportsSafetyCenterRule.kt
new file mode 100644
index 000000000..7227873ff
--- /dev/null
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SupportsSafetyCenterRule.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.safetycenter.testing
+
+import android.content.Context
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * JUnit [TestRule] for on-device tests that requires Safety Center to be supported. This rule does
+ * not require Safety Center to be enabled.
+ *
+ * For tests which should only run on devices where Safety Center is not supported, instantiate with
+ * [requireSupportIs] set to `false` to invert the condition.
+ */
+class SupportsSafetyCenterRule(private val context: Context, requireSupportIs: Boolean = true) :
+ TestRule {
+
+ private val shouldSupportSafetyCenter: Boolean = requireSupportIs
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return object : Statement() {
+ override fun evaluate() {
+ val support = context.deviceSupportsSafetyCenter()
+ if (shouldSupportSafetyCenter) {
+ assumeTrue("Test device does not support Safety Center", support)
+ } else {
+ assumeFalse("Test device supports Safety Center", support)
+ }
+ base.evaluate()
+ }
+ }
+ }
+}
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 124f44101..eceffb74f 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt
@@ -16,9 +16,15 @@
package com.android.safetycenter.testing
import android.app.Activity
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+import android.content.pm.PackageManager.DONT_KILL_APP
import android.os.Bundle
import android.view.View
import android.widget.TextView
+import androidx.test.core.app.ApplicationProvider
/** An activity used in tests to assert the redirects. */
class TestActivity : Activity() {
@@ -32,4 +38,32 @@ class TestActivity : Activity() {
val exitButton: View? = findViewById(R.id.button)
exitButton?.setOnClickListener { finish() }
}
+
+ companion object {
+
+ /**
+ * Enable a higher-priority alias of TestActivity.
+ *
+ * <p>We have seen flakes where implicit intents for TEST_ACTIVITY fail owing to multiple
+ * receivers, perhaps due to an older CTS APK hanging around. This component should be
+ * turned on (and off in tidyup) in tests in the hope of only resolving to the actively
+ * running test in these cases.
+ */
+ 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")
+ getApplicationContext()
+ .packageManager
+ .setComponentEnabledSetting(name, state, DONT_KILL_APP)
+ }
+
+ private fun getApplicationContext(): Context = ApplicationProvider.getApplicationContext()
+ }
}
diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/TestNotificationListener.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt
index 113ad3b23..21bf76fad 100644
--- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/TestNotificationListener.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.safetycenter.functional.testing
+package com.android.safetycenter.testing
+import android.app.Notification
import android.app.NotificationChannel
import android.content.ComponentName
+import android.content.Context
import android.os.ConditionVariable
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
@@ -84,15 +86,7 @@ class TestNotificationListener : NotificationListenerService() {
}
companion object {
- private const val TAG = "TestNotificationListene"
-
- private val id: String =
- "android.safetycenter.functional/" + TestNotificationListener::class.java.name
- private val componentName =
- ComponentName(
- "android.safetycenter.functional",
- TestNotificationListener::class.java.name
- )
+ private const val TAG = "SafetyCenterTestNotif"
private val connected = ConditionVariable(false)
private val disconnected = ConditionVariable(true)
@@ -108,32 +102,8 @@ class TestNotificationListener : NotificationListenerService() {
* if it is met and then violated.
*/
fun waitForZeroNotifications(timeout: Duration = TIMEOUT_LONG) {
- waitForNotificationCount(0, timeout)
- }
-
- /**
- * Blocks until there is exactly one Safety Center notification and ensures that remains
- * true for a short duration. Returns that notification, or throws an [AssertionError] if a
- * this condition is not met within [timeout], or if it is met and then violated.
- */
- fun waitForSingleNotification(
- timeout: Duration = TIMEOUT_LONG
- ): StatusBarNotificationWithChannel {
- return waitForNotificationCount(1, timeout).first()
- }
-
- /**
- * Blocks until there are exactly [count] Safety Center notifications and ensures that
- * remains true for a short duration. Returns those notifications, or throws an
- * [AssertionError] if a this condition is not met within [timeout], or if it is met and
- * then violated.
- */
- private fun waitForNotificationCount(
- count: Int,
- timeout: Duration = TIMEOUT_LONG
- ): List<StatusBarNotificationWithChannel> {
- return waitForNotificationsToSatisfy(timeout, description = "$count notifications") {
- it.size == count
+ waitForNotificationsToSatisfy(timeout = timeout, description = "No notifications") {
+ it.isEmpty()
}
}
@@ -162,18 +132,50 @@ class TestNotificationListener : NotificationListenerService() {
): List<StatusBarNotificationWithChannel> {
val charsList = characteristics.toList()
return waitForNotificationsToSatisfy(
- timeout,
+ timeout = timeout,
description = "notification(s) matching characteristics $charsList"
- ) { NotificationCharacteristics.areMatching(it, charsList) }
+ ) {
+ NotificationCharacteristics.areMatching(it, charsList)
+ }
+ }
+
+ /**
+ * Waits for a success notification with the given [successMessage] after resolving an
+ * issue.
+ *
+ * Additional assertions can be made on the [StatusBarNotification] using [onNotification].
+ */
+ fun waitForSuccessNotification(
+ successMessage: String,
+ 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.
+ val statusBarNotification =
+ (runBlockingWithTimeout {
+ waitForNotificationEventAsync {
+ (it is NotificationPosted &&
+ it.statusBarNotification.notification
+ ?.extras
+ ?.getString(Notification.EXTRA_TITLE) == successMessage)
+ }
+ }
+ as NotificationPosted)
+ .statusBarNotification
+ onNotification(statusBarNotification)
+ // Cancel the notification directly to speed up the tests as it's only auto-cancelled
+ // after 10 seconds, and the teardown waits for all notifications to be cancelled to
+ // avoid having unrelated notifications leaking between test cases.
+ cancelAndWait(statusBarNotification.key, waitForIssueCache = false)
}
/**
- * Blocks until [forAtLeast] has elapsed, or throw an [AssertionError] if any notification
- * is posted or removed before then.
+ * Blocks for [TIMEOUT_SHORT], or throw an [AssertionError] if any notification is posted or
+ * removed before then.
*/
- fun waitForZeroNotificationEvents(forAtLeast: Duration = TIMEOUT_SHORT) {
+ fun waitForZeroNotificationEvents() {
val event =
- runBlockingWithTimeoutOrNull(forAtLeast) {
+ runBlockingWithTimeoutOrNull(TIMEOUT_SHORT) {
safetyCenterNotificationEvents.receive()
}
assertThat(event).isNull()
@@ -185,10 +187,6 @@ class TestNotificationListener : NotificationListenerService() {
description: String,
predicate: (List<StatusBarNotificationWithChannel>) -> Boolean
): List<StatusBarNotificationWithChannel> {
- fun formatError(notifs: List<StatusBarNotificationWithChannel>): String {
- return "Expected: $description, but the actual notifications were: $notifs"
- }
-
// First we wait at most timeout for the active notifications to satisfy the given
// predicate or otherwise we throw:
val satisfyingNotifications =
@@ -197,7 +195,11 @@ class TestNotificationListener : NotificationListenerService() {
waitForNotificationsToSatisfyAsync(predicate)
}
} catch (e: TimeoutCancellationException) {
- throw AssertionError(formatError(getSafetyCenterNotifications()), e)
+ throw AssertionError(
+ "Expected: $description, but notifications were " +
+ "${getSafetyCenterNotifications()} after waiting for $timeout",
+ e
+ )
}
// Assuming the predicate was satisfied, now we ensure it is not violated for the
@@ -208,7 +210,10 @@ class TestNotificationListener : NotificationListenerService() {
}
if (nonSatisfyingNotifications != null) {
// In this case the negated-predicate was satisfied before forAtLeast had elapsed
- throw AssertionError(formatError(nonSatisfyingNotifications))
+ throw AssertionError(
+ "Expected: $description to settle, but notifications changed to " +
+ "$nonSatisfyingNotifications within $forAtLeast"
+ )
}
return satisfyingNotifications
@@ -226,6 +231,17 @@ class TestNotificationListener : NotificationListenerService() {
return currentNotifications
}
+ private suspend fun waitForNotificationEventAsync(
+ predicate: (NotificationEvent) -> Boolean
+ ): NotificationEvent {
+ var event: NotificationEvent
+ do {
+ event = safetyCenterNotificationEvents.receive()
+ Log.d(TAG, "Received notification event: $event")
+ } while (!predicate(event))
+ return event
+ }
+
private fun getSafetyCenterNotifications(): List<StatusBarNotificationWithChannel> {
return with(getInstanceOrThrow()) {
val notificationsSnapshot =
@@ -276,16 +292,20 @@ class TestNotificationListener : NotificationListenerService() {
/**
* Cancels a specific notification and then waits for it to be removed by the notification
* manager and marked as dismissed in Safety Center, or throws if it has not been removed
- * within [timeout].
+ * within [TIMEOUT_LONG].
*/
- fun cancelAndWait(key: String, timeout: Duration = TIMEOUT_LONG) {
+ fun cancelAndWait(key: String, waitForIssueCache: Boolean = true) {
getInstanceOrThrow().cancelNotification(key)
waitForNotificationsToSatisfy(
- timeout,
+ timeout = TIMEOUT_LONG,
description = "no notification with the key $key"
- ) { notifications -> notifications.none { it.statusBarNotification.key == key } }
+ ) { notifications ->
+ notifications.none { it.statusBarNotification.key == key }
+ }
- waitForIssueCacheToContainAnyDismissedNotification()
+ if (waitForIssueCache) {
+ waitForIssueCacheToContainAnyDismissedNotification()
+ }
}
private fun waitForIssueCacheToContainAnyDismissedNotification() {
@@ -313,10 +333,12 @@ class TestNotificationListener : NotificationListenerService() {
}
/** Runs a shell command to allow or disallow the listener. Use before and after test. */
- private fun toggleListenerAccess(allowed: Boolean) {
- // TODO(b/260335646): Try to do this using the AndroidTest.xml instead of in code
+ private fun toggleListenerAccess(context: Context, allowed: Boolean) {
+ val componentName = ComponentName(context, TestNotificationListener::class.java)
val verb = if (allowed) "allow" else "disallow"
- SystemUtil.runShellCommand("cmd notification ${verb}_listener $id")
+ SystemUtil.runShellCommand(
+ "cmd notification ${verb}_listener ${componentName.flattenToString()}"
+ )
if (allowed) {
requestRebind(componentName)
if (!connected.block(TIMEOUT_LONG.toMillis())) {
@@ -332,22 +354,29 @@ class TestNotificationListener : NotificationListenerService() {
}
/** Prepare the [TestNotificationListener] for a notification test */
- fun setup() {
- toggleListenerAccess(true)
+ fun setup(context: Context) {
+ toggleListenerAccess(context, true)
}
/** Clean up the [TestNotificationListener] after executing a notification test. */
- fun reset() {
+ fun reset(context: Context) {
waitForNotificationsToSatisfy(
forAtLeast = Duration.ZERO,
description = "all Safety Center notifications removed in tear down"
- ) { it.isEmpty() }
- toggleListenerAccess(false)
+ ) {
+ it.isEmpty()
+ }
+ toggleListenerAccess(context, false)
safetyCenterNotificationEvents.cancel()
safetyCenterNotificationEvents = Channel(capacity = Channel.UNLIMITED)
}
private fun StatusBarNotification.isSafetyCenterNotification(): Boolean =
- packageName == "android" && notification.channelId.startsWith("safety_center")
+ packageName == "android" &&
+ notification.channelId.startsWith("safety_center") &&
+ // Don't consider the grouped system notifications to be a SC notification, in some
+ // scenarios a "ranker_group" notification can remain even when there are no more
+ // notifications associated with the channel. See b/293593539 for more details.
+ tag != "ranker_group"
}
}
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 ec676e3d9..0e062692a 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt
@@ -29,10 +29,11 @@ import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.StaleObjectException
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
import com.android.compatibility.common.util.SystemUtil.runShellCommand
import com.android.compatibility.common.util.UiAutomatorUtils2.getUiDevice
import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
-import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull
+import com.android.compatibility.common.util.UiDumpUtils
import java.time.Duration
import java.util.concurrent.TimeoutException
import java.util.regex.Pattern
@@ -46,19 +47,30 @@ object UiTestHelper {
const val MORE_ISSUES_LABEL = "More alerts"
private const val DISMISS_ISSUE_LABEL = "Dismiss"
- private val WAIT_TIMEOUT = Duration.ofSeconds(10)
- private val NOT_DISPLAYED_TIMEOUT = Duration.ofMillis(500)
+ private const val TAG = "SafetyCenterUiTestHelper"
- private val TAG = UiTestHelper::class.java.simpleName
+ private val WAIT_TIMEOUT = Duration.ofSeconds(20)
/**
- * Waits for the given [selector] to be displayed and performs the given [uiObjectAction] on it.
+ * Waits for the given [selector] to be displayed, and optionally perform a given
+ * [uiObjectAction] on it.
*/
fun waitDisplayed(selector: BySelector, uiObjectAction: (UiObject2) -> Unit = {}) {
- waitFor("$selector to be displayed", WAIT_TIMEOUT) {
- uiObjectAction(waitFindObject(selector, it.toMillis()))
- true
+ val whenToTimeout = currentElapsedRealtime() + WAIT_TIMEOUT
+ var remaining = WAIT_TIMEOUT
+ while (remaining > Duration.ZERO) {
+ getUiDevice().waitForIdle()
+ try {
+ uiObjectAction(waitFindObject(selector, remaining.toMillis()))
+ return
+ } catch (e: StaleObjectException) {
+ Log.w(TAG, "Found stale UI object, retrying", e)
+ remaining = whenToTimeout - currentElapsedRealtime()
+ }
}
+ throw UiDumpUtils.wrapWithUiDump(
+ TimeoutException("Timed out waiting for $selector to be displayed after $WAIT_TIMEOUT")
+ )
}
/** Waits for all the given [textToFind] to be displayed. */
@@ -77,16 +89,21 @@ object UiTestHelper {
/** Waits for the given [selector] not to be displayed. */
fun waitNotDisplayed(selector: BySelector) {
- waitFor("$selector not to be displayed", NOT_DISPLAYED_TIMEOUT) {
- waitFindObjectOrNull(selector, it.toMillis()) == null
+ // TODO(b/294038848): Add scrolling to make sure it is properly gone.
+ val gone = getUiDevice().wait(Until.gone(selector), WAIT_TIMEOUT.toMillis())
+ if (gone) {
+ return
}
+ throw UiDumpUtils.wrapWithUiDump(
+ TimeoutException(
+ "Timed out waiting for $selector not to be displayed after $WAIT_TIMEOUT"
+ )
+ )
}
/** Waits for all the given [textToFind] not to be displayed. */
fun waitAllTextNotDisplayed(vararg textToFind: CharSequence?) {
- for (text in textToFind) {
- if (text != null) waitNotDisplayed(By.text(text.toString()))
- }
+ waitNotDisplayed(By.text(anyOf(*textToFind)))
}
/** Waits for a button with the given [label] not to be displayed. */
@@ -101,11 +118,11 @@ object UiTestHelper {
*/
@RequiresApi(TIRAMISU)
fun waitSourceDataDisplayed(sourceData: SafetySourceData) {
- waitAllTextDisplayed(sourceData.status?.title, sourceData.status?.summary)
-
for (sourceIssue in sourceData.issues) {
waitSourceIssueDisplayed(sourceIssue)
}
+
+ waitAllTextDisplayed(sourceData.status?.title, sourceData.status?.summary)
}
/** Waits for most of the [SafetySourceIssue] information to be displayed. */
@@ -131,7 +148,7 @@ object UiTestHelper {
fun waitCollapsedIssuesDisplayed(vararg sourceIssues: SafetySourceIssue) {
waitSourceIssueDisplayed(sourceIssues.first())
waitAllTextDisplayed(MORE_ISSUES_LABEL)
- sourceIssues.asSequence().drop(1).forEach { waitSourceIssueNotDisplayed(it) }
+ waitAllTextNotDisplayed(*sourceIssues.drop(1).map { it.title }.toTypedArray())
}
/** Waits for all the [SafetySourceIssue] to be displayed with the [MORE_ISSUES_LABEL] card. */
@@ -221,35 +238,17 @@ object UiTestHelper {
}
private fun buttonSelector(label: CharSequence): BySelector {
- return By.clickable(true).text(Pattern.compile("$label|${label.toString().uppercase()}"))
+ return By.clickable(true).text(anyOf(label, label.toString().uppercase()))
}
- private fun waitFor(
- message: String,
- uiAutomatorConditionTimeout: Duration,
- uiAutomatorCondition: (Duration) -> Boolean
- ) {
- val elapsedStartMillis = SystemClock.elapsedRealtime()
- while (true) {
- getUiDevice().waitForIdle()
- val durationSinceStart =
- Duration.ofMillis(SystemClock.elapsedRealtime() - elapsedStartMillis)
- if (durationSinceStart >= WAIT_TIMEOUT) {
- break
- }
- val remainingTime = WAIT_TIMEOUT - durationSinceStart
- val uiAutomatorTimeout = minOf(uiAutomatorConditionTimeout, remainingTime)
- try {
- if (uiAutomatorCondition(uiAutomatorTimeout)) {
- return
- } else {
- Log.d(TAG, "Failed condition for $message, will retry if within timeout")
- }
- } catch (e: StaleObjectException) {
- Log.d(TAG, "StaleObjectException for $message, will retry if within timeout", e)
+ private fun anyOf(vararg anyTextToFind: CharSequence?): Pattern {
+ val regex =
+ anyTextToFind.filterNotNull().joinToString(separator = "|") {
+ Pattern.quote(it.toString())
}
- }
-
- throw TimeoutException("Timed out waiting for $message")
+ return Pattern.compile(regex)
}
+
+ private fun currentElapsedRealtime(): Duration =
+ Duration.ofMillis(SystemClock.elapsedRealtime())
}
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/WaitForBroadcasts.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/WaitForBroadcasts.kt
new file mode 100644
index 000000000..3c19ea180
--- /dev/null
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/WaitForBroadcasts.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.safetycenter.testing
+
+import android.util.Log
+import androidx.annotation.GuardedBy
+import com.android.compatibility.common.util.SystemUtil
+import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG
+import com.android.safetycenter.testing.Coroutines.runBlockingWithTimeout
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+
+/** A class to help waiting on broadcasts to be processed by the system. */
+object WaitForBroadcasts {
+
+ private const val TAG: String = "WaitForBroadcasts"
+
+ private val mutex = Mutex()
+ @GuardedBy("mutex") private var currentJob: Job? = null
+
+ /**
+ * Waits for broadcasts for at most [TIMEOUT_LONG] and prints a warning if that operation timed
+ * out.
+ *
+ * The [SystemUtil.waitForBroadcasts] operation will keep on running even after the timeout as
+ * it is not interruptible. Further calls to [WaitForBroadcasts.waitForBroadcasts] will re-use
+ * the currently running [SystemUtil.waitForBroadcasts] call if it hasn't completed.
+ */
+ fun waitForBroadcasts() {
+ try {
+ runBlockingWithTimeout {
+ mutex
+ .withLock {
+ val newJob = currentJob.maybeStartNewWaitForBroadcasts()
+ currentJob = newJob
+ newJob
+ }
+ .join()
+ }
+ } catch (e: TimeoutCancellationException) {
+ Log.w(TAG, "Waiting for broadcasts timed out, proceeding anyway", e)
+ }
+ }
+
+ // We're using a GlobalScope here as there doesn't seem to be a straightforward way to timeout
+ // and interrupt the waitForBroadcasts() call. Given it's uninterruptible, we'd rather just have
+ // at most one globally-bound waitForBroadcasts() call running at any given time.
+ @OptIn(DelicateCoroutinesApi::class)
+ private fun Job?.maybeStartNewWaitForBroadcasts(): Job =
+ if (this != null && isActive) {
+ this
+ } else {
+ GlobalScope.launch(Dispatchers.IO) { SystemUtil.waitForBroadcasts() }
+ }
+}